Domain Driven Design: Entities, Value Objects, Aggregates and Roots with JPA (Part 4)
Don’t abuse the `public` keyword in Java. The source code has very few public classes or methods. This is unusual for Java projects. Typically Java projects have package layouts that model the solution; “this package has all the entities, that package has all database related code, that package is all the services code”. That approach forces you to make almost everything public. In the long term on a big project brittle connections are made across business responsibility boundaries. There is no way the compiler can enforce boundaries that align to the business domain.
With DDD you model the problem space not the solution; “this package is everything to support contracts, that package holds everything for products, that package is all about customers”. Then you can be very strict and only expose the service classes, root entities, and core business concepts, and force client code to go via a narrow public API. This helps keep bugs at bay and allows you to add or refactor logic with confidence. It also makes it easy to add sophisticated and professional features like locking patterns, audit trails, and “restore to date” just inside of the narrow public API.
Java didn’t make `public` the default it made “package private” the default. Tragically that excellent hint as to how to write good OO/DDD code is ignored by the vast majority of Java developers. This is an epic fail. Package private is awesome as you can put your test code into the same package to be able to setup and verify fine grained unit tests whilst only exposing a minimal public API to code outside of the package. The unit tests in this sample code demonstrate this approach.
The optimal package layout for DDD is one where the compiler enforces boundaries that separate core concepts in the business domain. Making as much as possible package private and only exposing a minimal public API is a great tool to achieve this.
When Java coders look at the code though they will likely scowl at the contract package. It has entities, hidden repositories, and services that demarcate the database transaction boundaries. Classic Java says you have a service layer and a data layer that are separated things. Then again a lot of class Java projects stick all the logic in the service layer, make everything public, and don’t really do DDD or even much OO at all; so we should question whether the classic layout is anything other than a comfort blanket from the turn of the century J2EE days.
One could easily argue that the service class in the demo isn’t a classic service class as it is only about storing and retrieving root entities. In an applicant the classic service layer would be higher up in the application stack one step closer to the user.
To quote the scabl blog someone else with the same sort of experiences:
Most Java/Spring/JPA projects have separate repository and service layers. Ostensibly, the persistence logic and persistence concerns are encapsulated in the repositories, often called data access objects (DAOs). But in every JPA project I’ve worked on, this encapsulation fails, and the service layer is intimately involved with persistence concerns. It has to be aware of fetching strategies, cascades, and special handling of proxy objects. And it has to manage flushing the persistence cache, and understand which entities need to be reloaded into the cache before continuing after a flush.
Do we have to have such persistence complexity bubble up in the business logic service? I would advocate having a root entity service that hides those details from the business services. I considered renaming the ContractService to be “ContractStore” to highlight that it is all about hiding the root entity persistence details. Then the sample code is all about containing the object to relational impedance mismatch into one package that exposes a Store class to find, load and save root entities.