Strengthening your domain: Domain Events

Previous posts in this series:

In the last post, we rounded out fully encapsulated domain models by encapsulating certain state information that requires certain restrictions in how that information can change.  In our model of customers, fees and payments, we started recording fee balance on the Fee object.  Once we did this, we started to lock down the public interface of our Fee and Payment objects, so that only operations supported and validated by our domain experts can be executed.  The side benefit of this sort of design is that our domain model now strongly reinforces the ubiquitous language.

In an anemic domain model, the ubiquitous language only reflects state information.  In our conversations with the domain experts, we are very likely to hear behavioral information as well.  We can talk about our model in a more cogent manner now that we have methods like “Customer.ChargeFee” and “Fee.RecordPayment”.  Merely having public setters for state information loses the context of why that information changed, and in what context it is valid for that information to change.

As we move towards a fully encapsulated domain model, we may start to encounter a very critical word in our conversations with the domain experts: When.  One of the features requested for our Customer object is:

We need to know if a customer has an outstanding balance on more than 3 fees. If a Customer has more than 3 fees with an outstanding balance, we flag them as AtRisk.  When a customer pays off a fee and they no longer have more than 3 fees outstanding, they are no longer at risk.

Note that final sentence and the implied interaction.  When a fee as been paid off, the customer needs to be notified that to update their AtRisk flag.  Whenever we hear the word “When”, that should be a signal to us that there is a potential event in our system.  We would like to model this event explicitly in our system and to have our model reflect this kind of relationship between Fee and Customer.  In our case, we’re going to use a design from Udi Dahan, Domain Events.

Introducing Domain Events

So what are domain events?  Domain events are similar to messaging-style eventing, with one key difference.  With true messaging and a service bus, a message is fired and handled to asynchronously.  With domain events, the response is synchronous.  Using domain events is rather straight forward.  In our system, we just need to know when a Fee has been paid off.  Luckily, our design up to this point has already captured the explicit operation in the Fee object, the RecordPayment method. In this method, we’ll raise an event if the Balance is zero:

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;
}

After the Balance has been updated, we raise a domain event using the DomainEvents static class.  The name of the event is very explicit and comes from the ubiquitous language, “FeePaidOff”.  We include the Fee that was paid off as part of the event message:

public class FeePaidOff : IDomainEvent
{
    public FeePaidOff(Fee fee)
    {
        Fee = fee;
    }

    public Fee Fee { get; private set; }
}

When it comes to testing the events, we have two kinds of tests we want to write.  First, we want to write a test that ensures that our Fee raised the correct event:

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

    DomainEvents.Register<FeePaidOff>(e => paidOffFee = e.Fee);

    var customer = new Customer();

    var fee = customer.ChargeFee(100m);

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

    paidOffFee.ShouldEqual(fee);
}

We’re not checking the Customer “IsAtRisk” flag in this case, as we’re only testing the Fee object in isolation.  In an integration test, we’ll have our IoC container in the mix, and we’ll test the handlers as part of the complete operation.  Some might argue that the complete model now includes the container, and we want to test the complete operation, events and all in our unit test.  I can’t really argue against that, as you might define “unit” to now include the entire domain model, not just one entity.

Handling the event

To handle the event, we want to update the Customer’s AtRisk status for the Customer charged for the paid-off Fee.  Our handler then becomes:

public class FeePaidOffHandler : IHandler<FeePaidOff>
{
    private readonly ICustomerRepository _customerRepository;

    public FeePaidOffHandler(ICustomerRepository customerRepository)
    {
        _customerRepository = customerRepository;
    }

    public void Handle(FeePaidOff args)
    {
        var fee = args.Fee;

        var customer = _customerRepository.GetCustomerChargedForFee(fee);

        customer.UpdateAtRiskStatus();
    }
}

Because our handlers are pulled from the container, we can use normal dependency injection to process the event.  If we were going the transaction script route, we would likely see this updating in some sort of application service.  With our event handler, we ensure that our model stays consistent.  The UpdateAtRiskStatus on Customer now can apply our original business logic:

public bool IsAtRisk { get; private set; }

public void UpdateAtRiskStatus()
{
    var totalWithOutstandingBalance = Fees.Count(fee => fee.HasOutstandingBalance());

    IsAtRisk = totalWithOutstandingBalance > 3;
}

With our domain event in place, we can ensure that our entire domain model stays consistent with the business rules applied, even when we need to notify other aggregate roots in our system when something happens.  We’ve also locked down all the ways the risk status could change (charged a new fee), so we can keep our Customer aggregate consistent even in the face of changes in a separate aggregate (Fee).

This pattern isn’t always applicable.  If I need to do something like send an email, notify a web service or any other potentially blocking tasks, I should revert back to normal asynchronous messaging.  But for synchronous messaging across disconnected aggregates, domain events are a great way to ensure aggregate root consistency across the entire model.  The alternative would be transaction script design, where consistency is enforced not by the domain model but by some other (non-intuitive) layer.

