SimpleCQRS the F# version
Here it is ! As promised after greg’s talk at dddx (you can already see all the presentations online !), a F# version of SimpleCQRS, a simple, quick EventSourcing + CQRS sample to see it in action.
Why rewrite it in F# ?
This is not just a simple copy of the C# version. The point was first to write it in a functional language, because event sourcing is inherently functional.
In C#, an aggregate method looks like this :
{
if(count <= 0)
throw new InvalidOperationException(
"must have a count greater than 0 to add to inventory");
ApplyChange(new ItemsCheckedInToInventory(_id, count));
}
The ApplyChange method is defined in the AggregateRoot base class.
It dispatches the event to a state denormalizer and add the Event to uncommitted changes.
Here is a denormalizer to apply a state change following an event :
_activated = false;
}
All this is fine, but why should the aggregate be mutable when the event stream is highly a append only store of immutable events.
Watch greg’s talk carefully, both methods can be transformed to an immutable equivalent easily.
First the goal of the public method is to determine which event to raise based on command parameters and current state. The CheckIn method can be defined by the following method signature:
int –> State –> Event or Func<int,State,Event>
Instead of calling an Apply change internally, the method simply returns an event :
let checkIn count s =
if count <= 0 then
raise (InvalidOperationException
"must have a count greater than 0 to add to inventory")
fire {ItemsCheckedInToInventory.Id= s.Id; Count = count}
where the fire function simply creates a Event array from a single event :
let fire o =
[o :> Event]
(notice the [o:> smiley here !)
An array is returned here so that a method can fire/return several events using a simple :: syntax.
The state application can also become immutable. The function gives next state based on event and previous state :
State –> Event –> State or Func<State,Event,State>
let applyOnInventoryItem s (e: Event) =
match e with
| :? InventoryItemCreated as e -> {Id = e.Id; Activated = true }
| :? InventoryItemDeactivated as e -> {s with Activated = false; }
| _ –> s
no need for several methods here, every thing is straight forward. Match the event using its type.
The first event is a creation, so a state record is creates.
The second events copies state s with Activated set to false. No change occurs here, a copy is returned.
The _ match specifies that any other event simply return previous state.
Done.
The current state is a left fold of passed events
Sure and the replayWith method is simply here to do this :
let replayWith =
Seq.fold
let replayInventoryItem =
replayWith applyOnInventoryItem { Id = Guid.Empty; Activated = false}
the replayInventoryItem is a function that takes a Event seq aka IEnumerable<Event>. It will start with the empty state, then for each event, call the applyOnInventoryItem function with previous state, current event, and iterate with new state.
The result is the current state.
Command handlers
The event handlers use the following functions :
let load id =
eventStore.GetEventsForAggregate id |>
replayInventoryItem
let save = eventStore.SaveEvents
let applyOn id version f =
load id |>
f |>
save id version
load simply pass events for the aggregate with identifier id to the replayInventoryItem
save is simply a short cut for the event store SaveEvents method
applyOn loads an aggregate to current state, pass state to f, a function that returns an event seq, then save it to the event store.
Here is a sample of its use :
member x.Handle (c: CheckInItemsToInventory) =
checkIn c.Count |>
applyOn c.InventoryItemId c.OriginalVersion
The checkIn function actually expect an second State argument, it signature is int –> State –> Event seq
After passing the c.Count integer argument this is now a State –> Event seq function.
When passed to the applyOn function, the state issued from the load call will be passed to it, resulting in an Event seq that will be passed to the save function.
Conclusion
There are a few things to notice in this implementation.
- It is very short, much shorter that C# version.
- There is no InventoryItem class. The InventoryItem module contains a State record, the representation of the aggregate internal state, functions to determine raised events, and functions to determine next state based on previous state and event. No base class is needed for event dispatch and uncommitted event handling.
- There is no repository. Actually the load and save methods do what a repository does, but it’s so simple that no class is required.
I did not talk about the read model that is a bit less interesting here.