The service layer shields the domain model and encapsulates domain logic. The service layer orchestrates the complexity of interacting with the model or external resources such as databases. Multiple components can then use the service layer while having limited knowledge of the model:
Figure 14.5: Service layer relationships with other layers
The preceding diagram shows that the presentation layer talks to the service layer, which manages the domain model and implements the business logic.The service layer contains services, which are classes that interact with other domain objects, such as the domain model and the data layer.We can further divide services into two categories, domain services, and application services:
- Domain services are those services we are talking about so far. They contain domain logic and allow consumers from the presentation layer to read or write data. They access and mutate the domain model.
- Application services like email services are unrelated to the domain and should live elsewhere, like in a shared (why rewrite an email service for every project, right?).
As with other layers, your service layer could expose its own model, shielding its consumers from domain model (internal) changes. In other words, the service layer should only expose its contracts and interfaces (keyword: shield). A service layer is a form of façade.
We further explore ways to keep copying anemic classes into other anemic classes to a minimum.
There are many ways to interpret this layer, and I’ll try to illustrate as many as possible in a condensed manner (from simpler to more complex ones):
- The classes and interfaces of the service layer could be part of the domain layer’s assembly, created in a Services directory, for example. This is less reusable, but it paves the way to sharing services in the future without managing multiple projects at first. It needs rigor to not depend on what you should not.
- The service layer could be an assembly containing interfaces and implementation. This is a great compromise between reusability and maintenance time. Chances are you will never need two implementations (see the next point) because the services are tied to the logic; they are the domain. You could even hide the implementation, as we did with the opaque façade in Chapter 11, Structural Patterns.
- The service layer could be divided into two assemblies — one containing abstractions (referenced by consumers) and one containing implementations.
- The service layer could be an actual web service tier (such as a web API).
When writing services code, by convention, people usually suffix a service class with Service, such as ProductService and InventoryService; the same goes for interfaces (IProductService and IInventoryService).No matter which technique you choose, remember that the service layer contains the domain logic and shields the domain model from direct access.The service layer is an amazing addition that shields and encapsulates the logic for manipulating an anemic domain model. It can defeat the purpose of a rich domain model if it’s just a pass-through but can be very useful to handle complex, non-atomic business rules that affect multiple domain objects.The primary decider of whether or not to add a service layer is tied to the complexity of your project’s domain. The more complex, the more it makes sense. The more trivial, the less it makes sense. Here are a few tips:
- Add a service layer when using an anemic model.
- Add a service layer for very complex domains.
- Do not add a service layer for low-complexity domains or façade over database applications.
Now, let’s look at the data layer.