Reactive Messaging Patterns with F# and Akka.NET


This article is part of the F# Advent Calendar in English 2015 organized by Sergey Tihon.

I've recently read the book Reactive Messaging Patterns with the Actor Model by Vaughn Vernon. This book applies the patterns described in Enterprise Integration Patterns using Scala language and Akka (Actor Model).

As I am an F# fan, I thought it would be good to translate the book examples to F# and Akka.NET. If you already know F# and Akka.NET (or want to learn), you may find the examples I share here useful while reading the book. Additionally, if you are interested in the Scala examples described in the book, you can find them here.

Sections

  1. Introduction
  2. Messaging with Actors
  3. Messaging Channels
  4. Message Construction
  5. Message Routing
  6. Message Transformation
  7. Message Endpoints
  8. System Management and Infrastructure

Introduction

Before we start, it is worth mentioning that the Akka.NET F# API provides different ways to create actors, the simplest one allows you to do it in just one line of code!

1: 
let actorRef = spawn system "myActor" (actorOf (fun msg -> (* Handle message here *) () ))

Although this is very impressive, the truth is that sometimes you will need more control over the way the actor is created and how it interacts with Akka.NET. So, to keep the code examples consistent, I chose to use a more advanced technique, the actor computation expression:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
let myActor (mailbox: Actor<_>) = 
    let rec loop () = actor {
        let! message = mailbox.Receive ()
        // Handle message here
        return! loop ()
    }
    loop ()

This second option is more verbose but it is also more powerful, as you have full access to the mailbox and you can control when the recursive function is executed.

As you can see, an actor is just a function that:

  • Receives the mailbox as parameter
  • Returns an actor computation expression

The mailbox is of type Actor<'a>, where 'a is the type of message the actor will handle. In most cases you can leave the type as Actor<_> and the F# compiler will infer the right message type for you.

The actor computation expression is returned using a self-invoking recursive function called loop. Its first line let! message = mailbox.Receive () is receiving the message sent to the actor. If no message is available yet, the actor will be blocked until a message arrives. After the message is received and handled, the line return! Loop () is executed, which invokes the loop again to wait for the next message.

Finally, the last line loop () executes the recursive function for the first time, starting the actor.

Don't worry if you couldn't follow the code easily, it took me a while to get my mind around it too. I recommend you to write a few actors to fully understand how it works.

Once you have defined an actor, you can create a new instance using the spawn function:

1: 
let actorRef = spawn system "myActor" myActor

Here we need to provide three things: the actor's system, a unique name ("myActor") and the actor function (myActor).

Now that you have created the actor, you can send it messages in this way:

1: 
actorRef <! "message"

How to Run the Examples

  1. Clone https://github.com/jorgef/fsharpreactivepatterns.git (more info: cloning a repository)
  2. Open FSharpReactivePatterns.sln and build the solution (you need internet connection, it will download the dependencies)
  3. Open the example (fsx file) you want to run
  4. Select all the code except the part where messages are sent to the actor(s), and send it to F# Interactive
  5. Clear the F# Interactive (optional)
  6. Select the code that calls the actor(s) and send it to F# Interactive

How to Run

What's Next

Great! Now that you know the basics you can start browsing and running the patterns, enjoy!

Let me know what you think: @jorgefioranelli

Special Thanks

val actorRef : obj

Full name: index.actorRef
val myActor : mailbox:'a -> 'b

Full name: index.myActor
val mailbox : 'a
val loop : (unit -> 'c)
F# Project
Fork me on GitHub