Sunday, March 2, 2014

Alternatives to Udi's domain events

Almost four years ago Udi Dahan introduced an elegant technique that allows you to have your domain model dispatch events without injecting a dispatcher into the model - keeping your model focused on the business at hand.

This works by having a static DomainEvents class which dispatches raised events.

This customer aggregate raises an event when a customer moves to a new address.
public class Customer
{
    private readonly string _id;
    private Address _address;
    private Name _name;

    public Customer(string id, Name name, Address address)
    {
        Guard.ForNullOrEmpty(id, "id");
        Guard.ForNull(name, "name");
        Guard.ForNull(address, "address");

        _id = id;
        _name = name;
        _address = address;
    }

    public void Move(Address newAddress)
    {
        Guard.ForNull(newAddress, "newAddress");

        _address = newAddress;

        DomainEvents.Raise(new CustomerMoved(_id));
    }
}
By having a dispatcher implementation that records the events instead of dispatching them, we can test whether the aggregate raised the correct domain event.
var recordingDispatcher = new RecordingDispatcher();
DomainEvents.Dispatcher = recordingDispatcher;

var customer = new Customer(
    "customer/1",
    new Name("Jef", "Claes"),
    new Address("Main Street", "114B", "Antwerp", "2018"));
customer.Move(new Address("Baker Street", "89", "Antwerp", "2018"));

recordingDispatcher.Raised(new CustomerMoved("customer/1")); // true
While this worked out great for a good while, I bumped into difficulties scoping my unit of work and such when I redid some of my infrastructure. While there are ways to have your container address these issues, getting rid of the static components is simpler throughout.  

A popular event sourcing pattern is to have your aggregate record events. There is no reason why we couldn't apply the same pattern here. Using this technique, we still avoid having to inject something into our models, plus we get rid of that static DomainEvents component. Reponsibility of dispatching the events is now delegated to an upper layer.
public class Customer : IRecordEvents
{
    private readonly EventRecorder _recorder = new EventRecorder();

    private readonly string _id;
    private Address _address;
    private Name _name;

    public Customer(string id, Name name, Address address)
    {
        Guard.ForNullOrEmpty(id, "id");
        Guard.ForNull(name, "name");
        Guard.ForNull(address, "address");

        _id = id;
        _name = name;
        _address = address;
    }

    public EventStream RecordedEvents() 
    {
        return _recorder.RecordedEvents();
    }

    public void Move(Address newAddress)
    {
        Guard.ForNull(newAddress, "newAddress");

        _address = newAddress;

        _recorder.Record(new CustomerMoved(_id));
    }
}

var customer = new Customer(
    "customer/1",
    new Name("Jef", "Claes"),
    new Address("Main Street", "114B", "Antwerp", "2018"));
customer.Move(new Address("Baker Street", "89", "Antwerp", "2018"));

customer.RecordedEvents().Contains(new CustomerMoved("customer/1")); // true
Another altnernative is to return events from your methods. This technique puts the responsibility of aggregating all events on to a higher layer. Better to put that closer to the aggregate.

What patterns are you using? 

5 comments:

  1. Isn't your approach causing problems too?
    The EventRecorder is still a dependency, which is initialized by the Customer instead of injected.
    What if you want to use a different recording mechanism?
    I feel like the recording infrastructure of the events doesn't belong inside the AR.
    I think the Customer should only contain state about its customer, not about the events it generated.

    I'm a bigger fan of the second approach, which gives you more flexibility in how the events should be processed further (record them, raise them immediately,...) and it isn't necessary to know the internals of the whole event recording.
    You are also sure to only get the events which are generated by the specific method you called.
    On the other side it also feels 'weird' to return a list of events for each method in your AR.

    Pure for readability I still like Udi's approach the most, but I also had problems when using it with a unit of work,
    although I think a good IoC offers a relatively easy way to keep the unit of work and the events recorder int the same scope.

    ReplyDelete
    Replies
    1. EventRecorder is as much a dependency as a value object or as a list. I feel comfortable taking a hard dependency on that, it's not something I need to swap out.

      I would say I'm mostly interested in the events an aggregate generated during it's lifetime. I don't care that much about the impact of one method.

      Yes, Udi's approach still reads best. While you can get his approach to work for sure, I prefer not to depend too much on the container these days - so much magic going on ;).

      Delete
  2. An alternative that still allows Injection of a sort: https://gist.github.com/bdavisx/9330012

    ReplyDelete
    Replies
    1. I'm using this pattern for DateTime.Now :)

      Delete
  3. Hi Jef,

    Please have a look at https://github.com/NikGovorov/Taijutsu, it covers: " I bumped into difficulties scoping my unit of work and such when I redid some of my infrastructure. While there are ways to have your container ..."

    ReplyDelete