Strengthening your domain: conclusion

Creating consistent aggregate root boundaries with a fully encapsulated domain model isn’t that hard, if you know what code smells to look for.  You can run into issues with less-mature ORMs that do not truly support a POCO domain model.  Having a fully encapsulated domain model represents the entirety of the ubiquitous language: state AND behavior.  Anemic domain models only tell half the picture in representing only the state.

Fully encapsulated domain models ensure that my domain model remains self-consistent, and that all invariants are satisfied at the completion of an operation.  Anemic domain models do not enforce consistency rules, and rely on reams of services to keep the model valid.

For many, many applications, a domain model is more trouble than it is worth, as the behavior of these services can be quite simple.  However, much of DDD is just plain good OOP, and a well-crafted domain model can be realized simply by paying attention to code smells and refactoring.

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. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://shane.jscconsulting.ca Shane Courtrille

    Thanks for the series.. has provided a lot of great ideas and I can already see where a lot of our domain problems can be improved upon now.

  • http://scottwhite.blogspot.com Scott White

    Great series, one question though.

    Some of the things you have mentioned (such as avoiding setters) won’t work with NHibernate. If you are using NHibernate, how have you gotten around having setters? Using field level access?

    There is a performance cost for using field level access, the reflection for this is quite a bit slower. I tend to use protected setters.

    Good article

  • Arnis L

    Jimmy, what are Your thoughts about CQRS?

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @Scott

    Everything you’ve seen here has come from a very large app done 100% with NHibernate. I haven’t heard the reflection penalty, as I thought NHib used fast reflection techniques like Reflection.Emit. Anyway, I still use things like private setters, and NHibernate works with them just fine. The only time I use fields are for collections.

    @Arnis

    It’s a very intriguing concept, and I’d love to try it out on a real project. I can’t really talk about benefits/limitations until I do though.

  • Ryan Roberts

    Would it be a wise design to have the aggregate implement IHandler, rather than calling methods explicitly? The main issue I can think of with that is per method service dependencies.

  • Pritchard

    Love the series, great clarifications for me, such as the difference between doman/synchronous and asynchronous events.

    There’s a smell in our app and I’m not sure how to fix it and I’d really appreciate your opinion. We have a (non-intuitive) layer named “MoveService” that interacts with the repositories and internals of our domain in order to enforce invariants that we can’t find a home for.

    We have Projects, Phases and Tasks. Tasks must belong to either a phase or project so that “membership” is managed by our task aggregate. Tasks can be moved to another phase or to another project and they must be sequentially ordered among other tasks in the same project or phase. In that sense, Project and Phase are merely “containers.”

    We are having trouble managing the sequencing, especially when combined with moving because all the tasks in the destination and source need to be reordered when a move occurs. We’ve tried creating a TaskSequenceList aggregate that enforces the tasks sequencing but there was awkwardness in the fact that it did not manage list “membership” in and of itself so moving was weird. We sense that Events may help, but not sure how.

    Thanks so much!

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @Ryan

    Remember, event handlers are loaded up via the container, so I can’t really pull out a specific aggregate root. If you mean something else, can you post a snippet to clarify?

    @Pritchard

    Can you shoot me an email from the Contact Me form? This one would be tough to answer in a blog post comment.

  • Peter

    This is a great series, Jimmy. One question: how are you handling transaction management in your handlers?

  • Ryan Roberts

    We have an IAggregateRoot { Guid id {get;}} convention. Messages that route to aggregates implement IForAggregate.

    So we could then do something along the lines of this in a single handler for aggregate messages:

    void HandleAggregateMessage(IForAggregate msg)
    {
    InvokeHandleOn(RepositoryFor
    ().ById(msg.Id),msg)
    }

    Our problem also requires that many messages will need to persist, to be stored as part of the aggregate for future execution. So this makes translating them into method calls in a handler inappropriate for us.

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @Peter

    Transactions are handled in our unit of work wrapping each HTTP request. Since our domain events are synchronous and on the same thread, they are part of the same transaction as the entity that first raised the event.

    @Ryan

    That’s a pretty interesting idea. We thought of doing something like that, having aggregate roots themselves responding to events, but couldn’t quite get the implementation working right. I think I’ll revisit it however, thanks!

  • http://geekswithblogs.net/wesm Wes

    Great post, domain events are a great way to decouple objects and produce a more flexible application!

    We took the DomainEvent idea to the next level and abstracted it away in a simple Dispatcher class. Using a singleton call we can fire and forget from within our domain objects. We still work with an interface for the dispatcher. We start with a “local” dispatcher which resolves handlers from our container and synchronously executes each. Then, we extended the local dispatcher with a “global” dispatcher which looks for event payloads of type IMessage (from NServiceBus). These get routed via our bus to other remote processes, albeit at this point asynchronously. These incoming events are dispatched in the remote processes via the same local dispatcher, where they are resolved in the remote process’s container. This makes a nice, transparent mechanism to move events with minimal effort on the event source and listeners, whether “local” or “remote”. Using a simple DSL we can register which events get routed globally with minimal effort.

    Add a splash of convention over configuration to register these into our container and the whole process is amazingly simple to use.

  • ryzam

    Just a simple question, why we need to use domain events as we can also do the same thing using a normal way?

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @ryzam

    Could you elaborate on what the normal way would look like?

  • ryzam

    What I meant to say was 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)
    var po = new PaidOffHandler()
    po.Handle(new FeePaidOff{ Fee = ff});

    return payment;
    }

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @ryzam

    While that’s certainly possible, you’re now coupling yourself to implementations of handlers. With an event, I don’t need to know who, or how many folks care about this event.

  • http://scottwhite.blogspot.com Scott White

    Again great articles. Was much of this inspired by NullObject pattern?

  • ryzam

    Thanks Jimmy

    If I’m not mistaken, there was a discussion in DDD group either entity can have reference to repository/service or not. Some of them reject this approach even though if we are using Dependency Injection to supply the instantiate object.

    If we look into this Domain Events implementation the concept is almost similar where your entity some how will also have to use external component to do a job.

    But, personally I agree with Domain Events solution, keep up ..

    Another question, does every command we send to entity need to have DomainEvent?

    Example: If we have command RegisterLicenseCustomer, do we also have LicenseCustomerRegistered as DomainEvents?

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @Scott White

    No, not really. What would be the connection?

    @ryzam

    In my experience, events only arise when someone cares about something that happened. A command may generate no events, or several, it just really depends on those “When” requirements.

  • http://scottwhite.blogspot.com Scott White

    Well for one, avoiding setters can eliminate the possibility of null value properties.

    I use protected setters for lists and objects and I do something like this:

    private AuditDate _auditDate = new AuditDate();

    protected set
    {
    if (value == null) throw new ArgumentNullException(“value”);
    _auditDate = value;
    }

    Eliminates the need to do any null checking, reduces likelyhood of bugs. In the declaration where I’m instantiating the AuditDate, if the setter was public allowing the caller to set a new object then I would have set the initial value to be a NullAuditDate using the null object pattern.

    To me this is the next logical step after protecting your setters. The objects using the NullObject pattern will always fail persistence since the ORM doesn’t track an object of that type.

  • Gary Brunton

    Thanks for the excellent series! I know I’m very late to this discussion but thought I’d ask anyway.

    I’m wondering how you manage the shared state (the actions) within the static DomainEvents class for asp .net web applications? Udi places the [ThreadStatic] attribute on the actions but this is not a good idea when in the context of an asp .net web application. I’m thinking of using Structure Map’s ObjectFactory to get the DomainEvents class and have that class wired up with a Hybrid lifecycle.

    How do you handle this issue?

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @Gary

    Funny you should ask this, as we’re looking at the exact same issues. Static gateways just don’t play as nicely with IoC, so we’re also examining our options. Right now, domain event handlers are instantiated by the container, so we do have SOME control over lifecycle.

    However, we can’t do things like nested containers.

  • Gary Brunton

    This might be lame but I think I like it….

    public static class DomainEvents
    {
    public static void Register(Action callback) where T : IDomainEvent
    {
    ObjectFactory.GetInstance().Register(callback);
    }

    public static void ClearCallbacks()
    {
    ObjectFactory.GetInstance().ClearCallbacks();
    }

    public static void Raise(T domainEvent) where T : IDomainEvent
    {
    ObjectFactory.GetInstance().Raise(domainEvent);
    }
    }

    Where the DomainEventsController (for lack of a better word) is the DomainEvents implementation and can be wired up as having a Hybrid lifecyle. I was also thinking that using ObjectFactory.TryGetInstance could be used instead of GetInstance. This could give us more flexibility within our tests.

    You got me thinking about nested containers now. I’ve been using the Hybrid lifecycle. Wonder if I’m doing that wrong?

    Any ideas?

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @Gary

    Looks cool! Yeah, the nested container is just a way to provide scoped lifecycle of things. Great for context objects, and can be used instead of Hybrid stuff in some places.

  • Gary Brunton

    Just came across another way to define the DomainEvents static class that I like more:

    public static class DomainEvents
    {
    private static IContainer container;

    public static void Initialize(IContainer newContainer)
    {
    if (newContainer == null) throw new ArgumentNullException(“newContainer”);
    container = newContainer;
    }

    public static void Register(Action callback) where T : IDomainEvent
    {
    container.GetInstance().Register(callback);
    }

    public static void ClearCallbacks()
    {
    container.GetInstance().ClearCallbacks();
    }

    public static void Raise(T domainEvent) where T : IDomainEvent
    {
    container.GetInstance().Raise(domainEvent);
    }
    }

    I got the idea from here:
    http://jasondentler.com/blog/2009/11/simple-domain-events/

    I like it better because I don’t have to use the static ObjectFactory class explicitly. Any thoughts on this?