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 ()
module System from Akkling
--------------------
namespace System
val string: value: 'T -> string
--------------------
type string = System.String
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
<summary> Builds an actor message handler using an actor expression syntax. </summary>
<summary> Gets <see cref="IActorRef" /> for the current actor. </summary>
<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>