Domain Events With Convention-Based Registration And Deferred Execution Support

In DDD, we establish bounded contexts and define how they will exchange information, which often occurs by raising events. According to Eric Evans, you should :

“Describe the points of contact between the models, outlining explicit translation for any communication and highlighting any sharing.”

These contexts will often communicate as part of a same database transaction. That’s actually desired, since it will assure integrity between the whole process. However, in the real world things can be tricky. Some actions might depend on committed transactions in order to get triggered. This happens, for instance, to services that run outside of the ongoing database transaction. You don’t want to send an email to notify the user that something changed and have the whole operation rolled back seconds later. Same thing happens when our application consume distributed services that do not support distributed transactions.

What We Want To Achieve

Let’s see our main goals. There are many good ways to implement domain event handlers. This implementation will use a dependency container to control the lifetime of the events raiser, it will register events with the container on a very transparent way and support delayed execution. Let’s review :

Effortless registration

In order to create a new handler for an event, we don’t want to look for a distant class in another project that has hundreds of event-to-handler bindings and add new lines. To that end, we might want to consider to use a dependency container.

Deferred execution support

Handlers that invoke code that are not bound to a transaction should be fired only once the transaction is committed.

Practical Example : Paying an Order on an E-Commerce App

Let’s take as example the payment of an order on an e-commerce application. Once the payment is processed, it raises an event. Then, the bounded context that processes customers orders handles the event and places the order for shipment. Furthermore, the bounded context that manages the inventory handles the same event to subtract from the stock the items that were ordered.

Domain-Events-01.png

Created with Balsamiq Mockups.

Firing The Event

PaymentService is instantiated by the dependency container. An event is raised on the last line of the PayOrder method.

    public class PaymentService : IPaymentService
    {
        private readonly IDomainEventsRaiser _events;
        private readonly IRepository _orderRepository;
        private readonly IRepository _paymentRepository;

        public PaymentService(IRepository paymentRepository, IRepository orderRepository, IDomainEventsRaiser events)
        {
            _paymentRepository = paymentRepository;
            _orderRepository = orderRepository;
            _events = events;
        }

        public void PayOrder(int orderId, decimal amount)
        {
            Order order = _orderRepository.GetById(orderId);

            Payment payment = new Payment()
            {
                OrderId = orderId,
                Amount = amount
            };

            _paymentRepository.Insert(payment);

            _events.Raise(new OrderPaidEvent(payment, order.Items));
        }
    }

IDomainEventsRaiser has the responsibility of raising events and it needs only one method to do so :

    public interface IDomainEventsRaiser
    {
        ///
        /// Raises the given domain event
        /// 

        /// Domain event type
        /// domainEvent">Domain event
        void Raise(T domainEvent) where T : IDomainEvent;
    }

What makes OrderPaidEvent an event is the fact that it implements the IDomainEvent interface. This interface actually does not expose any service. Its only purpose is to serve as a generic constraint to the Raise method of IDomainEventsRaiser to group classes that represent an event.

    public class OrderPaidEvent : IDomainEvent
    {
        public OrderPaidEvent(Payment payment, IEnumerable orderItems)
        {
            Payment = payment;
            OrderItems = new List(orderItems);
        }

        public Payment Payment { get; private set; }

        public List OrderItems { get; set; }
    }

Handling The Event

As we said earlier, we want effortless registration. To that end, we will use some conventions. Every handler will have to implement the interface IHandles, as described below :

    ///<summary>
    /// Handles an event. If there is a database transaction, the
    /// execution is delayed until the transaction is complete.
    /// </summary>

    /// <typeparam name="T"></typeparam>
    public interface IHandles<T> where T : IDomainEvent
    {
        void Handle(T domainEvent);
    }

