A better domain events pattern

Domain events are one of the final patterns needed to create a fully encapsulated domain model – one that fully enforces a consistency boundary and invariants. The need for domain events comes from a desire to inject services into domain models. What we really want is to create an encapsulated domain model, but decouple ourselves from potential side effects and isolate those explicitly. The original example I gave looked something like this:

public Payment RecordPayment(decimal paymentAmount, IBalanceCalculator balanceCalculator)
{
    var payment = new Payment(paymentAmount, this);

    _payments.Add(payment);

    Balance = balanceCalculator.Calculate(this);

    if (Balance == 0)
        DomainEvents.Raise(new FeePaidOff(this));

    return payment;
}

There’s one small problem with our domain event implementation. Because the domain events class is static, it also dispatches to handlers immediately. This makes testing our domain model difficult, because side effects, however crazy, are executed immediately at the point of raising the domain event.

Typically, I want the side effects of a domain event to occur within the same logical transaction, but not necessarily in the same scope of raising the domain event. If I cared enough to have the side effects occur, I would instead just couple myself directly to that other service as an argument to my domain’s method.

Instead of dispatching to a domain event handler immediately, what if instead we recorded our domain events, and before committing our transaction, dispatch those domain events at that point? This will have a number of benefits, besides us not tearing our hair out. Instead of raising domain events, let’s define a container for events on our domain object:

public interface IEntity
{
    ICollection<IDomainEvent> Events { get; }
}

In our method that raises the domain event, instead of dispatching immediately, we simply record a domain event on our entity:

public Payment RecordPayment(decimal paymentAmount, IBalanceCalculator balanceCalculator)
{
    var payment = new Payment(paymentAmount, this);

    _payments.Add(payment);

    Balance = balanceCalculator.Calculate(this);

    if (Balance == 0)
        Events.Add(new FeePaidOff(this));

    return payment;
}

This makes testing quite a bit simpler since we don’t this global domain event dispatcher firing things off, we can just assert on our self-contained entity class:

[Test]
public void Should_notify_when_the_balance_is_paid_off()
{
    Fee paidOffFee = null;

    var customer = new Customer();

    var fee = customer.ChargeFee(100m);

    fee.RecordPayment(100m, new BalanceCalculator());

    var paidOffEvent = fee.Events.OfType<FeePaidOff>().SingleOrDefault();
    
    paidOffEvent.ShouldNotBeNull();
    paidOffEvent.Fee.ShouldEqual(fee);
}

Finally, we need to actually fire off these domain events. This is something we can hook into our infrastructure of whatever ORM we’re using. In EF, we can hook into the SaveChanges method:

public override int SaveChanges() {
    var domainEventEntities = ChangeTracker.Entries<IEntity>()
        .Select(po => po.Entity)
        .Where(po => po.Events.Any())
        .ToArray();

    foreach (var entity in domainEventEntities)
    {
        var events = entity.Events.ToArray();
        entity.Events.Clear();
        foreach (var domainEvent in events)
        {
            _dispatcher.Dispatch(domainEvent);
        }
    }

    return base.SaveChanges();
}

Just before we commit our transaction, we dispatch our events to their respective handlers. With this approach, we decouple the raising of a domain event from dispatching to a handler, which is much more obvious to me as a developer. If you want immediate side effects with other entities, just pass those in as arguments.

In dispatching our domain events, we have some flexibility now. We can dispatch synchronously, as we did in our EF example, or asynchronously by persisting our events as JSON and having an offline process pick up those undispatched events and dispatch them out-of-band. All of this is decoupled from our domain event raiser and handler.

