Handling domain events

Last time I wrote a post about the commands. Events are quite similar to the commands – the main difference between these 2 patterns is that the events are related to the things that have actually happened to our entity e.g. by invoking the command. They’re also a core part of the DDD (Domain Driven Design) and can be easily implemented within our software solution.


 

Let’s define our event interface at first:

Now, we can move further and take a look into the base Entity class:

In case you were wondering why I’m using a dictionary here – thanks to this type of a collection I can avoid adding multiple events of the same type e.g. UserEmailChanged and then invoking them multiple times. Yet, again – it’s totally up to you, so feel free to choose the preferable solution.

So far so good, eventually we can define our event once the user’s email was changed.

Remember that you’d better keep your events very thin. Sure thing, you could pass the whole user object, but think outside of the box – what if you want to scale your application on multiple servers? By passing the minimum amount of the information required to identify the entity (e.g. it’s id and maybe an old value along with the new one) you will be able to push such event by message bus or any other communication protocol and handle easily by some external application, without worrying about (de)serialization issues and huge traffic due to the large objects.

And here’s our user:

As you can see, whenever the user changes the email address, we will store such event.
And here comes to the question, how to handle this event? Well, quite easily, so let’s start with the event handler definition:

And the event dispatcher based on the Autofac IoC container:

Speaking of the Autofac, we have to register our handlers and dispatcher, so that IoC magic will be able to happen:

And that’s pretty much it. What you can do now, is, for example, the following:

My approach to the events is mostly based on the Jimmy Bogard’s post which is an opposite to the static pattern described by the Udi Dahan here.

