SOLID ugly code
Today we are working on a system that, among other things, sends notification e-mails to employees when their attention is required. Getting an employee’s e-mail address is normally pretty simple, but this organisation has are around 10,000 staff out in the field, many of whom don’t have access to a computer let alone a work e-mail account.
To counter this problem we use some simple chain-of-command rules:
- If the Employee has an e-mail address, send it to that.
- If he doesn’t have one, send it to his immediate manager. If his manager doesn’t have an e-mail address, keep backtracking up the organisation until you find someone that does.
- If still no email address is found, send the message to a system administrator, and they can get the word out via other channels.
The interface for this service is pretty simple. It takes an employee, and returns an email address:
/// <summary>/// Service that can find an email address for an Employee... or the next best/// alternative if they don't have one./// </summary>public interface IEmailAddressResolver{ string GetEmailAddressFor(IEmployee employee);}
So how am I going implement it? With a T-SQL stored procedure, of course.
What? That may sound like a pretty bad idea — stored procedures are notorious for leaking application + domain logic into the persistence layer, and they are practically impossible to write tests for. But here is my justification:
- This is a database-driven legacy app, and only one bounded context has been modeled using DDD so far. The organisational hierarchy is only accessible via SQL, and modeling and mapping the legacy schema with NHibernate would take a couple of weeks at least. Therefore the simplest way to query it is via stored procedure, or stored-procedure backed services.
- I don’t want to add an e-mail property to Employee because that is an application concern, not part of the domain model. This needs to be done in a different layer, along with usernames, passwords and UI state, and we haven’t really thought about that yet.
- We’re getting close to the final release date for this project and we have a massive backlog of work remaining. A stored procedure is about the quickest thing I can think of to implement, and everyone in the team is well-versed in SQL.
Putting it to practice, here’s the concrete implementation we wrote. It’s called via NHibernate so at least we get caching:
// Implements IEmailAddresssResolver using a stored proc.public class EmailAddressResolver : IEmailAddressResolver{ readonly ISession session; ... public string GetEmailAddressFor(IEmployee employee) { if (employee == null) throw new ArgumentNullException("employee"); return this.session.GetNamedQuery("employeeEmailAddress") .SetParameter("employee", employee) .SetCacheable(true) .UniqueResult<string>(); }}
I’m not even going to show you the stored proc.
SOLID lets you write ugly code when you have to
The point of this story is that sometimes you have to write ugly code. But when you do, SOLID lets you do so in a neat decoupled manner. None of the callers of IEmailAddressResolver have any idea it’s actually just a dirty stored procedure because the implementation details are all hidden behind an intention-revealing interface. One day we can write a better implementation, swap them out in the IoC container, and no-one will be any wiser.