Sponsor: Do you build complex software systems? See how NServiceBus makes it easier to design, build, and manage software systems that use message queues to achieve loose coupling. Get started for free.
Separating data and behaviors can have a pretty profound impact on your design. An Event Sourced Aggregate allows you to focus on business logic by having capabilities to produce data (Events). Event Sourcing does exactly this by limiting the amount of state you require only to that needed for business logic within an Aggregate.
Check out my YouTube channel where I post all kinds of content that accompanies my posts including this video showing everything that is in this post.
Before jumping in code, the example I’m going to use is of a shipment. The simple setup is you have a shipment that has two stops. The first stop is the Pickup and the last stop is the Delivery. You could think of this as a food delivery service where the shipment is of your food. A delivery driver goes to the Pickup Stop, which is the restaurant that you’re ordering from, and the delivery stop is your house.
Each stop will go through a transition in its status. At first, a stop will be “In Transit” and then will progress to “Arrived” once the delivery driver has reached the stop. Once the driver leaves the stop and heads to the next stop, the status will change to “Departed”. Once each stop goes through its transition, the shipment is completed.
Here’s an example using only the current state without event sourcing. The state is represented by properties on the ShipmentAggregateRoot and the list of Stops.
The primary reason we have this aggregate is to control the invariants and create a consistency boundary. Check out my post Using Invariants as a Guide when designing aggregates. In this case, we have a few invariants but the one I want to point out is in the Arrive() method. We need to check that all other previous stops have Departed and gone through their full progression before we can call Arrive on the next stop.
Event Sourced Aggregate
The first step in moving our aggregate towards event sourcing is actually creating events.
The next step is defining the state that we need in our aggregate. This is the key point of this post. The state we need is only for our invariants (business logic) in the aggregate. We do not need any other data in our state other than what we use for business logic.
Finally, here is the new Aggregate that is using the defined Events and the ShipmentState.
If you compare the Event Sourced version with the previous version, you will notice that we’re sorely focused on the behavior methods and the business logic. The projection/state we’re recording is much slimmer as it’s now recorded separately in the events.
Our tests are incredibly simple as we don’t really need to build up a large data model for our aggregate.
Developer-level members of my CodeOpinion YouTube channel get access to the full source for any working demo application that I post on my blog or YouTube. Check out the membership for more info.