Once the order is paid, its related items are subtracted from the stock. In other words, once OrderPaidEvent is raised, a SubtractInventaryWhenOrderPaidEventHandler will handle it. The IHandles interface makes the bridge  between a domain event and its handlers.

    public class SubtractInventaryWhenOrderPaidEventHandler : IHandles<OrderPaidEvent>
    {
        private readonly IInventoryService _inventory;

        public SubtractInventaryWhenOrderPaidEventHandler(IInventoryService inventory)
        {
            _inventory = inventory;
        }

        public void Handle(OrderPaidEvent domainEvent)
        {
            foreach (var item in domainEvent.OrderItems)
                _inventory.SubtractAvailability(item.InventoryItemId, item.Quantity);
        }
    }

The handler is also created by the dependency container. That allows dependencies to be passed more easily.

We can have multiple handlers for a same event. That said, once the order is paid, it can be placed for delivery. That means that a PlaceOrderWhenPaidEventHandler will handle this same event (we can also have one single handler that handles multiples events).

    public class PlaceOrderWhenPaidEventHandler : IHandles<OrderPaidEvent>
    {
        private readonly IOrderPlacementService _orderPlacement;

        public PlaceOrderWhenPaidEventHandler(IOrderPlacementService orderPlacement)
        {
            _orderPlacement = orderPlacement;
        }

        public void Handle(OrderPaidEvent domainEvent)
        {
            _orderPlacement.PlaceOrder(domainEvent.Payment.OrderId);
        }
    }

The Domain Events Raiser

Now that we understand how events are fired and handled, let’s see how the events raiser is implemented :

    ///<summary>
    /// Simple domain events raiser that is functional, but doesn't support deferred execution
    /// </summary>

    class DomainEventsRaiser : IDomainEventsRaiser
    {
        ///
        /// Locator of event handlers
        /// 

        private readonly IServiceProvider _locator;

        internal DomainEventsRaiser(IServiceProvider locator)
        {
            _locator = locator;
        }

        ///
        /// Raises the given domain event
        /// 

        /// Domain event type
        ///Domain event
        public void Raise(T domainEvent) where T : IDomainEvent
        {
            //Get all the handlers that handle events of type T
            IHandles[] allHandlers = (IHandles[])_locator.GetService(typeof(IHandles[]));

            if (allHandlers != null && allHandlers.Length > 0)
                foreach (var handler in allHandlers)
                    handler.Handle(domainEvent);
        }
    }

As we can see, a locator finds all the handlers that handles a specific event, then fires each handler sequentially. The domain events raiser is also created by the dependency container. It allows services to have its reference on their constructors.

These same principles could be used for other needs, like CQRS to handle commands.

Convention-Based Registration

You might want to use convention-based registration to services that are a part of a same concept, have similar structure and follow a same pattern of registration and resolving. A more “manual” registration can be more interesting for services that don’t fall on the first rule and require more specific needs.

We will be using Unity on this section, but any decent container should give you similar capabilities.

Container.RegisterTypes(new EventHandlersConvention());

This will make the container register all classes that implements IHandles. That allows the DomainEventsRaiser to use a locator that find the handlers for a specific event.

    ///<summary>
    /// Register the conventions that allows domain events to be raised and handled
    /// </summary>

    class EventHandlersConvention : RegistrationConvention
    {
        public override Func<type, ienumerable> GetFromTypes()
        {
            return WithMappings.FromAllInterfaces;
        }

        public override Func<type, ienumerable> GetInjectionMembers()
        {
            return (t) => new InjectionMember[0];
        }

        public override Func<type, lifetimemanager> GetLifetimeManager()
        {
            return WithLifetime.Transient;
        }

        public override Func<type, string> GetName()
        {
            return WithName.TypeName;
        }

        public override IEnumerable GetTypes()
        {
            Type handlerType = typeof(IHandles<>);

            return AllClasses.FromLoadedAssemblies(skipOnError: false).
                        Where(t => !t.IsAbstract &&
                            t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition().Equals(handlerType)));
        }
    }

