Loading...
Skip to Content

Blog

Post: How to publish and handle Domain Events

How to publish and handle Domain Events

2019-06-19 UPDATE: Please check Handling Domain Events: Missing Part post which is a continuation of this article.

Introduction

Domain Event is one of the building blocks of Domain Driven Design. It is something that happened in particular domain and it captures memory of it. We create Domain Events to notify other parts of the same domain that something interesting happened and these other parts potentially can react to.

Domain Event is usually immutable data-container class named in the past tense. For example:

// Simple Domain Event
public class OrderPlaced
{
    public Order Order { get; }

    public OrderPlaced(Order order)
    {
        this.Order = order;
    }
}

Three ways of publishing domain events

I have seen mainly three ways of publishing domain events.

1. Using static DomainEvents class

This approach was presented by Udi Dahan in his Domain Events Salvation post. In short, there is a static class named DomainEvents with method Raise and it is invoked immediately when something interesting during aggregate method processing occurred. Word immediately is worth emphasizing because all domain event handlers start processing immediately too (even aggregate method did not finish processing).

// Raising event using static method
public class Shop
{
    public void PlaceOrder(Order order)
    {
        // business logic....

        DomainEvents.Raise(new OrderPlaced(order));
    }
}

2. Raise event returned from aggregate method

This is approach when aggregate method returns Domain Event directly to ApplicationService. ApplicationService decides when and how to raise event. You can become familiar with this way of raising events reading Jan Kronquist Don’t publish Domain Events, return them! post.

// Aggregate method - returning events
public List<IDomainEvent> PlaceOrder(Order order)
{
    // business logic....

    List<IDomainEvent> events = new List<IDomainEvent>();
    events.Add(new OrderPlaced(order));

    return events;
}
public class ShopApplicationService
{
    private readonly IOrderRepository orderRepository;
    private readonly IShopRepository shopRepository;
    private readonly IEventsPublisher eventsPublisher;
        
    public void PlaceOrder(int shopId, int orderId)
    {
        Shop shop = this.shopRepository.GetById(shopId);
        Order order = this.orderRepository.GetById(orderId);

        List<IDomainEvent> events = shop.PlaceOrder(order);

        eventsPublisher.Publish(events);
    }
}

3. Add event to Events Entity collection

In this way on every entity, which creates domain events, exists Events collection. Every Domain Event instance is added to this collection during aggregate method execution. After execution, ApplicationService (or other component) reads all Events collections from all entities and publishes them. This approach is well described in Jimmy Bogard post A better domain events pattern.

// Add events to Events collection
public abstract class EntityBase
{
    ICollection<IDomainEvent> Events { get; }
}

public class Shop : EntityBase
{
    public List<IDomainEvent> PlaceOrder(Order order)
    {
        // business logic....

        Events.Add(new OrderPlaced(order));
    }
}
// Application service (publishing events from collection)
public class ShopApplicationService
{
    private readonly IOrderRepository orderRepository;
    private readonly IShopRepository shopRepository;
    private readonly IEventsPublisher eventsPublisher;
        
    public void PlaceOrder(int shopId, int orderId)
    {
        Shop shop = this.shopRepository.GetById(shopId);
        Order order = this.orderRepository.GetById(orderId);

        shop.PlaceOrder(order);

        eventsPublisher.Publish();
    }
}
// Publishing events from collections
public class EventsPublisher : IEventsPublisher
{
    public void Publish()
    {
        List<IDomainEvent> events = this.GetEvents(); // for example from EF DbContext

        foreach (IDomainEvent @event in events)
        {
            this.Publish(@event);
        }
    }
}

Handling domain events

The way of handling of domain events depends indirectly on publishing method. If you use DomainEvents static class, you have to handle event immediately. In other two cases you control when events are published as well handlers execution - in or outside existing transaction.

In my opinion it is good approach to always handle domain events in existing transaction and treat aggregate method execution and handlers processing as atomic operation. This is good because if you have a lot of events and handlers you do not have to think about initializing connections, transactions and what should be treat in “all-or-nothing” way and what not.

