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).
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.
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:
- Passing in the strategy when instantiating a
Product
is a pain. You need to createProduct
s via a factory curried with the concrete service to use, which will in turn pass it into the entity when creating it. - Less fine-grained control over database access. Depending on the ORM settings, the
Contract
delegating toProduct
may perform a query perProduct
. Greedily loadingProduct
s in the mapper (or ORM) may be overzealous when we load aContract
but don't intend to callcontract.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?