Important : The lifetime of DomainEventsRaiser is not transient. It can be singleton, but I suggest scoped to the duration of a full transaction, since the state of this object (the events queue that we will see on the next section) is not reusable between one transaction and another.

Adding Deferred Execution Support

We already have a full working example of domain events that are registered using conventions. Now let’s add a teaspoon of real life.

Let’s say, once the order is paid, an e-mail is sent to the customer to inform that his payment has been processed. This is what our process now looks like :

Domain-Events-02.png

Created with Balsamiq Mockups.

We will be handling e-mail sending using the domain events raiser that we proposed for domain events. However, the e-mail should be sent only after the transaction is committed.

So, in our IHandles interface, we add a Deferred property :

    ///<summary>
    /// Handles an event. If there is a database transaction, the
    /// execution is delayed until the transaction is complete.
    /// </summary>

    /// <typeparam name="T"></typeparam>
    public interface IHandles<T> where T : IDomainEvent
    {
        void Handle(T domainEvent);

        bool Deferred { get; }
    }
One single class can handle many events. <strong>NotificationsHandler</strong> demonstrate how this behavior could be achieved :
    public class NotificationsHandler : IHandles, IHandles //<- multiple events handled by a single handler
    {
        private readonly IUserNotifier _notifier;

        public NotificationsHandler(IUserNotifier notifier) // <-- instantiated by the dependencies container
        {
            _notifier = notifier;
        }

        public bool Deferred { get { return true; } } //<- 'true' here indicates that all these events should be invoked only after the transaction is committed

        public void Handle(OrderPaidEvent domainEvent)
        {
            _notifier.Notify("Yay! Your payment has been processed.");
        }

        public void Handle(OrderShippedEvent domainEvent)
        {
            _notifier.Notify(string.Format("Your order has finally been shipped. Address : \"{0}\"", domainEvent.Order.ShipmentAddress));
        }
    }

DeferredDomainEventsRaiser

And DeferredDomainEventsRaiser replaces the simple DomainEventsRaiser that I mentioned earlier. It adds deferred handlers in a Queue and dispatch them once the transaction is committed. Please notice that :

  • Not all events are deferred. Only those that have IHandles.Deferred = true.
  • In addition, events are never deferred if there is no transaction in progress.
    ///<summary>
    /// Domain events handler that supports deferred execution
    /// </summary>

    class DeferredDomainEventsRaiser : IDomainEventsRaiser
    {
        ///
        /// Locator of event handlers
        /// 

        private readonly IServiceProvider _resolver;
        ///
        /// Collection of events queued for later execution
        /// 

        private readonly ConcurrentQueue _pendingHandlers = new ConcurrentQueue();
        ///
        /// Data access state manager
        /// 

        private readonly IDbStateTracker _dbState;

        public DeferredDomainEventsRaiser(IServiceProvider resolver, IDbStateTracker dbState)
        {
            _resolver = resolver;
            _dbState = dbState;

            _dbState.TransactionComplete += this.Flush;
            _dbState.Disposing += this.FlushOrClear;
        }

        ///
        /// Raises the given domain event
        /// 

        /// Domain event type
        /// Domain event
        public void Raise(T domainEvent) where T : IDomainEvent
        {
            //Get all the handlers that handle events of type T
            IHandles[] allHandlers = (IHandles[])_resolver.GetService(typeof(IHandles[]));

            if (allHandlers != null && allHandlers.Length > 0)
            {
                IHandles[] handlersToEnqueue = null;
                IHandles[] handlersToFire = allHandlers;

                if (_dbState.HasPendingChanges())
                {
                    //if there is a transaction in progress, events are enqueued to be executed later

                    handlersToEnqueue = allHandlers.Where(h => h.Deferred).ToArray();

                    if (handlersToEnqueue.Length > 0)
                    {
                        lock (_pendingHandlers)
                            foreach (var handler in handlersToEnqueue)
                                _pendingHandlers.Enqueue(() => handler.Handle(domainEvent));

                        handlersToFire = allHandlers.Except(handlersToEnqueue).ToArray();
                    }
                }

                foreach (var handler in handlersToFire)
                    handler.Handle(domainEvent);
            }
        }

        ///
        /// Fire all the events in the queue
        /// 

        private void Flush()
        {
            Action dispatch;
            lock (_pendingHandlers)
                while (_pendingHandlers.TryDequeue(out dispatch))
                    dispatch();
        }

        ///
        /// Execute all pending events if there is no open transaction. Otherwise, clears the queue without executing them
        /// 

        private void FlushOrClear()
        {
            if (!_dbState.HasPendingChanges())
                Flush();
            else
            {
                //If the state manager was disposed with a transaction in progress, we clear
                //the queue without firing the events because this flow is pretty inconsistent
                //(it could be caused, for instance, by an unhandled exception)
                Clear();
            }
        }

        ///
        /// Clear the pending events without firing them
        /// 

        private void Clear()
        {
            Action dispatch;
            lock (_pendingHandlers)
                while (_pendingHandlers.TryDequeue(out dispatch)) ;
        }
    }

