Craftsman at Work

I'm Artur Karbone, coding software architect and independent IT consultant and this is my blog about craftsmanship, architecture, distributed systems, management and much more.

Akka.NET: Leveraging State Machines

The Goal

In this post we are going to implement a simple finite-state machine (FSM) using Akka.NET, which ideally suits for this kind of tasks.

The Domain

As an engineer, You probably noticed that in many conferences there is a "voting machine", with usually red, green, yellow buttons so the folks can leave their feedback and evaluate a speaker. Let's go ahead and make a simple FSM for this voting scenario.

alt

The Implementation

The simplified domain model may look something like this.

A talk entity itself:

class Talk  
    {
        public string Speaker { get; set; }
        public string Title { get; set; }
        public DateTime EventDate { get; set; }
        public List<Vote> Votes { get; set; } = new List<Vote>();
    }

A vote entity:

 enum SatisfactionLevel
    {
        Red,
        Green,
        Yellow
    }

    class Vote
    {
        public SatisfactionLevel SatisfactionLevel { get; set; }
        public DateTime TimeStamp { get; set; }
    }

It goes without saying that we can not start voting before a talk is initiated, meaning our voting actor needs to be notified that it is allowed to accept votes for a particular talk.

Basically, we are going to send two messages BeginVoting to initiate and Vote to actually vote for the talk. In the simplest implementation scenario we would rather introduce some flag that voting is up and running:

        public SimpleVotingActor()
        {
            Receive<BeginVoting>(message =>
            {
                this.IsVoteOpen = true;
                this.Talk = message.Talk;
            });
            Receive<Vote>(message =>
            {
                if (IsVoteOpen)
                {
                    EmulateLatency();
                    Talk.Votes.Add(message);
                    Console.WriteLine($"Satisfacton: {message.SatisfactionLevel}; Timestamp: {message.TimeStamp}; Processed: {DateTime.Now}");
                }
            });
        }

In this trivial scenario having one "if" is not a showstopper. But what if we have a bunch of states? The good news is that Akka.net provides an elegant solution to this problem. You can define and switch your states/behaviors via Become method. Like this:

  public void ClosedForVoting()
        {
            Receive<BeginVoting>(message =>
            {
                this.Talk = message.Talk;
                Become(OpenForVoting);
            });
        }

        public void OpenForVoting()
        {
            Receive<Vote>(message =>
            {
                EmulateLatency();
                Talk.Votes.Add(message);
                Console.WriteLine($"Satisfacton: {message.SatisfactionLevel}; Timestamp: {message.TimeStamp}");
            });
        }

The code is much cleaner. It's immediately obvious what is going on out here.

Now let's look at some typical ticket's workflow from Issue & Project Tracking Software. Just five states, but a huge number of variations for each particular state. Implementing the workflow via ifs would not be so elegant:

alt

The full source of this sample is available here

comments powered by Disqus