The journeylism of @yreynhout

On CQRS, DDD(D), ES, …

A role to play

Every so often someone new arrives at the DDD/CQRS list (*) and topics such as set based validation rear their head, resulting in near-endless threads of discussion and coming to a common understanding. A topic that isn’t as often discussed is one of roles in the domain model and how that would work in combination with event sourcing. If you want to read up on roles, Mark Seemann has some great posts on that topic on his blog, albeit in a slightly different context. There’s also this video by Udi Dahan about making roles explicit, which is more akin to what I’ll be touching upon here. Fanatics of whitepapers, might get their brain washed by papers like “Role Interfaces“, “The Role Object Pattern“, “Modeling Roles” or “Mock Roles, not Objects“.

Let’s make it practical

Suppose I’m building a Realtor app that has a finite number of real estate property types (think apartment, villa, house, warehouse, etc…). I could model each type of property as a class/type (**) putting specific behavior on each of them. I’d end up with Apartment, Villa, House and Warehouse as aggregate root entity types (and hence as aggregates). What if I had some common behavior that applies to all of them, where the calling code has a desire to be ignorant of the specific type, i.e. it is interested in the role that a property plays, not the specific type of property it represents. Let’s call that role ‘Property’ for lack of inspiration. I could implement it using a base class, an interface or even a totally separate class as we’ll see in a moment. The calling code could be about adding a set of properties to a listing:

public class AddPropertiesToListingService {
  readonly IListingRepository _listingRepository;
  readonly IPropertyRepository _propertyRepository;

  public AddPropertiesToListingService(IListingRepository listingRepository, IPropertyRepository propertyRepository) {
    _listingRepository = listingRepository;
    _propertyRepository = propertyRepository;
  }

  public void AddPropertiesToListing(ListingId listingId, PropertyId[] propertyIds) {
    var listing = _listingRepository.Get(listingId);
    var properties = propertyIds.Select(propertyId => _propertyRepository.Get(propertyId));
    listing.AddProperties(properties);
  }
}

If this was modeled using either an interface or base class to denote the role, the repository code (***) would look a bit like this:

public interface IPropertyRepository {
  Property Get(Guid id);
}

public class PropertyRepository : IPropertyRepository {
  readonly IPropertyFactory _factory;
  readonly IEventStreamReader _reader;

  public PropertyRepository(IPropertyFactory factory, IEventStreamReader reader) {
    _factory = factory;
    _reader = reader;
  }

  public Property Get(Guid id) {
    var result = _reader.Read(id);
    if(!result.HasValue) {
      throw new PropertyNotFoundException(id);
    }
    var root = _factory.Create(result.Value);
    root.Initialize(result.Value.Events);
    return root;
  }
}

//This could also be a Func<EventStream, Property>
public interface IPropertyFactory {
  Property Create(EventStream eventStream);
}

public class EventStreamAnalyzingPropertyFactory : IPropertyFactory {
  readonly Dictionary<Type, Func<Property>> _propertyFactories;

  public EventStreamAnalyzingPropertyFactory() {
    _propertyFactories = new Dictionary<Type, Func<Property>>();
    //Assume that each of the aggregate root entities
    //has a static Factory method that creates a new instance.
    _propertyFactories.Add(typeof(ApartmentRegistered), () => Apartment.Factory());
    _propertyFactories.Add(typeof(VillaRegistered), () => Villa.Factory());
    _propertyFactories.Add(typeof(HouseRegistered), () => House.Factory());
    _propertyFactories.Add(typeof(WarehouseRegistered), () => Warehouse.Factory());
    //Remark: Yes, this is an OCP violation.
  }

  public Property Create(EventStream eventStream) {
    Func<Property> propertyFactory;
    if(!_propertyFactories.TryGet(eventStream[0].GetType(), out propertyFactory))
      throw new PropertyUnknownException(eventStream.Id);
    return propertyFactory();
  }
}

public interface IEventStreamReader {
  Optional<EventStream> Read(Guid id);
}

