A few thoughts for the weekend in my usual opinionated way. Please take these with a measure of whimsy!
Lets kick things off and poke about with the topic of API design. Now if we’re fair this topic has been done to death. That said, any topic like this is well worth re-visiting too. In any form of programming, there are always going to be best practises but our application of these should always be on a ‘well it depends’ nature.
We see this quite often in how an API is designed. Good practise tells us that it should be SOLID but the steps to achieving that in the confines of your
project can vary quite a bit. Its here that rigid adherence to ‘best practise’ without allocating a good helping of common sense and experience can stunt or even ruin an API for general use.
Its in this case, more than any other, that writing tests first will really help in API design. By being your own consumer, you get to work with the public face of your code first and flesh out behaviour with interfaces and mocks. This forces you to consider the domain of your design and have a strong appreciation for the contracts you might need, the model objects you should have and so on.
Bottom up design
This lets me introduce two topics in this area. First off database first or ‘bottom up’ design. I’ll be frank here and that this does turn me off a project quite a bit. I’ve worked on many projects where the design of the database is being done first and the code bubbles up in layers until it eventually reaches the UI. What usually happens is we have monolithic service classes that are really static classes with interfaces (more on this later) or we end up with most of the work being done in repository classes with any service or manager classes acting as a simple pass through.
The missed opportunity here is that the application is defined by the operations it supports. Or more importantly the operations at each boundary in your domain (note I haven’t used ‘business logic’ here… *shudder*). In a very broad sense we need to define the operations of the application through use cases that extend into broad areas of responsibility which can be captured into domain objects in the UI and service domains. We then provide communication between these using model objects and can mock the dependencies in each layer.
This also means that we can mock our entire data access if needed. Something I’ve had to do when working with a major retailer where the webservice data was three months behind.
Now in fairness if you’re integrating with an existing database, then that’s how your project is. But should you be writing SQL Queries and Repository interfaces before working API tests? Personally – no.
Monolithic classes hidden behind interfaces
When the operations of our service domain are not coherently designed, what generally results is that we have monolithic interfaces to overly general objects. This is really only one step above a giant static class. Sure you can use dependency injection and you can mock it but that’s just an exercise
in ticking the best practise check list, without really understanding what you are doing. You’ll be able to get some testing in there but its not a loosely coupled design and it is still inherently brittle.
Consider an email service. Now in general you will see projects with an IEmailService with methods such as SendBillToUser, SendBillToBusinessUser,
SendWarning, SendRegistrationEmail and so on. If you’re getting the gist of my thread, you’ll know I think this is a complete mistake.
The API needs service classes for handling registration, billing and the like which in turn have a communication dependency. Then I can swap out how communication is transmitted either in part of my API or in whole. Taking the example above, the IEmailService should only require one or
two methods at best and have a model class for the email to be sent.
Mocking existing APIs
Integrating with third party applications or APIs or hosting your own code in a third party product (such as Sitecore) is something we do every day. Choosing to abstract these APIs behind adapter classes and mocks depends on that API design and any compromises we have to make. With that
in mind, if we have to expose elements of this API as part of our own then how far do we go into mocking or abstracting that API.
The reality is that ‘it depends’. Lets take the Sitecore API for the item and consider how we can make this testable. Should we create an IItem style interface and mock each field type with further interfaces, create a factory system that can produce model objects mapped from Sitecore items or create specific repository objects with the knowledge of each built in, perhaps built via code generation using pre-determined templates?
Right off the first is a big no for me. If I’m dealing with a model object in my domain, I don’t need to be worried about the low level implementation. This is really just a cut down version of the monolithic interface problem mentioned earlier. I’ll have a string for text, a link model object for a link and an image model object for an image thanks. If I create a custom field for a map (for example) I’ll decide if I want to map to multiple fields or create a complex type. The point being, its my domain not yours and when I come to mock it, I won’t need to mock your implementation.
There is some mileage in creating repository objects that can return and accept specific model types and perform the manual loading and saving. After all, this is what we would expect to do when querying a database ourselves. However, if I’m dealing with a model for a View, this model can be different depending on how the data is used. I want a model that matches my UI concern. This is where my UI/Service boundary comes in and I should be able to define different use cases based on my requirements. Though these repository objects will do it, I’ll have to consider how I map these to the view models. In some places I have worked this has been done by introducing translator objects as part of the service layer.
Or in other words, Controller/Presenter -> domain service -> repository -> load data -> translate data in domain -> Controller/Presenter -> View. This isn’t necessarily bad (remember ‘it depends’) but it does come with a significant trade off. It means that the data loading can be re-used but that I then have to have translators to manage each concern in my domain.
Finally I can write mappings in code/configuration and map data to my model objects as I need them. This is really a fluent set up of the system above but it does allow for some flexibility. Provided the both the data loading and the mapping systems are well tested, my mapping specifications are where the meat of the work will go. It also lets me remember that I’m dealing with a third party datasource and that I should have an extensible method of integrating with it. One that can grow or shrink with my needs and is easy to write tests for.
In closing, writing a good API is hard as its subject to so much opinion. After all, you are completely free to disagree with everything I’ve just said! Its also difficult to code for every eventuality so be clear on what problems your API solves and be clear on the boundaries of how it does it. At least you can be confident that it will do a core set of things and do them well. If suggestions come in to widen those boundaries and they make sense – great!
Equally though, don’t waste time making your API harder to use in the hope you’ll get more geek points with your peers :).