This new interface IDbStateTracker simply expose some events about  the state of the database and it could be implemented by an UnitOfWork (see source code attached to this post, also available on GitHub).

    ///<summary>
    /// Tracks the database's state
    /// </summary>

    public interface IDbStateTracker : IDisposable
    {
        ///
        /// Triggered when disposing
        /// 

        event Action Disposing;

        ///
        /// Triggered when the transaction is completed
        /// 

        event Action TransactionComplete;

        ///
        /// Returns true if there are uncommitted pending changes. Otherwise, returns false.
        /// 

        bool HasPendingChanges();
    }

Putting It All Together

This approach allows event handlers to be registered automatically by using conventions. Also, the same design allows event handlers to be fired online or have their execution delayed until transaction is over. The source code available on GitHub) contains a full working example of the principles discussed here.

Cohesion and coupling: Principles of orthogonal, scalable design

The sections below are about enabling an application to evolve and be maintained with minimal risks and effort. It is not easy to interpret a lot of complex information derived from the organizational structure of a source code. By separating concerns (link), we minimize complexity. Different responsibilities are maintained in different places. Separation of concerns is about dividing to conquer, about modularity, encapsulation, defining layers, about individual pieces of code that are developed and maintained individually and independently.

Instead of worrying about different sections of the code, we need to focus on localized changes in the right (and expected) places.

Orthogonality

In geometry, Euclidean vectors are orthogonal if they are perpendicular, i.e., form a right angle. Even if these vectors grow infinitely in space, they will never cross. Well designed softwares are orthogonal. Their components can grow or be modified without affecting other components.

Orthogonal design is built upon two pillars: cohesion and coupling. These concepts form the basis of software design. However, although well known, they are constantly ignored or misunderstood.

Orthogonal

Coupling

Coupling (also known as Dependency) is a degree to which one program unit (e.g. a class, module, subsystem) relies on other units. It is a measure of strength of the interconnections between elements, which should be minimized.

We want elements that are independent of each other. In other words, we want to develop applications that exhibit loose (rather than tight) coupling.

However, since parts need to communicate among themselves, we do not have completely independent modules. As interconnections grows between the parties involved, one module will need more information about the other, increasing the dependency between them.

CohesionNCoupling

The code below is a sample of content coupling. It occurs when one component depends (by modifying or relying) on internal data or behavior of another component. Changing elementary structure or behavior of one component leads to refactoring of other components.

	public class LoggedUsersController
	{
		public Dictionary<int, DateTime> LastUserLoginDateAndTime { get; set; }
		public List Users { get; set; }
	}

	public class BusinessRule
	{
		private LoggedUsersController loggedUsers =
					new LoggedUsersController();

		public User RegisterUserLogin(int userId)
		{
			User user = getUserFromDatabase(userId);

			if (loggedUsers.Users.Exists(u => u.Id == userId))
				throw new UserAlreadyExistsException();

			loggedUsers.Users.Add(user);

			if (!loggedUsers.LastUserLoginDateAndTime.ContainsKey(user.Id))
				loggedUsers.LastUserLoginDateAndTime.Add(user.Id,DateTime.Now);
			else
				loggedUsers.LastUserLoginDateAndTime[user.Id] = DateTime.Now;

			return user;
		}
	}