If there’s one lesson to be learned here, it’s to beware of static, opaque dependencies, even it is a really cool concept. Separating the two concerns of raising versus dispatching keeps our domain model fully encapsulated without introducing land mines in our model.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in Domain-Driven Design, Entity Framework. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://tech.trailmax.info/ trailmax

    If you use Ambient Context pattern for domain events, you can have static call in your models, but able to replace them for unit testing: https://gist.github.com/trailmax/30f1f73fff08fcd2064d
    No need to pollute your models with lists of events, store events in a list in domain context and then on save process/purge them, as you do in DbContext.

    • NathanGonzalez

      The model that Jimmy has laid out is actually much more in line with Evan’s original vision for DDD. Events should be stored on a list in the actual aggregate. The publishing model is a little weird, I’d think that only one aggregate should probably have events in a given transaction, but that’s a little beside the point.

      • http://tech.trailmax.info/ trailmax

        Right now I can not see a benefit having events in the aggregate. But perhaps there are scenarios where this comes handy.
        Just merely highlighting that static calls can also be testable.

        • jbogard

          That was my original design (see the first post), but it turns out the static nature of it, and synchronous dispatching cause confusing side effects that aren’t obvious.

      • Thomas Eyde

        Don’t know much about the original vision, but I like the event recording pattern. I agree the publishing model is weird. I don’t like the idea of hooking up to my ORM. My ORM should take care of data persistence, nothing else.

        What if we record the events to our event bus and let our command dispatcher take care of the event dispatching?

        • jbogard

          That’s another option, but I’m still not a fan of a static recorder. Shared state is problematic, with the events on the entity it’s all self-contained.

          We’ve also done dispatching via decorators (like an action filter or something similar). The ORM was just one example, but it was a spot where I could be guaranteed to catch *all* events without having to have some global state or collecting parameter everywhere.

  • NathanGonzalez

    what is the allure of domain events without event sourcing? it seems like an over complicated way to decouple your system without any practical benefit. I guess maybe the question is why would you go to this trouble without implementing event sourcing?

    • RyanVice

      We found them useful for side effects like sending push notifications or posting to queues for out of process handling. Without Domain Events, your Domain Model can get weighted down with implementation details.

      • NathanGonzalez

        Do you not experience transactional oddities sometimes since your push notifications are not bound by the same transaction scope? I can see the value of using this as an extension point for sure, but it seems like you could get in trouble pretty quickly.

        • RyanVice

          You have to implement the transaction piece as part of it and this blog post is one recommending an approach for how to do that.

    • jbogard

      Event sourcing is adding a lot, domain events are much, much less invasive to our codebase. Also, this is all in a transaction so side effects participate in the same transaction as everything else.

      • NathanGonzalez

        I get the value proposition now that I’ve chewed on it for a bit (never comment on an empty stomach). I still would have concerns about non-transactional side-effects of services, like sending a push notification, or an email, or talking to a message bus, etc.

        • http://alexeyzimarev.blogspot.com Alexey Zimarev

          We publish regular events from outside of the domain classes. We do it from our domain service classes and use it only for cross-domain and intra-domain communication, when domain events scope is purely within the domain itself. This solves the issue of transactions since domain events are always scoped within a transaction but bus events are only published to the outside world if the transaction was committed. By doing this we can be sure that other domains and systems do not react on something that in fact has rolled back and does not exist anymore.

  • Vlad Kopachinsky

    Record Payment is simple (2 parameters) method. How about more complexly event with many variable ( like this new NewNotification() { NotificationId = noti.Id, Message = “{0} – {1}”.Format(Repositiory.GetById(UserId) ,noti.Messag), Company = Repository.GetById(CompanyId) ).

    I think more agile push/raise event from code Command/Query because entity (domain) not have all infrastructure ( Repository, Service Locator and etc).

    • jbogard

      Ideally the event is small, and the recipient of the event can get whatever extra information it needs. For example, I typically have some contextual interface about who is performing the action that any service can take as a dependency.

      • Vlad Kopachinsky

        | any service can take as a dependency.

        Then i should create IBalanceCalculator instead Query CalculateBalanceQuery ?

        My version (code from RecordPaymentCommand )

        new Payment() { Balance = Dispatcher.Query(new CalculateBalanceQuery() { Amount = PaymetAmount }) }

        What feature ?

        1. Less than abstraction

        2. Reuse Query/Command

        3. Encapsulate all action in Command/Query ( don’t invent IBankCalculate, ISearchBuilder, IFacebookSdk and etc )

        Event not one to one with entity. I mean complexly event can’t publish/raise from entity because need many dependency.

        P.S. Sorry for my English )

        • jbogard

          You might also have a good candidate here for a service, where an operation spans multiple aggregates. This is slightly different than a domain event, where one aggregate needs to be notified of changes from a different one.

          • Vlad Kopachinsky

            About your scenario with event on domain entity.

            IBalanceCalculator can access to data base ? –

            1.If YES then problem with Unit of work ( forward (across) current context )

            2.If NO then boundary use very very limited ( sample for calculated balance need get average last 5 transaction by company or something … )

            We was used similar (Events on entity) solution like your but really is not agile and we follow priority to segregate Command/Query (like CQRS but more details).

            Of course i understand your idea about follow to domain model but really is hard to reuse because entity very dependency by context (mapping and etc).

            Command/Query can reuse ( inner command , composite push, delay/async push ) because have parameters.
            new RecordPaymentCommand () { IsMuteEvent = false }

            Also rich logic entity can be performance trouble because


            Order.Items.Where(r=>r.Active) != Repository.Query().Where(r=>r.Order.Id = orderId && r.Active)

  • Jay

    Nice post Jimmy. What if the event handler(s) that got called by the dispatcher modifies an entity that raises/adds domain events which then dispatches to handlers that modifies entities that raises/adds domain events etc… Is this ok?

    • Pawel Pabich

      I can imagine this is possible and can be handled by looping until there are no more entities with events.

      • jbogard

        Yep! I try to avoid this scenario, you can confuse yourself quickly. But it would be nice not to accidentally mess yourself up.

  • svenschelfaut

    What if you are saving a new entity which has its id being generated in the database and this new id must be known by the event handlers?
    The auto generated id is only filled in by EF when calling SaveChanges.
    Should you call base.SaveChanges() first then before dispatching the events?

    • jbogard

      Probably, yes. Sometimes my events contain the entity itself, rather than just an ID.

  • RyanVice

    This is great Jimmy. On my last projects when we implemented Domain Events one of the better developers immediately flagged us on not having transaction support.

    The static part of these patterns always gives me pause but I like the pattern a lot and we got a lot of value out of it on my last project.

  • Sean S.

    Why would you want to raise the events just before changes get saved to the database? If the save fails, then aren’t your events invalid?

    • jbogard

      It’s all in the same transaction scope (I create explicit contextual transactions scopes no matter the context) so it would all get rolled back anyway.

      • Sean S.

        That makes sense. I seem to have a difficult time finding good blog posts on implementing transaction infrastructure components. I liked your blog post “Dealing with Transactions”, but I just don’t understand how to implement transactions when the actual resource is not transactional (e.g. Mongo, SMTP, etc.). Any good reading material or examples you could direct me to?

  • Paul Cox

    Do you have any advice when you want the events to be consumed by multiple consumers in separate transactional contexts?

    Currently I am raising the events similar to the above (although using thread local storage and the ambient context pattern), and then sending them to a MassTransit endpoint that is processed by a back-end service process. I then have a consumer that raises further command messages for each action that I want to occur. For instance, an OrderCreatedEvent might require a consumer to send a confirmation email, another to calculate expected delivery dates, another to invoice etc.

    This results in a lot of plumbing code when I want to add a new consumer of an event and requires changing exisiting code rather than just adding a new consumer.

  • Job

    What would the code for the Events class look like? A collection of static methods?

    • Sean

      There isn’t an “Events” class in his example. He’s adding a list of IDomainEvents (usually just a marker interface on your event classes) to each entity and using that to collect events to dispatch later.

  • jphamilton

    Am I missing something here? Wouldn’t you want to SaveChanges first to complete the transaction and then raise the events?

    • jbogard

      No, you wouldn’t, since then you’d have to worry about what happens when the event’s transactions fail.

      • Daniel Vásquez

        But events are already done things. If something fails, I should worry about corrective actions, not rolling back everything.

        • jbogard

          Yes, events are already done things. But *dispatched, handled* events are a separate story.

  • HEskandari

    We use pretty much the same approach. We learned the hard way that using Identity is not a good approach, as you need another round trip to the database to fetch that in order to pass that down to the event / event handler and we almost strictly do not pass the whole entity through the events to other domain services as they usually require having another entity / relation / aggregate root to be populate which would break the aggregate root boundaries.

  • cbp

    This is a nice implementation but I think you do need a clean mechanism to control the timing of event execution. There are three logical places: immediately, just before transaction commit or just after successful transaction commit.

    For example, if you are sending a notification then that should execute after the transaction successfully commits.
    If you are updating another aggregate then you may need the event to execute immediately.
    The need to execute the event ‘just before’ transaction commit is rarer actually.

    • Sean

      I’m thinking through this exact situation myself. I don’t think the event or event handler determines how it interacts with the transaction. I think the resources used by the event handlers make the decision to be transaction aware or not. In the case of notifications, the resource for sending notification is transaction aware and knows it should send after the transaction completes.

      • Magnus

        Transactions handling are done in Application Layer and have that “Use Case” granularity like “AddCustomerOrder” type of scope. So if you for example have a TransacationDecorator around you CommandHandler in your service layer for that particular command AddCustomerOrder, your transaction should scope everything that persist. However when you raises event from Domain entity methods that are reused of several command handlers from different clients (mobile site, mvc site, background batches). It will be difficult to make command handlers take desicions on how to dispatch events.

        We are right now in a crossroad to choose from three different approaches regarding events.

        1) Make a EventDispactherDecorator around our command handlers . After each Command is handle we go through events. This only give us option to post process events after saveChanges but within a TransactionScope.

        2) Make a CommandBus (Service Locator pattern) and Validate, Handle every command within a CommandBus and at last dispatch queued events. This is not that good from a unit test perspective but very clean and easy to implement.

        3) Jimmys approach which gives a even cleaner Domain that do not need reference to CommonServiceLocator through DomainEvents static class. One draw back here is when you actually want to raise event without persist. This can be an option when you want to make push a notice or email without saving this particular entity just because you want that trigger or calculation (ex) within domain capsulated with a method. Then when you do this from another client (background batch) you get the same behavior (same event handler is triggered by same event.

        But all three share your problem above that How to control when events should be handled.

  • gilligan_MH

    Not to be a stickler here, but wasn’t one of the reasons for a static internal component the TEll Don’t Ask Principle? I know the principle is not always a hard and fast rule but still.

    • jbogard

      With this approach, my domain model is now fully encapsulated. Also, what I’m doing here is separating raising the event from dispatching. The entity never actually intended to *dispatch* synchronously – after all, it might not be done doing its own internal work.

  • http://joebuschmann.com Joseph Buschmann

    How about replacing the static DomainEvents class with a concrete type, perhaps a proxy that wraps the static class, and pass that into the Fee class as a dependency? Then in your unit test you can provide a mock for the proxy and record/validate the events raised.

    • jbogard

      That’s confusing as the entity’s constructor should be to enforce invariants, not receive dependencies. Entities don’t have dependencies, they’re completely self-sufficient.

      • http://joebuschmann.com Joseph Buschmann

        Got it. And I also missed the piece about wanting to separate raising events from dispatching them. Good article.

      • http://joebuschmann.com Joseph Buschmann

        One more thing. Have you written any articles covering entities and dependencies?

  • cntrlks

    Now you have to assume that any repository implementation properly handles dispatching the events. I think I would be more in favor of simply using some kind of EventContext *outside* of the entities and repositories that can use code generation to subscribe to plain old events and then dispatch them when the context is committed.

    • cntrlks

      You know what… You could have less effort and zero dependencies by just using static events and optionally subscribe to the completion of an ambient transaction in your handlers.

    • tuespetre

      So far, every method of domain events looks ugly to me besides straight up CLR events. Either:

      - The entity has to depend on a static class (that may not in itself be relevant to the domain)

      - The entity has to track its own events and rely on something else to broadcast them

      - The consumer of the entities has to consciously register the entity with some kind of event bus (the solution I posited in my original comment, which I have implemented for concept and will share soon for laughs and great fun)

      I have finally decided on implementing both static and instance methods on my domain entities for a variety of consumers to respond to domain events. Instance-sensitive consumers such as repositories can use the events for change tracking while instance-insensitive consumers such as mailers and query store writers can subscribe to the static events at application startup. Any handler can be transaction-sensitive if it wants to by checking System.Transactions.Transaction.Current and optionally subscribing to the TransactionCompleted event.

      • Sean

        I’m doing similar things with the TransactionComplete event, but I can’t decide if the event handler should interact with transactions or if the resources it uses should do that. What do you think?

        • tuespetre

          I think the handler is responsible for deciding whether to actually handle the event in any particular way.

  • Mike

    So how does the call to SaveChanges() get triggered?

    • jbogard

      Usually via an action filter or HTTP module.

  • Capetonis

    How would the override SaveChanges example in NHibernate, please.

    • jbogard

      In this case I’d just do this before I committed the transaction.

  • http://scrambledbrains.net/ Mike McG

    Interesting, thanks for posting. Can you shed light on why you’d introduce a whole new system for decoupling (DomainEvents) rather than rely on C#’s idiomatic `event` keyword? I do coarse-grained event sourcing (more like transaction sourcing) in Blocvox.com (along with https://github.com/blocvox/graymatter) and was able to use `event` just fine to keep the domain very clean, clear, and simple. I understand there are a few quirks with their invocation (e.g. exception handling) but `event` is pervasive and well-understood and therefore keeps the cognitive load of the codebase much lower.

    • jbogard

      Two reasons – it’s not easy to wire up these event handlers to entities – you have to handle removing handlers as well. The other is with the recording method, I’m making it absolutely explicit that these events aren’t getting dispatched – merely recorded. Exposing events via .NET events or the original domain event class are functionally equivalent, IMO. I like the idea of event handlers, but that works best for stateful apps, I’ve found.

  • Bjorn Vercruyssen

    Would you use domain events to coordinate a workflow, for example UserWasSaved triggers a CheckAccessConfiguration raises an AccessConfigurationChanged event which triggers an PushAccessConfiguration. Or are domain events not ment for this?

    • jbogard

      Hmmm, probably not. Saved is not a domain concept, but Created is. But I might want to know “which entity” in that case I might need to know the ID of the entity, which can only be known after saved.

  • http://six.dev-heaven.net Sickboy

    I love it, thanks so much again Jimmy!
    I’ve been grinding on this problem for a while as well, especially considering the per-request container setups and dislike of static state/services…
    This seems to be the most elegant solution by far!

    • jbogard

      No problem, glad to have shared!

  • http://six.dev-heaven.net Sickboy

    Here’s one extension to this event handler to deal with events that need to be postponed until after the transaction is complete.

    I am using this for SignalR events, which are not part of database transactions in my apps.

    I am not 100% happy with the approach, but it gets the job done.

    Any thoughts?

    https://gist.github.com/sickboy/d873b8f05b99415090fc

    • http://six.dev-heaven.net Sickboy

      In the meantime I actually removed the explicit NotificationHandler class, and instead implemented the notification handler directly in the ChatHubMessenger class, but that is besides the point of the demonstration :)

      I am still mostly wondering if there wouldn’t be a more elegant way to include the logic inside the transaction, even without actually persisting it to the DB.
      (At least to me it would seem weird to persist a SignalR call to the DB to then just read it again and post it, although it might make sense in more-tier applications.. at the same time I am already using Azure ServiceBus to distribute the messages across multiple frontends)

    • NathanGonzalez

      this is kind of a bad example, and i’d hesitate to use this exact code, but doing something like this to specific abstractions, like your signalr publisher could work well. it doesn’t allow for roll-backs, but it does actually subscribe to a transaction and only invoke your actions on commit.

      https://gist.github.com/ncgonz/0ccab60a152369c2047c

  • Pingback: End of Month Research Roundup – May 2014 | endjin blog

  • Dale Busuttil

    Wonderful article Jimmy. I only have one slight problem with incorporating this change. My dispatch call need the contrete type (Dispatch(TEvent e) where TEvent : IDomainEvent). When I call it in the unit of work it loses its type and the handlers are not retrieve.

    • jbogard

      You’ll have to do some tricks to use the runtime type to load the correct handler.

      • Dale Busuttil

        would you suggest doing this in the unit of work or in the dispatch method?

      • Dale Busuttil

        also isn’t there a substantial performance penalty involved?

        • jbogard

          Substantial compared to what? In any case that’s easy to optimize, no?

    • http://six.dev-heaven.net Sickboy

      The quick way is to cast it to dynamic when calling the Dispatch method.
      Other ways of doing it is by using reflection, which is generally said to be slower as dynamic has some caching under the hood.

      • Dale Busuttil

        yep (dynamic) worked. Thanks :) But it feels like a workaround and not good practice even though the dm is fully encapsulated now.

        • http://six.dev-heaven.net Sickboy

          NP. I ran into exactly the same issue when calling to the Notify method of ShortBus.

          IMO it’s a workaround for a language limitation; there are a few options, and imo the dynamic one is one of the cleaner and perhaps better performing ones.
          Also while dynamic in many cases does not protect you against mismatches during compiling, that fact does not really matter in this instance imo.

          Double dispatch in this case has my second preference as there is no actual casting or reflection. But it does shape your notification objects.

          I wonder what other options and their trade-offs we have…

          • Dale Busuttil

            Thanks so much for the detailed explanation. I will leave the dynamic casting solution as it is the simplest, cleanest and performs the best.

          • Alexander Bautista Pinillos

            Hi, i´m having the same problem but i don’t know how to cast to dynamic. This is how my code looks like:

            public static void Raise(T eventToRaise) where T : IDomainEvent

            {
            var registeredHandlers = ServiceLocator.Current.GetAllInstances<IEventHandler>();

            foreach (var handler in registeredHandlers)
            {
            handler.Handle(eventToRaise);
            }

          • Cody Geest

            You should place dynamic on the call to raise itself
            DomainEvents.Raise((dynamic) event)

  • Pingback: Domain Events and non-transactional behavior | Gediminas Geigalas' blog

  • Daskul

    is better or atleast possible to put the publishing of events on UnitOfWork which wraps the DbContext? I am trying to put it inside UnitOfWork but I am injecting the Context by passing IDbContext which doesnt have the ChangeTracker property.