Our Story

Ceremony

Just Married

The Second Commandment: Thy entity shalt not participate in dependency resolution

by - December 21, 2010

Following on from my First Commandment a while back, I’d like to talk about a second mistake that a lot of people make when getting started with IoC containers. They worry about getting their domain entities sourced from the container, or participating in dependency resolution in some way. Domain entities should be POCO classes, they shouldn’t participate in dependency resolution.

Most business applications have ‘domain entities’ AKA ‘business objects’ usually something like this:

public class Car
{
public void Start()
{ ... }

public IEnumerable<Wheel> Wheels
{
get { return ...; }
}
}





When you start using an IoC container it’s a natural reaction to think, ‘how do I get my Car from the container’ or ‘how can my Car’s dependencies be resolved from the container’. You might see code like this for example:




public class Car
{
public IWheelRepository WheelRepository { get; set; }

public IEnumerable<Wheel> Wheels
{
get { return WheelRepository.GetWheelsFor(this); }
}
}





Along with code like this:




var car = container.Resolve<Car>();





This is wrong.



The general rule is that the domain model shouldn't have any outward facing dependencies, so you shouldn't be asking them to reference components from the container. The worst example of this is the Active Record pattern where domain entities are also responsible for their persistence. If you have a Customer object with methods like 'Save' and 'Update' you are breaking this rule.



The persistence problem is easy to solve, simply de-couple your domain class from its persistence. You now have a Customer and a CustomerRepository or maybe an IRepository<Customer>, I like the last one. Modern ORMs like NHibernate make this easy to do.



But what if your business logic requires you to do something with the 'outside world', like send  an email?



Say I have an Order domain entity that has a rule: when the order is confirmed, send the customer a confirmation email. I probably feel like I want to do something like this:




public class Order
{
private readonly IEmailSender emailSender;

public Order(IEmailSender emailSender)
{
this.emailSender = emailSender;
}

public void Confirm()
{
// ...
var confirmationEmail = CreateConfirmationEmail();
emailSender.Send(confirmationEmail);
}
}





But if my Order is not resolved from the container, how can I inject the IEmailSender?



There seem to be 3 main ways people solve this problem, each with its pros-and-cons...



1. Factor your business logic into a domain service, so I now have an Order class and an OrderService with a Confirm(Order order) method, the OrderService can be resolved from the container and have a dependency on the IEmailSender.




public class OrderService
{
private readonly IEmailSender emailSender;

public OrderService(IEmailSender emailSender)
{
this.emailSender = emailSender;
}

public void Confirm(Order order)
{
// ...
var confirmationEmail = CreateConfirmationEmail();
emailSender.Send(confirmationEmail);
}
}





The problem with this, is that we're now loosing encapsulation, our Order class' functionality is leaking out into domain services and it's ending up being just a property bag; the anaemic entity anti-pattern. We're not doing object oriented programming any more.



2. Inject the IEmailSender into the Order's confirm method: Order.Confirm(IEmailSender emailSender). Now we can get the IEmailSender from the container by doing constructor injection on the orchestrating class and simply pass it in when we call Confirm.




public class Order
{
public void Confirm(IEmailSender emailSender)
{
// ...
var confirmationEmail = CreateConfirmationEmail();
emailSender.Send(confirmationEmail);
}
}





The problem now is that the dependencies of the Confirm operation have become the responsibility of the orchestrating class and it has to worry about getting an instance of IEmailSender from somewhere. This violates Separation of Concerns. It makes your Confirm method brittle as well, what happens if you decide you don't need to send an email at a later date? Do you then go and rewrite every consumer of Order?



3. Udi Dahan's domain events pattern or something like it. Here we raise an event from our Order class' Confirm method and then have a handler that sends the email. Because the handler is resolved from the container, it can have the IEmailSender passed in via constructor injection. The caller is unaffected, it just calls Order.Confirm() and we don't have domain logic leaking out of the Order entity.




public class Order
{
public void Confirm()
{
// ...
DomainEvent.Raise(new OrderConfirmedEvent(this));
}
}

public class SendEmailOnOrderConfirmHandler : IHandle<OrderConfirmedEvent>
{
public void Handle(OrderConfirmedEvent orderConfirmedEvent)
{
var confirmationEmail = CreateConfirmationEmail();
emailSender.Send(confirmationEmail);
}
}





The downside is that we now have a nasty static dependency on the DomainEvent.Raise call scattered throughout our domain model.



I've used all three of these techniques at different times. I tend to avoid 2, it's got the nastiest code smell, and I'd probably favour 3 these days, simply because I always tend to have an Entity super class that can wrap the DomainEvent.Raise call and I really like the emerging CQRS patterns that are closely related.



But whatever you decide to do, avoid getting into the trap where you try and resolve your domain entities from the container.

You May Also Like

0 comments