public interface Optional<T> {
  bool HasValue { get; }
  T Value { get; }
}

public interface EventStream {
  Guid Id { get; }
  Int32 ExpectedVersion { get; }
  //In a real world implementation this would be streaming,
  //i.e. IEnumerable<object>
  object[] Events { get; }
}

public class PropertyNotFoundException : Exception {
  public PropertyNotFoundException(Guid id) { }
}

public class PropertyUnknownException : Exception {
  public PropertyUnknownException(Guid id) { }
}

//As an aside, Property could also be 
//turned into an interface to describe
//the role behavior.
public abstract class Property : AggregateRootEntity { 
  /* Common behavior can be put here */
}

//Similar for the other property types.
public class Villa : Property {
  public static readonly Func<Villa> Factory = () => new Villa();

  Villa() { /* ... */}

  /* Specific behavior can be put here */
}

Notice how the factory is made responsible for analyzing the event stream and deciding which Property type to instantiate based on the type of the first event. It should be obvious that “the type of the first event” is just one of the ways you could come to decision of which Property type to instantiate.

A slightly different scenario is one where you load the stream into a dedicated class, instead of relying on a base class or an interface to fulfill the role.

public class Property {
  public static readonly Func<Property> Factory = () => new Property();

  Property() { 
    /* Streams from each of the Property types can 
       be loaded into this Role class  */
    Register<ApartmentRegistered>(When);
    Register<VillaRegistered>(When);
    Register<HouseRegistered>(When);
    Register<WarehouseRegistered>(When);
    /* Notice how I haven't even brought up what 
       you could do if these were polymorphic
       messages */
  } 

  /* Common role behavior goes here */
}

public class PropertyRepository : IPropertyRepository {
  readonly IEventStreamReader _reader;

  public PropertyRepository(IEventStreamReader reader) {
    _reader = reader;
  }

  public Property Get(Guid id) {
    var result = _reader.Read(id);
    if(!result.HasValue) {
      throw new PropertyNotFoundException(id);
    }
    var root = Property.Factory();
    root.Initialize(result.Value.Events);
    return root;
  }
}

This is exactly why you shouldn’t use the type name of an aggregate as a form of stream identification (at least if you want to support this kind of scenario). The repository will just load up the stream, being totally ignorant of what class was used to produce the events in the stream in the first place and happily feed it to the Property class.

Another scenario where this technique could prove to be useful is when your entity goes through a life cycle where each state has very different behavior or behavior needs to be limited as of a certain stage in its life cycle. Of course, this shouldn’t be used as an excuse to NOT model things explicitly.

Conclusion

The most important takeaway is that a stream of events does not need to be loaded into the same class all the time and that roles remain useful within a model backed by event sourcing. Like anything, this should be used with moderation and only if applicable.

(*) I’m reliving a scene with Arnold in Total Recall (1990) as I’m writing this (http://www.youtube.com/watch?feature=player_detailpage&v=WFMLGEHdIjE#t=86s).
(**) Having worked in the Realtor business, I can tell you right off the bat that having a class per real estate property type is going to hurt in the long run, but who am I to judge about the usefulness of this particular model.
(***) Don’t complain if the code doesn’t compile out of the box. I used my C# brain compiler.

5 responses to “A role to play

  1. Mathias Verraes February 24, 2013 at 15:12

    I suppose the idea of having multiple Aggregate types dealing with the same events, could be useful when refactoring a legacy Event Sourced application (in a distant future, because I don’t think there are legacy ES applications yet).
    While LegacyProperty still publishes and consumes events, NewProperty is already being used by new parts of the code. Gradually you can move old code to use NewProperty — or maybe not, and leave both Aggregate types to happily coexist.

    Or maybe I just spend too much time dealing with brownfield code đŸ˜‰

  2. Marijn February 24, 2013 at 16:20

    Nice explanation. This might be of particular interest when working with event sourced saga’s/process managers.

Leave a comment