Inversion of control is constantly a hot topic. So are domain model and domain-driven design. How about combining these into one hot post? Yeah, combining posts is easy compared to combining the patterns. I will focus on particular type of inversion of control — the dependency inversion.

The problem

You are building a system. One day you encounter a new requirement stating that customers with unpaid dues should be notified by e-mail.

As a careful designer, you had decided that your business logic will be structured according to domain model pattern. When the new requirement comes, you instantly know how to implement it — just add CheckUnpaidDues method to the Customer entity.

On the other hand, as a conscious developer, you recognize that abstract concepts should not depend upon details. Put it another way, business logic (the Customer entity) should not depend upon infrastructure (the e-mail sending library). So you decide to make use of Uncle Bob’s dependency inversion principle.

Here is the initial solution.

public interface IEmailSender
{
   void SendEmail(string address);
}

public class Customer
{
   public string Name { get; set;}
   public string EmailAddress { get; set;}
   public IEmailSender EmailSender { get; set; }

   public void CheckUnpaidDues()
   {
      //...
      HasUnpaidDues = true;
      EmailSender.SendEmail(EmailAddress);
   }
}

Dependency injection

It solution uses dependency injection pattern. Dependencies are being ‘injected’ into a domain model object using specialized framework — the Inversion of Control (IoC) container. This causes at least two problems.

First of all, injecting dependencies into domain objects is a technically challenging task. You have to deal with your object/relational mapping (O/RM) framework, plug into it’s instance creation process and make it use your container instead of simply new-ing up objects. This, however, can be accomplished with most O/RMs, given a skillful developer and reasonable amount of time.

The far worse problem is degradation of the model. Can you spot it right away? By adding an EmailSender field/property to the Customer entity we stated that e-mail sending mechanism is a part of customer model. It is ridiculous. Real world customers don’t carry SMTP servers in a backpack, so why should we model them that way? It is obvious that other, non domain-related, concepts are creeping into the model. We should do something to prevent this.

Double dispatch

The are some doubts (see comments under this Jimmy Bogard’s blog post) about concerning the name of this pattern, but that’s not the point. What does matter is the idea of passing a dependency as an argument to the method of domain object.

public class Customer
{
   public string Name { get; set;}
   public string EmailAddress { get; set;}

   public void CheckUnpaidDuesAndNotifyUsing(IEmailSender sender)
   {
      //...
      sender.SendEmail(EmailAddress);
   }
}

What we gain here by using this pattern is a clear message stating that e-mail sending mechanism is not a part of Customer’s model, but Customer-representing entity can use it in order to send notifications.

Service Locator

We can achieve a quite similar effect using service locator pattern:

public class Customer
{
   public string Name { get; set;}
   public string EmailAddress { get; set;}

   public void CheckUnpaidDues()
   {
      //...
      ServiceLocator.Current.GetInstance<IEmailSender>()
         .SendEmail(EmailAddress);
   }
}

There is a difference, however. A very big one, actually. This code hides the dependency instead of exposing it. This is what makes service locator an anti-pattern in majority of cases. As Krzysztof recently said (and I remembered)

service locator dependency is the worst of static class dependencies because it makes out class depend on possibly anything

There is, at least in case of dependency inversion, a third way (I don’t consider a service locator a solution anymore).

Domain Events

One again I will build upon great blog post series by Udi Dahan introducing a pattern called domain events. This pattern describes a solution to the problem of pushing information out of domain model without breaking it’s encapsulation. Sounds pretty useful, isn’t it? Will it enable us to send out e-mails (push data out) without introducing dependency on e-mail sender (without breaking encapsulation). Let’s try it out.
First, the pushing data out part.

public class Customer
{
   public string Name { get; set;}
   public string EmailAddress { get; set;}
   public bool HasUnpaidDues { get; set; }

   public void CheckUnpaidDues()
   {
      //...
      HasUnpaidDues = true;
      DomainEvents.Publish(new CustomerHasUnpaidDuesEvent(this));
      return true;
   }
}

This model tells us that Customer entity is able to check whether there are any unpaid dues and, if so, publish that information for others to use. What are these others? An e-mail notifier, for example.

public class SendEmailIfCustomerHasUnpaidDues : IEventHandler<CustomerHasUnpaidDuesEvent>
{
   public IEmailSender EmailSender { get; set; }
   public void Handle(CustomerHasUnpaidDuesEvent @event)
   {
      EmailSender.SendEmail(@event.Subject.EmailAddress);
   }
}

It is obvious that this solution is slightly more complicated that a double dispatch one. Apart from the additional event handler class, users must also include the domain events infrastructure (I use the one Udi published, almost unmodified). Why pay the price of additional complexity?

I think that the benefit of having really clear and focused model is worth its price. Customer model no longer cares about the notification process which is not essential to the model. It focuses on that’s important — checking whether there are any unpaid dues.

This solution has, however, a downside. It can be used only in case of one-way communication. Attempts to use the event to carry the result back to the model (I have seen some) negate the publish semantics of the operation and make the whole model unreadable.

Summary

My own rule of thumb is to use domain events wherever possible (communication is, or can be, one way) and double dispatch in all the other cases. What’s yours?

VN:F [1.8.7_1070]
Rating: 5.0/5 (3 votes cast)
Dependency inversion patterns and the domain model5.053