Since RegisterUserLogin performs direct access to inner content of LoggedUsersController, it contributes to a tighter coupling from the caller to the behavior of LoggedUsers. A better approach is to isolate the behavior inside LoggedUsersController.

	public class LoggedUsersController
	{
		private Dictionary<int, DateTime> LastUserLoginDateAndTime;
		private List Users;

		public void AddUser(User user)
		{
			if (this.Users.Exists(u => u.Id == user.Id))
				throw new UserAlreadyExistsException();

			this.Users.Add(user);
			if (!this.LastUserLoginDateAndTime.ContainsKey(user.Id))
				this.LastUserLoginDateAndTime.Add(user.Id, DateTime.Now);
			else
				this.LastUserLoginDateAndTime[user.Id] = DateTime.Now;
		}
	}

	public class BusinessRule
	{
		private LoggedUsersController loggedUsers =
					new LoggedUsersController();

		public User RegisterUserLogin(int userId)
		{
			User user = getUserFromDatabase(userId);
			loggedUsers.AddUser(user);
		}
	}

Now, the BusinessRule class is not tied to the implementation of LoggedUsersController. Instead, it is interested only in the responsibilities of its interface. It does not know details about the implementation of LoggedUsersControllers anymore, which contributes to a looser coupling. Moreover, all the logic related to the data of LoggedUsersControllers is handled closer, eliminating the inappropriate intimacy, which increases cohesion of the class.

Types of coupling

The types are listed in order of the highest to the lowest coupling.

  • Content coupling (worst) occurs when one components depends on internal data or behavior of another component. This is the worst degree of coupling, since changes to one component will almost certainly require modification to others.
  • Common coupling occurs when modules share common data, like global variables. As we all know, globals are evil. Changing the shared resource implies changing all the modules using it.
  • Control coupling occurs when one service or module knows something about the implementation of another and passes information to control its logic.
  • Stamp coupling occurs when modules share objects and use only a part of it. Sharing more data than what was needed allows the called module to access more data than it really needs.
  • Data coupling occurs when one modules or services share data between each other. Data passed as parameter to a function call is included in this type of coupling. Although services with many parameters are a bad sign of design, well handled data coupling is preferable when compared to other forms of coupling.
  • No coupling (best) – No intersection between modules.

If two or more components need to communicate, they should exchange as little information as possible.

Cohesion

Cohesion is a measure of responsibility and focus of an application component. It is the degree to which the elements of a module belong together, which should be maximized.

We want strong-related responsibilities in a single component. Thus, we want to develop highly cohesive code.

In a highly cohesive code, all data, methods and responsibilities are kept close. Services tend to be similar in many aspects.

A simple and intuitive way to test the cohesion of a class is to check if all the data and methods that it contains have a close relationship with the class name. Considering this, you should be aware that generic class names tend to generate cohesion problems, because they can get many different responsibilities over time. In fact, classes that have a vague name might one day become god objects (an anti-pattern that defines an “all-knowing” object that contains tons of features and services with many different purposes, which dramatically compromises cohesion and coupling of components).

The Law of Demeter: Talk only to your closest friends

Demeter

Also known as Principle of Least Knowledge or just LoD, the law of Demeter governs the interconnection between components. It reinforces loose coupling and high cohesion by stating that your object-oriented entities should only be talking to their closest friends.

The Law of Demeter states that a method of a given object should only access methods and accessors belonging to:

  • The object Itself
  • Parameters passed in to the method
  • Any object created within the method
  • Direct component elements of the object

