Define An API By Consuming It
Lately I’ve been working on a project that uses a layered architecture with DDD principles. There’s a repository layer and, below that, lies the purity of the domain modeling. Above that is an API service layer and then various flavors of presentation, as needed. One of the toughest thing to explain to people who are new to some of these concepts is “what should the service layer do?” Go ahead and try it yourself, or even look it up somewhere. Things like data access, domain, repository and presentation layers all have easily explained and understood purposes. The API service layer kind of winds up being the junk drawer of your architecture; “well, it doesn’t really belong in the domain logic and it’s not a presentation concern, so I guess we’ll stick it in the service layer.”
This seems to happen because it’s pretty hard for a lot of people to understand the concept of an API of which their team is both creator and consumer. I addressed this some time back in a post I wrote about building good abstractions and this post is sort of an actual field study instead of a contrived example. How do we know what makes sense to have in the application’s service layer? Well, write all of your presentation layer code assuming that you have magic boxes behind interfaces that cater to your every whim. If you do this, unless you have a pure forms-over-data CRUD app, you’ll find that your presentation layer wants different things than your repositories provide, and this is going to define your service level API.
Take a look at this relatively simple example that I dreamed up, based loosely on a stripped down version of something I’ve been doing:
public class EmployeeController : Controller
{
private ISomeServiceIllDefineLater _service;
public EmployeeController(ISomeServiceIllDefineLater service)
{
_service = service;
}
public ActionResult Index()
{
return View(_service.GetAllEmployees());
}
public ActionResult Index(string departmentName)
{
var employees = _service.GetEmployeesByDepartmentName(departmentName);
return View(employees);
}
[HttpPost]
public ActionResult Create(Employee employeeToCreate)
{
_service.Create(employeeToCreate);
_service.DispatchWelcomeEmail(employeeToCreate);
return RedirectToAction("Employees", "Index");
}
}
One thing you’ll notice straightaway is that my controller is pretty stripped down. It really just worries about the arguments to the HTTP methods, a terse summary of what to do, and then what to return or where to redirect. The only things that will make this more complex as time goes on are GUI related things — enriching the model, adding more actions, altering the user’s workflow, etc. This makes unit testing/TDD a breeze and it keeps unpleasantness such as “how to send an email” or “where do we get employees” elsewhere. But, most importantly, it also defines an API.
Right here I’m thinking in the purest terms. I want to show my users a list of employees and I want to let them filter by department. I also want to be able to add a new employee. So, let’s see, I’m going to need some means of getting employees and some means of creating a new one. Oh, and the requirement says I need to send a welcome email in most circumstances (times I wouldn’t based on an elided GUI setting), so I’ll need something that does that too.
Now, you’re probably noticing a serious potential flaw with this approach, which is that having a service that sends emails and fetches customers seems as though it will violate the Single Responsibility Principle. You’re completely right. It will (would). But we’re not done here by any means. We’re not defining what a service will do, but rather what our controller won’t do (as well as what it will do). Once we’re done with the controller, we can move on to figuring out appropriate ways to divvy up the behavior of the service or services that this will become.
Here’s another thing I like about this approach. I’m in the habit of defining a “Types” assembly in which I put the interfaces that the layers use to talk to one another. So, the Presentation layer doesn’t actually know about any concrete implementations in the Service layer because it doesn’t even have a reference to that assembly. I use an IoC container, and I get to do this because of it:
public class EmployeeController : Controller
{
private ISomeServiceIllDefineLater _service;
public EmployeeController(ISomeServiceIllDefineLater service)
{
_service = service;
}
public ActionResult Index()
{
return View(_service.GetAllEmployees());
}
public ActionResult Index(string departmentName)
{
var employees = _service.GetEmployeesByDepartmentName(departmentName);
return View(employees);
}
[HttpPost]
public ActionResult Create(Employee employeeToCreate)
{
_service.Create(employeeToCreate);
_service.DispatchWelcomeEmail(employeeToCreate);
return RedirectToAction("Employees", "Index");
}
}
public class DummyService : ISomeServiceIllDefineLater
{
public void Create(Employee employeeToCreate)
{
}
public void DispatchWelcomeEmail(Employee employeeToCreate)
{
}
public IEnumerable GetEmployeesByDepartmentName(string departmentName)
{
return Enumerable.Repeat(new Employee(), 12);
}
public IEnumerable GetAllEmployees()
{
return Enumerable.Repeat(new Employee(), 100);
}
}
Right there in the controller’s source file, below it, I stick a dummy implementation of the service and I wire up the IoC to use it. This is really handy because it lets me simulate, in memory, the way the application will behave assuming we later define real, actual service implementations. So I can use TDD to define the behavior of the controllers, but then I can also use this dummy service to define the layout, appearance, and flow of the views using actual, plausible data. And all of this without worrying about data access or anything else for the time being. I’m bifurcating the application and separating (to a large degree) the fulfillment of user stories from the nitty gritty technical details. And, perhaps most importantly, I’m defining what the service API should be.
I like having that dummy class there. It creates a warning for me in Code Rush (multiple classes in the same source file) that nags at me not to forget to delete it when I’m done. I can modify the GUI behavior right from the controller. As I add methods to the service while going along with my TDD for the controller, it’s easy enough to add implementers here as needed to compile. I consider this a win on my many fronts.
And when I’m done with the presentation layer and satisfied, I can now look at the single service I’ve defined and say to myself things like “should this be more than one class?” or “is some of this functionality already implemented?” I’ve nailed the presentation layer and I’m ready to do the same with the service layer, knowing exactly what kind of API my ‘client’ wants.