Header menu logo FCQRS

Adding Sagas

So far our aggregate was simple and didn't have side effects. But what happens if we want to send an email or make a call to an external service? Or what happens if we want to send a command to another aggregate? In this case we need to use a saga. A saga manages a potentionally long-running business process which interacts with one or more external services or resources it can also act as a Distributed Transaction Manager.
Sagas can include logic for timers and retries as well.

We will revise our app such that it will now send a verification email to the user when they register. For that let's add a Send Mail actor. Note that this can be done without an actor, but our sagas nicely support sending messages to actors. So far our user aggregate was a cluster sharded actor. Whereas sagas will also be cluster sharded actors with auto start mode thanks to remember-entities feature of Akka.NET. Akka.net would auto restart any actor makred as rembember entities which we specify while initializing the saga. This is set for you when you initialize the saga. You can find the full sample code in the sample folder of repo.

The mail sending actor however will be a normal actor. Inside send mail module:

open Akkling

type Mail =
    { To: string
      Subject: string
      Body: string }


let behavior (m: Actor<_>) =
    let rec loop () =
        actor {
            let! (mail: obj) = m.Receive()
            match mail with
            | :? Mail as mail ->
                printfn "Sending mail to %A !!" mail
                m.Sender().Tell("sent", m.Self.Underlying 
                    :?> Akka.Actor.IActorRef)
                return! loop ()
                
            | _ ->
                return! loop ()
        }

    loop ()
Multiple items
module System from Akkling

--------------------
namespace System
namespace System.IO
namespace Microsoft
namespace Microsoft.Extensions
namespace Microsoft.Extensions.Configuration
namespace Hocon
namespace Hocon.Extensions
namespace Hocon.Extensions.Configuration
namespace Akkling
type Mail = { To: string Subject: string Body: string }
Multiple items
val string: value: 'T -> string

--------------------
type string = System.String
val behavior: m: Actor<obj> -> Effect<obj>
val m: Actor<obj>
Multiple items
type Actor = inherit UntypedActor interface IWithUnboundedStash new: unit -> Actor

--------------------
type Actor<'Message> = inherit ICanWatch inherit IActorRefFactory abstract Parent: unit -> IActorRef<'Other> abstract Receive: unit -> IO<'Message> abstract Schedule: TimeSpan -> IActorRef<'Scheduled> -> 'Scheduled -> ICancelable abstract ScheduleRepeatedly: TimeSpan -> TimeSpan -> IActorRef<'Scheduled> -> 'Scheduled -> ICancelable abstract Sender: unit -> IActorRef<'Response> abstract SetReceiveTimeout: TimeSpan option -> unit abstract Stash: unit -> unit abstract Unstash: unit -> unit ...
<summary> Exposes an Akka.NET actor API accessible from inside of F# continuations </summary>

--------------------
new: unit -> Actor
val loop: unit -> Effect<obj>
val actor: ActorBuilder
<summary> Builds an actor message handler using an actor expression syntax. </summary>
val mail: obj
type obj = System.Object
abstract Actor.Receive: unit -> IO<'Message>
val mail: Mail
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
abstract Actor.Sender: unit -> IActorRef<'Response>
property Actor.Self: IActorRef<obj> with get
<summary> Gets <see cref="IActorRef" /> for the current actor. </summary>
property ICanTell.Underlying: Akka.Actor.ICanTell with get
namespace Akka
namespace Akka.Actor
type IActorRef = inherit ICanTell inherit IEquatable<IActorRef> inherit IComparable<IActorRef> inherit ISurrogated inherit IComparable member Path: ActorPath
<summary> An actor reference. Acts as a handle to an actor. Used to send messages to an actor, whether an actor is local or remote. If you receive a reference to an actor, that actor is guaranteed to have existed at some point in the past. However, an actor can always be terminated in the future. If you want to be notified about an actor terminating, call <see cref="M:Akka.Actor.ICanWatch.Watch(Akka.Actor.IActorRef)">IActorContext.Watch</see> on this actor and you'll receive a <see cref="T:Akka.Actor.Terminated" /> message when the actor dies or if it is already dead. </summary>
<remarks>Actor references can be serialized and passed over the network.</remarks>

Type something to start searching.