42 Comments Handling domain events

  1. Pingback: Handling domain events - How to Code .NET

  2. Philipp

    var method = handler.GetType()
    .GetRuntimeMethods()
    .First(x => x.Name.Equals(“HandleAsync”));

    This will not work if a handler implements multiple IEventHandler interfaces. This can be a valid possibility. E.g. Sagas that mutate the state on multiple different events.
    Its better to do it like this: ((dynamic)handler).HandleAsync(@event)
    (because you get the handler using the interface you know that method exists)

    Reply
  3. Pingback: Dew Drop - August 1, 2016 (#2299) - Morning Dew

  4. Raham Rahim

    Excellent article, but wouldn’t make more sense to use a ConcurrentDictionary instead of a plain Dictionary?

    Reply
    1. Piotr Gankiewicz

      Hi Jernej,
      In my scenario, I publish the events after the transaction has been completed – for example within the IUserService -> CreateAsync(); dispatching events is the last thing that happens only after the particular operation (e.g. creating and saving a new user in the database) was successfully completed. I hope that it does answers your question (rolling back the transaction would prevent invoking the event dispatcher).

      Reply
  5. Pingback: "Use your feelings, Obi-Wan, and find Links you will." – Yoda - Magnus Udbjørg

  6. jernej

    Makes sense. But I forgot to mention I’m working with a long living db context (desktop app). So after rolling back I would need to clear the _events collection in every entity that participated in the failed transaction whereas if you are returning events from commands, you are not mutating states of any entities.

    Reply
  7. jernej

    One cool side effect when returning events from commands is that you can have a EventDispatcherCommandHandler that you can wrap arround a TransactionalCommandHandler and let the command handling pipeline automatically dispatch events so you don’t need to dispatch them manually from application services 🙂

    Reply
  8. Pingback: Zbudujemy nowy DOM – Dev on board

  9. Zomat

    You used 2 constructor for User class. One of them is paremeterless and other gets email parameter. Do you want to take precautions to don’t send email while update data?

    Reply
  10. 0xmarcin

    I agree that event classes should be small and contain only serializable state, but in case of UserEmailChanged shouldn’t we also include old and new value of user email. Let’s consider situation in which user quickly changes emails from foo1@example.com to foo2@example.com and to foo3@example.com – then if our infrastructure responsible for event processing will not be fast enough we may lost information about first change. Storing additional data in the event also will allow us to more easily debug production issues (if we save event’s somewhere of course).

    Reply
    1. Piotr Gankiewicz

      Of course, this is a good example and I also do it like this. A few properties are good enough, we just have to keep in mind that passing nested objects via service bus is rather not a best choice.

      Reply
      1. JinX

        What about “SomethingCreatedEvent” when you are applying CQRS so you have to update the querry database? You’ll need sometimes 15-20 properties to materialize complete view in query database. Any alternatives?

        Reply
        1. Piotr Gankiewicz

          That should not be an issue at all. When you make use of the events in memory (not shared across service bus), feel free to include the whole object if needed. You could also send only the entity ID and then fetch the actual object using some repository or so.

          Reply
  11. Igor Kuznetsov

    With Autofac extension generic method ResolveOptional we can do instead of this:
    var eventType = @event.GetType();
    var handlerType = typeof(IEventHandler).MakeGenericType(eventType);
    object handler;
    _context.TryResolve(handlerType, out handler)
    if (handler == null)
    return;
    var method = handler.GetType()
    .GetRuntimeMethods()
    .First(x => x.Name.Equals(“HandleAsync”));
    await (Task) ((dynamic) handler).HandleAsync(@event);

    just this:
    var handler =_context.ResolveOptional<IEventHandler<T>>();
    if (handler == null) return;
    await handler.HandleAsync(@event);

    Reply
    1. Igor Kuznetsov

      A little mistake in my comment. ResoveOptional should be called with type argument IEventHandler:
      var handler =_context.ResolveOptional<IEventHandler>();

      Reply
      1. Igor Kuznetsov

        oh, now I see, that’s is not my mistake. Type argument was automatically removed . Type argument should be “IEventHandler” (remove underscore)

        Reply
  12. Charlie

    Great post, just interest where would you like to put your event handler? and why? I think the application service may be a good layer to put the event handler.

    Reply
  13. Bilal

    Hi
    Can you share a sample code using the above code to see the full picture when it comes to using Ioc, UserService, etc.

    Thanks
    Bilal

    Reply
  14. B

    First of all thank you so much for sharing this. I’ve been searching for this for a long time. You have no idea how happy it made me upon reading this article. Keep up the good work!

    With that said – I’m getting this error. Can you please throw some light on this?

    The best overloaded method match for ‘Domain.Services.Processing.Order.SalesOrderLineCreateHandler.HandleAsync(Models.Entities.Processing.Order.SalesOrderLineCreated)’ has some invalid arguments

    happens on the last step of the EventDispatcher
    await (Task) ((dynamic) handler).HandleAsync(@event);

    Reply
  15. Jarek

    Are you using handler to get state of DM?
    public async Task HandleAsync(UserEmailChanged @event)
    {
    //Fetch the user from database by id, log this event, store some data etc.

    You can’t do like that.

    Register handler by Container has one problem. You can use only one scenario per event. What you gone do if two application services will use the same event which has difference user case. ?

    Reply
  16. Jarek

    AddEvent(new UserEmailChanged(Id));
    It should belong to application services or less common scenario to domain services.

    DM should only publish the event. Responsibility of subscribe events not belong to DM.

    Reply
    1. Piotr Gankiewicz

      Domain models do not publish events at all. They only store the events in their internal collection (as shown above) and only then, events are being dispatched by EventDispatcher.

      Reply
  17. michiro

    Isn’t there a bug in dispatcher code, i think lines:

    if (handler == null)
    return;

    should be replaced with:

    if (handler == null)
    continue;

    if we don’t have a single handler we still want to handle other types of events (we are in foreach loop).

    Reply
  18. Vinay

    HI PIOTR

    I created a sample application based on your post and In EventDispatcher.cs class, I got handler=null in DispatchAsync() method. due to this my handler didn’t invoke.

    Please do the needful.

    Thanks in advance.

    Reply

Leave A Comment

Your email address will not be published. Required fields are marked *