Domain Model and Service Layer patterns in P of EAA

moteutsch picture moteutsch · Mar 2, 2014 · Viewed 7.4k times · Source

In Patterns of Enterprise Application Architecture, Martin Fowler talks about two patterns for organizing Domain Logic: Domain Model and Service Layer. The Domain Model pattern is the "pure OOP" approach, where models (those objects that are probably being looked up from the database using an ORM) contain business logic (albeit, probably only delegating to the logic in another class).

A sample Domain Model

The Service Layer pattern is like the Domain Model pattern, but with a thin layer in front of it containing the business operations that can be performed. In MVC, the controller would mostly interact with the Service Layer. I believe that most well-designed MVC web applications use this pattern.

A sample Service Layer

Now, to my question. Martin suggests that the Domain Model approach is the more object-oriented approach, and is therefore better. In my experience, it is very difficult (see: impossible) to implement in practice.

Take the example given in the first diagram above. There are two "entities" Contract and Product. These are persisted to the database with a mapper. In the example, there is a RecognitionStrategy. Martin puts the methods for delegating to this strategy, which contains the actual business logic, in the entities themselves; the client performs this calculation with contract.calculateRecognitions or contract.recognizedRevenue(someDate). When implementing similar designs, I usually write the client interface as strategy.calculateRecognitions(contract) and strategy.recognizedRevenue(contract, someDate). This makes the service layer necessary for coordinating the strategy and contract. The concrete strategy used is injected into the service.

Martin's approach is definitely more appealing from a design perspective, but the work around the setup is much more difficult:

  1. Passing in the strategy when instantiating a Product is a pain. You need to create Products via a factory curried with the concrete service to use, which will in turn pass it into the entity when creating it.
  2. Less fine-grained control over database access. Depending on the ORM settings, the Contract delegating to Product may perform a query per Product. Greedily loading Products in the mapper (or ORM) may be overzealous when we load a Contract but don't intend to call contract.calculateRecognitions(). My approach gives us finer-grained control, because the service has knowledge of the database abstraction layer, where as the entities shouldn't.

I'm sure there are more pain points in practice that I haven't enumerated here.

What concrete advantages are there in Martin's approach that might convince me to use a pure Data Model pattern?

Answer

Regarding your first point, you should use dependency injection when instantiating the Product object. Object graph construction is a full flagged responsibility and should not be mixed with your business logic (single responsibility principle).

Regarding the second point, your vendor particularities should be hidden behind you data access layer and your DAO or Repository should return the objects according to your needs.

An alternative for your concern over greedily loading Product s (on a situation the relationship is one to many) is to have the Product DAO injected into the Contract object. With that approach you could get the Product s related to the contract when required (probably on a getter that could also be used internally).

Of course a perfect solution does not exist and there will always be trade offs. Your job as an architect to evaluate the approach that better fits you application.

On my personal experience I noticed that relying too much on service classes tends to generate gigantic classes that don't have a well defined responsibility and are usually too difficult to test.

So benefits of using the Domain Model approach are clear separation of concerns and increased testability.

Finally you don't need to use a "pure" Domain Model approach. The domain model and service layer are expected to be used together. The domain model entities cover behaviors that fall within their boundaries and the service layer cover logic doesn't belong in any domain entity.

Some additional reference you may find interesting

Domain Driven Design and Development In Practice - An interesting article on DDD

Dependency Injection, Design patterns using Spring and Guice - Great book on dependency injection

Regards,

Emanuel Luiz Lariguet Beltrame