Long chaining  of accessors and methods is a sign of bad design.

For example:

	public class BusinessRule
	{
		public Bid GetCarAuctionBestBid(int carId)
		{
			//... some logic here
			return bidsLogic.Bids.AllBids.GetBestBid(b => b.CarId = carId);
		}
	}

Even if you need a particular information that is at the end of the chain, digging out the methods and accessors yourself is a terrible idea.

	public class BusinessRule
	{
		public Bid GetCarAuctionBestBid(int carId)
		{
			//... some logic here
			return bidsLogic.GetBestBid(carId);
		}
	}

Now you are only talking to your closest friend. “bidsLogic” resolves its properties internally and expose services that are explicitly needed by others components. There is another thing going on here. The law of Demeter is not just about chaining. When you do not have to worry about navigating accessor and methods (i.e., when you have what you need by just calling a method or property of a nearby object), we say that you are telling the object what to do. The principle of least knowledge is closely related to the principle “Tell, don’t ask”!

Tell, Don’t Ask

The problem is not to use “get” accessor to understand a behavior of an object. The problem is to make decisions based on that. You do not want to ask the object about its inner state, make decisions about that state and then perform some dark operation. Object-oriented programming tells objects what to do.

The sample below is a example of code that asks too much. The code makes decisions about the state of the bill by adding prices contained in the list of Items assuming that the sum represents the total value of the bill.

	public class BusinessRule
	{
		public double CalculateDinnersCost(List<Bill> bills)
		{
			if (bills == null) return 0;

			double totalCost = 0;
			foreach (var bill in bills)
				if (bill.Items != null ? bill.Items.Count : 0)
					foreach (var item in bill.Items)
						totalCost += item.Price;

			return totalCost;
		}
	}
	public class Bill
	{
		public List<Item> Items { get; set; }

		public void AddItem(Item item) {...}
		public void Remove(int itemId) {...}
	}

Instead of asking that much, if the state of an object can be inferred by examining closer accessors and methods, we should consider relocating the logic inside the right object. This is the “Tell, don’t ask” principle: instead of developing procedural code, we tell the objects what to do.

	public class BusinessRule
	{
		public double CalculateDinnersCost(List<Bill> bills)
		{
			if (bills == null) return 0;

			double totalCost = 0;
			foreach (var bill in bills)
				totalCost += bill.CalculateTotalCost();

			return totalCost;
		}
	}
	public class Bill
	{
		public List<Item> Items { get; set; }

		public void AddItem(Item item) {...}
		public void Remove(int itemId) {...}

		public double CalculateTotalCost()
		{
			double totalCost = 0;
			if (this.Items != null ? this.Items.Count : 0)
				foreach (var item in this.Items)
					totalCost += item.Price;

			return totalCost;
		}
	}

Single Responsibility Principle

The Single Responsibility Principle is straightforward:

Every class should have a single responsibility and have one, and only one, reason to change.

Imagine an entity called MessagesRegister, which is responsible for registering alerts and notifications.

This is how MessagesRegister works:

  • It reads a configuration file
  • It processes some business logic to create notifications.

This class can be changed for two reasons. First, the source of the configuration can evolve into something more elaborate over time (instead of XML, a configuration through graphical user interface). Furthermore, the actual processing rule might change.

It is a bad design decision to keep together two pieces of information that are changed for different reasons. The cohesion (i.e., focus and responsibility of the class) is reduced and the principle of separation of concerns is violated.

Putting it all together

So… Where should I put this code? While coupling measures the degree of dependence between different entities of the system, cohesion is a measure of how focused are the responsibilities of a given entity.

By following these principles, we can enumerate some major achievements in our design:

  • Maintenance is child’s play: we keep together things that need to be maintained together and things that are not directly related can be changed without affecting other components.
  • The code is readable and algorithms become more self-documenting.
  • Classes have well-defined responsibilities and code duplication is drastically reduced.

CohesionNCoupling2

Related readings