Event sourcing: making it functional (7)

Posted on January 26, 2017 · 3 mins read · tagged with: #domain driven design #event sourcing

TL;DR

It’s the seventh chapter of making event sourcing functional. In the last post we introduced the base class for aggregates capturing events and applying them on the state. In this entry we question this choice and make the final step to make our code more functional. Let’s go!

All entries in this series:

Question your choices

Let’s again take a look at the method responsible for receiving the gateway response

paymentstate

This is a void method. It’s not a pure function though. It “returns” its result by passing emitted events to the apply function. We could rewrite it in the following manner:

paymentstate

Now you can probably see, that there are two methods in there. One that e mits, the event, possibly taking the state into consideration and the second that applies the event. We can make the first one even more generic and make it return IEnumerable of events.

PaymentState.png

Final revelation

Now you can see, that ReceiveImpl is the true implementation of the aggregate action, but it does not require the aggregate class at all! It gets the state, the action parameter and returns events! The ReceiveGatewayResponse is now just an infrastructure code that applies events, which is totally unneeded! We no longer have the Payment aggregate! All we have is just a set of functions that acts on the state basis, accepts some parameters and returns events. We can make it even an extension to call it in an easier way.

paymentstate

This pure function is so easy to test. You can test it using Given, When, Then with events, but you can test it with regular unit testing as well.

Now you can see that we were able to split the Payment aggregate into set of functions, that accept a state and other needed parameters returning a enumerable of events. Is there anything easier to test and to work with? For sure you need to pay some tax by introducing storing and applying events on the infrastructure side, but still it’s worth it, as we change aggregates, from being classes to just sets of simple functions.

Summary

I hope you enjoyed this series and that I was able to encourage you to see aggregates and event sourcing from a bit more functional point of view. All the commenters, thank you for providing valuable feedback. See you soon!


Comments

You're right. My concerns are about using bunch of static methods instead of encapsulating the functionality in self-contained, testable class. Whether with or without syntactic sugar, that still applies.

by Michał Gajek at 2017-01-29 19:26:09 +0000

What a plot twist! This explains my doubts on the aggregate projection relation ... but frankly, I don't like the idea of an aggregate being a bunch of extension methods over projection/state. I don't really know why, it just doesn't seem right. Maybe because it's the first time I've seen such a solution ...

by Michał Gajek at 2017-01-29 14:03:24 +0000

You could easily make them pure static methods, the extension part is not required. The biggest advantage is that they are side effect free. Events are not applied somewhere but just returned, hence the flow is much simpler imo

by Szymon Kulec 'Scooletz' at 2017-01-29 14:21:23 +0000

One of the reasons against such use of extensions methods came to my mind right after submitting the previous comment.

The mocking problem. I'm using NSubstitute for unit tests and remember tearing my hair out trying to figure out why didn't one of the mocks behave as I expected it to. It took me a while to realise I was creating a substitute for an interface to mock, yet the method I was working with (using "myInterface.Method().Returns(..)" ) was an extension method.

I feel like putting business logic in an extension methods is not a good practice.

I'll be very glad if you convince me otherwise :)

by Michał Gajek at 2017-01-29 14:10:51 +0000

The extension part is just a convenience. You can easily skip 'this'.

by Szymon Kulec 'Scooletz' at 2017-01-29 14:22:42 +0000