First thing that made me confused when reading original DDDSample source code were those 'application events' - an interface defined on an the
application layer level which defines (possibly) asynchronous operations used to communicate between aggregates.
I used to view
application layer as merely orchestrating
domain operations. Application layer methods were supposed to be called either by GUI or by messaging infrastructure.This layer was also responsible for managing non-functional requirements, such as transactions and logging. I have always followed zealously the 'no logic in app layer' principle. When I started digging deep into DDD, I encountered Udi Dahan's series about
domain events. I got to like the idea of
domain object pushing out events.
So, now you understand my confusion when I encountered the events on the app layer level. Who and what is wrong?
I started with examining the Java way of defining app layers. In .NET we usually create one assembly per layer. A fairly complicated solution can contain as few as 4-5 assemblies and that is OK. In Java however, there is no notion of assemblies, only packages. And
package is a completely different animal. A package, more or less, resembles a .NET
namespace - is much more focused and fine-grained.
Application layer responsibilities which I was used to are divided into 2 main (and some helper) packages in DDDSample: the
app layer itself and a
facade for calling it from GUI code. The thinner app layer is, naturally, much closer to domain code, than would be in a .NET solution, because it is free of any UI or messaging dependencies. The breech in encapsulation of domain layer is much smaller in this fine-grained package structure then it would by in coarse-grained .NET code.
There is also another issue - an issue of naming. The DDDSample project names its events '
application events'. Udi called his mechanism '
domain events'. It the latter case there is a name clash with
events as domain elements: the entities which don't change their state. Both live in the domain and both are events in their nature. Udi's events, however, are
transient - their role is to push state change notifications out of the domain. On the contrary, events as domain elements are
persistent entities. So, it makes sense, from purely naming perspective, to define these transient event as 'application events' and avoid the naming confusion.
The effect of this name and semantics clash is critical in the
HandlingEvent registration function. On one hand
HandlingEvent is an event as domain element - an immutable entity. And on the other hand, it is also an event in Udi's sense - it pushes out, from the
Handling aggregate, a notification of state change to be processed within the
Cargo aggregate. In the original code the application event is published by the app layer and this approach solves the problem quite well. But I wanted bady to use domain events as described by Udi and it came out that there is no object which can be made responsible for publishing the transient 'domain event' since only operations performed on
HandlingEvents are
create (using constructor) and
store (using repository).
I decided to
change the design in this particular area and make
HandlingHistory object responsible for both storing
HandlingEvent persistent events and publishing
CargoWasHandler transient events. I think it finally solves my problem, at least for now. And, completely for free, I made the design follow Udi's advice
not to create aggregate roots.