diyes: time for aggregates

Hi again,

After we have seen how the event store works by saving and loading events related with an identity, in this post I am going up in the abstraction layer and talk about aggregates. A very good introduction to aggregate design can be found in Vaughn Vernons’ articles – Effective Aggregate Design.

We are going to see how we can use the event store to load and store aggregates (business objects). To do this I created an AggregateRepository class with the following interface

public interface IAggregateRepository
{
  T Load<T>(IIdentity aggregateId) where T : AbstractAggregate;
  void Save<T>(T abstractAggregate) where T : AbstractAggregate;  
  EventStore Store { get; }
}

Loading aggregates

Now lets dig into the Load code

public T Load<T>(IIdentity aggregateId) where T : AbstractAggregate
{
  var eventStream = Store.LoadEventStream(aggregateId);
  var typeT = typeof(T);
  var ctor = GetAggregateConstructor<T>(typeT);
  var instance = (T) ctor.Invoke(new object[] { eventStream });
  return instance;
 }

It looks quite simple I think, first we load the event stream for said aggregate using its id, then we find the aggregate constructor that has an event stream as parameter, and then we simply create a new aggregate instance using that constructor. Now you are probably thinking about what the hell is the constructor with the event stream as parameter right? To make my life easier, we demand that all the aggregates have to inherit the AbstractAggregate abstract class. This base class for the aggregates contains meta-information and methods that can help loading and saving of our aggregates. One of those methods, is the constructor that receives an event stream as parameter.

protected AbstractAggregate(EventStream eventStream)
{
  Id = eventStream.Id;
  Version = eventStream.Version;
  Changes = new List<IEvent>();  

  foreach (var @event in eventStream.Events)
  {
    Mutate(@event);
  }
}

The constructor is used to apply all the events that came from this aggregate event stream, by repeatedly mutating the aggregate state by applying the events.

Saving aggregates

As you might have noticed in the previous snippet, the AbstractAggregate class contains a Changes collection which is a list of events. This collection represents the events applied to this aggregate to mutate its state. We use this collection of events when we need to save what happened to the Aggregate after we have loaded it. This is how the Save method on the AggregateRepository looks

public void Save<T>(T abstractAggregate) where T : AbstractAggregate
{
  var aggregateId = abstractAggregate.Id;
  var version = abstractAggregate.Version;
  var changes = abstractAggregate.Changes;
  Store.AppendToStream(aggregateId, version, changes);
}

Basically we use the event store to store the changes (I explained how the event store works in the previous blog post). Pretty simple, right? The version is used by the store to check against concurrent modifications of the aggregate. After this, the aggregate is saved and ready to be reloaded when needed!

 

Conclusion

After these three blog posts I explain how we can use an event store to keep our aggregates, and how we can get them again. I didn’t show how to use a real aggregate and events yet, but there is already an example in the unit tests. I will write my next post about how to model aggregates using the AbstractAggregate though.

At this point, I think the code on the diyes project is looking pretty good, I am open for code reviews and suggestions if you are interested in pointing something out.

If you by any chance took a look in the repository you might have seen that I already implemented snapshoting of aggregates which will be the topic of one of next posts. Until then, have a good one!

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s