In a follow-up to his earlier post on Constructor Over-Injection, Jeffrey Palermo changes his stance on Constructor Over-Injection from anti-pattern to the more palatable code smell. In this post I introduce the concept of a Facade Service and outline a refactoring that addresses this code smell.
If I should extract a core message from Jeffrey Palermo’s blog post it would be that it’s a code smell if you have a class that takes too many dependencies in its constructor.
I can only agree, but only so far as it’s a code smell. However, it has nothing to do with DI in general or Constructor Injection specifically. Rather, it’s a smell that indicates a violation of the Single Responsibility Principle (SRP). Let’s review the example constructor:
public OrderProcessor(IOrderValidator validator,
IOrderShipper shipper,
IAccountsReceivable receivable,
IRateExchange exchange,
IUserContext userContext)
In this version, I even added IOrderShipper back in as I described in my earlier post. Surely, five constructor parameters are too many.
Constructor Injection makes SRP violations glaringly obvious.
What’s not to like? My personal threshold lies somewhere around 3-4 constructor parameters, so whenever I hit three, I start to consider if I could perhaps aggregate some of the dependencies into a new type.
I call such a type a Facade Service. It’s closely related to Parameter Objects, but the main difference is that a Parameter Object only moves the parameters to a common root, while a Facade Service hides the aggregate behavior behind a new abstraction. While the Facade Service may start its life as a result of a pure mechanistic refactoring, it often turns out that the extracted behavior represents a Domain Concept in its own right. Congratulations: you’ve just move a little closer to adhering to the SRP!
Let’s look at Jeffrey Palermo’s OrderProcessor example. The core implementation of the class is reproduced here (recall that in my version, IOrderShipper is also an injected dependency):
public SuccessResult Process(Order order)
{ bool isValid = _validator.Validate(order);
if (isValid)
{ Collect(order);
_shipper.Ship(order);
}
return CreateStatus(isValid);
}
private void Collect(Order order)
{ User user = _userContext.GetCurrentUser();
Price price = order.GetPrice(_exchange, _userContext);
_receivable.Collect(user, price);
}
If you examine the code it should quickly become apparent that the Collect method encapsulates a cluster of dependencies: IAccountsReceivable, IRateExchange and IUserContext. In this case it’s pretty obvious because they are already encapsulated in a single private method. In real production code, you may need to perform a series of internal refactorings before a pattern starts to emerge and you can extract an interface that aggregates several dependencies.
Now that we have identified the cluster of dependencies, we can extract an interface that closely resembles the Collect method:
public interface IOrderCollector
{ void Collect(Order order);
}
In lieu of a better name, I simply chose to call it IOrderCollector, but what’s interesting about extracting Facade Services is that over time, they often turn out to be previously implicit Domain Concepts that we have now dragged out in the open and made explicit.
We can now inject IOrderCollector into OrderProcessor and change the implementation of the private Collect method:
private void Collect(Order order)
{ _collector.Collect(order);
}
Next, we can remove the redundant dependencies, leaving us with this constructor:
public OrderProcessor(IOrderValidator validator,
IOrderShipper shipper,
IOrderCollector collector)
With three constructor parameters it’s much more acceptable, but we can always consider repeating the procedure and extract a new Facade Service that aggregates IOrderShipper and IOrderCollector.
The original behavior from the Collect method is still required, but is now implemented in the OrderCollector class:
public class OrderCollector : IOrderCollector
{ private readonly IUserContext _userContext;
private readonly IRateExchange _exchange;
private readonly IAccountsReceivable _receivable;
public OrderCollector(IAccountsReceivable receivable,
IRateExchange exchange,
IUserContext userContext)
{ _receivable = receivable;
_exchange = exchange;
_userContext = userContext;
}
#region IOrderCollector Members
public void Collect(Order order)
{ User user = _userContext.GetCurrentUser();
Price price =
order.GetPrice(_exchange, _userContext);
_receivable.Collect(user, price);
}
#endregion
}
Here’s another class with three constructor parameters, which falls within the reasonable range. However, once again, we can begin to consider whether the interaction between IUserContext and the Order could be better modeled.
In outline form, the Introduce Facade Service refactoring follows these steps:
- Analyze how dependencies interact to identify clusters of behavior.
- Extract an interface from these clusters.
- Copy the original implementation to a class that implements the new interface.
- Inject the new interface into the consumer.
- Replace the original implementation with a call the new dependency.
- Remove the redundant dependencies.
- Rinse and repeat :)
The beauty of Facade Services is that we can keep wrapping one Facade Service in new Facade Services to define more and more coarse-grained building blocks as we get closer and closer to the application boundary.
Keeping each class and its dependencies to simple interactions also makes it much easier to unit test all of them because none of them do anything particularly complex.
Adhering strictly to Constructor Injection makes it easy to see when one violates the SRP and should refactor to an Facade Service.
Update (2011.04.10): In my book I've changed the name of this concept to Facade Service as it more clearly communicates the relationship with the Facade pattern.
Last modified (2011.08.23): Changed all references to Aggregate Service (the old name of the concept) to Facade Service.