Hi All,
I have an architectural question about wicket, DDD and the service layer.
Let's say we have a simple JPA entity (Customer), and a few simple CRUDL
screens.
For database access, we have a DAO layer (CustomerDao) which delegates
to an EntityManager, and provides some convenience methods for searching.
We also like to have clear boundaries, so we have a thin service layer
which wraps persist() and delete() calls in a transaction before
forwarding them to the DAO layer (@Transactional, as provided by
guice-persist).
A wicket model fetches one or more customers (by id or by running a
search), and attaches to a form. In the form we use PropertyModels which
push their changes to the entity, and in onSubmit() we call
service.persist(entity).
This means that the actual changes to the model happen outside of the
transaction (in wicket code), and within the transaction (/service
layer) we merely call persist() and flush().
Then parts of the app need something a bit more advanced, so we decide
to apply parts of DDD and put logic where it belongs (on the domain
models). However, some logic coordinates multiple models, so we add a
domain- or application-service for that.
The good thing about DDD is that it's a lot more clear what happens
(intent). We now realize that having a persist() method on a
entity-based service now looks like a bit of a code smell, since it does
not capture intent at all. Also, since the changes to the model happen
in wicket, before the service layer is called, I feel that the service
layer is not doing anything to act as a boundary. We might as well mark
the persist() method on our daos @transactional and remove the service
layer.
The only clean way to fix this seems to be either:
(a) using DTO's so the UI/wicket is not actually modifying domain entities
upside: the state of the domain is not modified by wicket itself
downside: duplication of models (actual model + DTO);
downside: validation is currently set-up in wicket by scanning
fields for validation annotations, so we would need to duplicate those
on the DTO?
(b) using a concept from CQRS: sending commands to the domain through a
bus. This clearly and cleanly defines the intent and captures the exact
change.
upside: the state of the domain is not modified by wicket itself
downside: likely overkill for what we are trying to achieve; lot of
extra complexity
(c) wrapping the entire request in a transaction
upside: easy to implement
downside: since anything in the request can fetch a dao, read some
entities and modify them, this means we can lose track of what happens
in a request;
downside: feels like moving backwards
(d) simplify by removing thin services and, where necessary, putting
more logic in the dao's
upside: simple api contract: want to save/update an entity? use the
dao directly
downside: dao's contain logic which does not really belong there
downside: if at some point we really do need a service, the api
contract becomes less clear: for X and Y you can use the dao, for Z you
have to use a service
(a) and (b) provide a way to capture a change and execute all of the
change inside a transaction.
So my question to the list is: what are your experiences with this? How
do you deal with this in simple to moderately complex webapps?
Thanks for reading!