Sometimes, however, it is necessary to communicate with 3rd party service (for example e-mail or web service) based on Domain Event. As we know, communication with 3rd party services is not usually transactional so we need some additional generic mechanism to handle these types of scenarios. So I created Domain Events Notifications.

Domain Events Notifications

There is no such thing as domain events notifications in DDD terms. I gave that name because I think it fits best - it is notification that domain event was published.

Mechanism is pretty simple. If I want to inform my application that domain event was published I create notification class for it and as many handlers for this notification as I want. I always publish my notifications after transaction is committed. The complete process looks like this:

  1. Create database transaction.
  2. Get aggregate(s).
  3. Invoke aggregate method.
  4. Add domain events to Events collections.
  5. Publish domain events and handle them.
  6. Save changes to DB and commit transaction.
  7. Publish domain events notifications and handle them.

How do I know that particular domain event was published?

First of all, I have to define notification for domain event using generics:

// Domain event notification specification
public interface IDomainEventNotification<out TEventType> where TEventType:IDomainEvent
{
    TEventType DomainEvent { get; }
}

public class DomainNotificationBase<T> : IDomainEventNotification<T> where T:IDomainEvent
{
    public T DomainEvent { get; }

    public DomainNotificationBase(T domainEvent)
    {
        this.DomainEvent = domainEvent;
    }
}

public class OrderPlacedNotification : DomainNotificationBase<OrderPlaced>
{
    public OrderPlacedNotification(OrderPlaced orderPlaced) : base(domainEvent)
    {
    }
}

All notifications are registered in IoC container:

// Register domain events notifiacations in IoC container
protected override void Load(ContainerBuilder builder)
{
    builder.RegisterAssemblyTypes(typeof(IDomainEvent).GetTypeInfo().Assembly)
    .AsClosedTypesOf(typeof(IDomainEventNotification<>));
}

In EventsPublisher we resolve defined notifications using IoC container and after our unit of work is completed, all notifications are published:

// Resolve defined notifications and publish them
var domainEventNotifications = new List<IDomainEventNotification<IDomainEvent>>();
foreach (var domainEvent in domainEvents)
{
    Type domainEvenNotificationType = typeof(IDomainEventNotification<>);
    var domainNotificationWithGenericType = domainEvenNotificationType.MakeGenericType(domainEvent.GetType());
    var domainNotification = _scope.ResolveOptional(domainNotificationWithGenericType, new List<Parameter>
    {
        new NamedParameter("domainEvent", domainEvent)
    });

    if (domainNotification != null)
    {
        domainEventNotifications.Add(domainNotification as IDomainEventNotification<IDomainEvent>);
    }             
}

var tasks = domainEventNotifications
    .Select(async (notification) =>
    {
        await _mediator.Publish(notification, cancellationToken);
    });

await Task.WhenAll(tasks);

This is how whole process looks like presented on UML sequence diagram:

You can think that there is a lot of things to remember and you are right!:) But as you can see whole process is pretty straightforward and we can simplify this solution using IoC interceptors which I will try to describe in another post.

Summary

  1. Domain event is information about something which happened in the past in modeled domain and it is important part of DDD approach.
  2. There are many ways of publishing and handling domain events - by static class, returning them, exposing by collections.
  3. Domain events should be handled within existing transaction (my recommendation).
  4. For non-trasactional operations Domain Events Notifications were introduced.

Image credits: Racool_studio on Freepik.

Comments

Related posts See all blog posts

Handling Domain Events: Missing Part
18 June 2019
Some time ago I wrote post about publishing and handling domain events. In addition, in one of the posts I described the Outbox Pattern, which provides us At-Least-Once delivery when integrating with external components / services without using the 2PC protocol. This time I wanted to present a combination of both approaches to complete previous posts. I will present a complete solution that enables reliable data processing in the system in a structured manner taking into account the transaction boundary.
Read More
Modular Monolith: Integration Styles
26 July 2020
In this post, I would just like to discuss the missing part – Integration Styles for modules in Modular Monolith architecture.
Read More
The Outbox Pattern
11 March 2019
Sometimes, when processing a business operation, you need to communicate with an external component in the Fire-and-forget mode. The question that arises is whether we are able to guarantee the atomicity of our business operation from a technical point of view?
Read More