|
|
@@ -3,7 +3,7 @@
|
|
3
|
3
|
------------------------------------
|
|
4
|
4
|
Patrik Fredriksson
|
|
5
|
5
|
------------------------------------
|
|
6
|
|
- July 29 2008
|
|
|
6
|
+ March 11, 2009
|
|
7
|
7
|
|
|
8
|
8
|
Careful characterization of the classes is an important activity when doing Domain-Driven Design. Sometimes it is fairly obvious in what category a particular class belongs, other times it is not as easy to sort out the different <Building Blocks of a Model-Driven Design>.
|
|
9
|
9
|
|
|
|
@@ -11,39 +11,39 @@
|
|
11
|
11
|
|
|
12
|
12
|
{Entities}
|
|
13
|
13
|
|
|
14
|
|
- <<<{{{xref/se/citerus/dddsample/domain/model/cargo/Cargo.html}Cargo}}>>>: <<<Cargo>>> has both a clear identity and a life-cycle with state transitions that we care about, so it's an entity. Many cargo instances will exist in the system simultaneously. The different instances have the same origin and destination, they may even contain the same kind of things, but it is important for us to be able to track individual cargo instances. In our case the cargo's identity is its tracking number. The tracking number is assigned upon creation and is never changed.
|
|
|
14
|
+ <<<{{{xref/se/citerus/dddsample/domain/model/cargo/Cargo.html}Cargo}}>>>: <<<Cargo>>> has both a clear identity and a life-cycle with state transitions that we care about, so it's an entity. Many cargo instances will exist in the system simultaneously. The different instances have the same origin and destination, they may even contain the same kind of things, but it is important for us to be able to track individual cargo instances. In our case the cargo's identity is its tracking id. The tracking id is assigned upon creation and is never changed. This is the handle used to track the cargo's progress, e.g. from the tracking website.
|
|
15
|
15
|
|
|
16
|
|
- The cargo's state will change during it's lifetime. It will start out as <<<RECEIVED>>> and in the normal case end its life as <<<CLAIMED>>> (note that this is a derived property of the cargo, calculated from its <<<{{{xref/se/citerus/dddsample/domain/model/cargo/DeliveryHistory.html}DeliveryHistory}}>>>). During a cargo's lifetime it may be assigned new destinations, its itinerary may be changed many times and it's <<<DeliveryHistory>>> will be recalculated as new <<<{{{xref/se/citerus/dddsample/domain/model/handling/HandlingEvent.html}HandlingEvent}}}>>>s arrive.
|
|
|
16
|
+ The cargo's delivery state will change during it's lifetime. It's transport status will start out as <<<NOT_RECEIVED>>>, i.e. booked but not yet handed over to the shipping company at the port, and in the normal case ends its life as <<<CLAIMED>>> (note that this is a property of the cargo's <<<{{{xref/se/citerus/dddsample/domain/model/cargo/Delivery.html}Delivery}}}>>>, tracking the current state the cargo. During a cargo's lifetime it may be assigned new destinations, its itinerary may be changed many times and it's <<<Delivery>>> will be recalculated as new <<<{{{xref/se/citerus/dddsample/domain/model/handling/HandlingEvent.html}HandlingEvent}}}>>>s are received.
|
|
17
|
17
|
|
|
18
|
|
- <<<{{{xref/se/citerus/dddsample/domain/model/carrier/CarrierMovement.html}CarrierMovement}}>>>: <<<CarrierMovement>>> is a vessel voyage from one location to another. In the sample app <<<CarrierMovment>>> is actually immutable, but it has a very clear notion of identity, <<<{{{xref/se/citerus/dddsample/domain/model/carrier/CarrierMovementId.html}CarrierMovementId}}>>>, This id could be something like a flight number for air shipments or a vessel voyage number for a ship, it is not the name or the identification of the actual vessel. To see this domain concept in a different context, try searching the current vessel schedule for {{{http://en.wikipedia.org/wiki/Emma_Maersk}Emma Maersk}} at {{http://www.maerskline.com/}} (please note that we are not associated with Maersk in any way).
|
|
|
18
|
+ <<<{{{xref/se/citerus/dddsample/domain/model/voyage/Voyage.html}Voyage}}>>>: <<<Voyage>>> is a vessel's trip from orgin to destination, typically made up of several segments, (<<<{{{xref/se/citerus/dddsample/domain/model/voyage/CarrierMovement.html}CarrierMovment}}>>>s). In the sample app <<<Voyage>>> consists of a <<<{{{xref/se/citerus/dddsample/domain/model/voyage/Schedule.html}Schedule}}>>> with the different <<<CarrierMovement>>>s in it and has a very clear notion of identity, <<<{{{xref/se/citerus/dddsample/domain/model/voyage/VoyageNumber.html}VoyageNumber}}>>>, This id could be something like a flight number for air shipments or a vessel voyage number for a ship, it is not the name or the identification of the actual vessel. To see this domain concept in a different context, try searching the current vessel schedule for {{{http://en.wikipedia.org/wiki/Emma_Maersk}Emma Maersk}} at {{http://www.maerskline.com/}} (please note that we are not associated with Maersk in any way).
|
|
19
|
19
|
|
|
20
|
20
|
{Value Objects}
|
|
21
|
21
|
|
|
22
|
|
- <<<{{{xref/se/citerus/dddsample/domain/model/cargo/Leg.html}Leg}}>>>: Leg consists of a starting point and an ending point (to <<<{{{xref/se/citerus/dddsample/domain/model/location/Location.html}Location}}>>> and from <<<Location>>>), and a reference to a carrier movement. A leg has no sense of identity; two legs with the same from Location, end Location and <<<CarrierMovment>>> are in our model completely interchangeable. We implement <<<Leg>>> as an immutable <Value Object>.
|
|
|
22
|
+ <<<{{{xref/se/citerus/dddsample/domain/model/cargo/Leg.html}Leg}}>>>: Leg consists of a starting point and an ending point (to <<<{{{xref/se/citerus/dddsample/domain/model/location/Location.html}Location}}>>> and from <<<Location>>>), and a reference to a voyage. A leg has no sense of identity; two legs with the same from Location, end Location and <<<Voyage>>> are in our model completely interchangeable. We implement <<<Leg>>> as an immutable <Value Object>.
|
|
23
|
23
|
|
|
24
|
|
- <<<{{{xref/se/citerus/dddsample/domain/model/cargo/Itinerary.html}Itinerary}}>>>: An Itinerary consists of a list of <<<Leg>>>s, with the from <<<Location>>> of the first <<<Leg>>> in the list as the starting point of the <<<Itinerary>>> and the to <<<Location>>> of the last <<<Leg>>> as the final destination. The same reasoning applies to <<<Itinerarie>>>s as to <<<Leg>>>s, they do not have identity and are implemented as <Value Objects>. Now, a <<<Cargo>>> can certainly have its <<<Itinerary>>> updated. One way to accomplish this would be to keep the original <<<Itinerary>>> instance and update the legs in the <<<Itinerary>>>'s list, in this case the <<<Itinerary>>> must be mutable and has to be implemented as an <Entity>. With the <<<Itinerary>>> as a <Value Object>, as in the case of the sample application model and implementation, updating it is a simple operation of acquiring a complete new <<<Itinerary>>> from the <<<RoutingService>>> and replacing the old one. Implementation of a <<<Cargo>>>'s <<<Itinerary>>> management is much simplified by having the <<<Itinerary>>> as a <Value Object>.
|
|
|
24
|
+ <<<{{{xref/se/citerus/dddsample/domain/model/cargo/Itinerary.html}Itinerary}}>>>: An Itinerary consists of a list of <<<Leg>>>s, with the load <<<Location>>> of the first <<<Leg>>> in the list as the starting point of the <<<Itinerary>>> and the unload <<<Location>>> of the last <<<Leg>>> as the final destination. The same reasoning applies to <<<Itinerarie>>>s as to <<<Leg>>>s, they do not have identity and are implemented as <Value Objects>. Now, a <<<Cargo>>> can certainly have its <<<Itinerary>>> updated. One way to accomplish this would be to keep the original <<<Itinerary>>> instance and update the legs in the <<<Itinerary>>>'s list, in this case the <<<Itinerary>>> must be mutable and has to be implemented as an <Entity>. With the <<<Itinerary>>> as a <Value Object>, as in the case of the sample application model and implementation, updating it is a simple operation of acquiring a complete new <<<Itinerary>>> from the <<<RoutingService>>> and replacing the old one. Implementation of a <<<Cargo>>>'s <<<Itinerary>>> management is much simplified by having the <<<Itinerary>>> as a <Value Object>.
|
|
25
|
25
|
|
|
26
|
26
|
{Domain Event}
|
|
27
|
27
|
|
|
28
|
|
- Some things clearly have identity but no life-cycle, or an extremely limited life-cycle with just one state. We call these things <Domain Events> and they can be viewed as hybrid of <Entities> and <Value Objects>. In the sample application <<<{{{xref/se/citerus/dddsample/domain/model/handling/HandlingEvent.html}HandlingEvent}}>>> is a <Domain Event> that represent real-life event such as a <<<Cargo>>> being loaded or unloaded, customs cleared etc. They carry both a completion time and a registration time. The completion time is the time when the event occurred and the registration time is the time when the event was received by the system. The <<<HandlingEvent>>> id is composed of the cargo, carrier movement, completion time, location and type (<<<LOAD>>> or <<<UNLOAD>>>).
|
|
|
28
|
+ Some things clearly have identity but no life-cycle, or an extremely limited life-cycle with just one state. We call these things <Domain Events> and they can be viewed as hybrid of <Entities> and <Value Objects>. In the sample application <<<{{{xref/se/citerus/dddsample/domain/model/handling/HandlingEvent.html}HandlingEvent}}>>> is a <Domain Event> that represent a real-life event such as a <<<Cargo>>> being loaded or unloaded, customs cleared etc. They carry both a completion time and a registration time. The completion time is the time when the event occurred and the registration time is the time when the event was received by the system. The <<<HandlingEvent>>> id is composed of the cargo, voyage, completion time, location and type (<<<LOAD>>>, <<<UNLOAD>>> etc).
|
|
29
|
29
|
|
|
30
|
30
|
{Aggregates}
|
|
31
|
31
|
|
|
32
|
32
|
In real life most things are connected, directly or indirectly. Mimicking this approach when building large software systems tend to bring unnecessary complexity and poor performance. DDD provides tactics to help you sort these things out, aggregates being one of the most important ones. Aggregates help with decoupling of large structures by setting rules for relations between entities. Aggregates can also have properties, methods, and invariants that doesn't fit within one single class. Java and other OO-languages typically miss specific language constructs to handle aggregates and in the sample application responsibilities that belong to an aggregate is most often implemented in the aggregate root.
|
|
33
|
33
|
|
|
34
|
|
- <<<{{{xref/se/citerus/dddsample/domain/model/cargo/package-summary.html}cargo}}>>>: cargo is the central aggregate in the sample application. <<<Cargo>>> is the aggregate root and the aggregate also contains the <Value Objects> <<<DeliveryHistory>>>, <<<Itinereray>>>, <<<Leg>>> and a few more classes.
|
|
|
34
|
+ <<<{{{xref/se/citerus/dddsample/domain/model/cargo/package-summary.html}cargo}}>>>: cargo is the central aggregate in the sample application. <<<Cargo>>> is the aggregate root and the aggregate also contains the <Value Objects> <<<Delivery>>>, <<<Itinereray>>>, <<<Leg>>> and a few more classes.
|
|
35
|
35
|
|
|
36
|
|
- <<<{{{xref/se/citerus/dddsample/domain/model/handling/package-summary.html}handling}}>>>: handling is another important aggregate. It contains the HandlingEvents that are registered throughout a cargo's progress from <<<RECEIVED>>> to <<<CLAIMED>>>. The <<<HandlingEvent>>>s have a relation to the <<<Cargo>>> for which the event belongs, this is allowed since <<<HandlingEvent>>> is an aggregate root and so is <<<Cargo>>>.
|
|
|
36
|
+ <<<{{{xref/se/citerus/dddsample/domain/model/handling/package-summary.html}handling}}>>>: handling is another important aggregate. It contains the <<<HandlingEvent>>>s that are registered throughout a cargo's progress from <<<NOT_RECEIVED>>> to <<<CLAIMED>>>. The <<<HandlingEvent>>>s have a relation to the <<<Cargo>>> for which the event belongs, this is allowed since <<<Cargo>>> is an aggregate root.
|
|
37
|
37
|
|
|
38
|
|
- The main reason for not making <<<HandlingEvent>>> part of the cargo aggregate is performance. <<<HandlingEvent>>>s are received from external parties and systems, e.g. warehouse management systems, port handling systems, that call our <<<{{{xref/se/citerus/dddsample/domain/service/HandlingEventService.html}HandlingEventService}}>>> webservice. The number of events can be very high and it is important that our webservice can dispatch the remote calls quickly. To be able to support this use case we need to handle the remote webservice calls asynchronously, i.e. we do not want to load the big cargo structure for each received <<<HandlingEvent>>>. Since all relationships in an aggregate must be handled synchronously we put the <<<HandlingEvent>>> in an aggregate of its own and we are able processes the events quickly and at the same time eliminate dead-locking situations in the system.
|
|
39
|
|
-
|
|
40
|
|
- <Editors note: We are aware that this example may need a little more work to drive the full benefits of aggregates home. This is one of the problems with a small sample app, real-world complexity is hard to simulate.>
|
|
|
38
|
+ The main reason for not making <<<HandlingEvent>>> part of the cargo aggregate is performance. <<<HandlingEvent>>>s are received from external parties and systems, e.g. warehouse management systems, port handling systems, that call our <<<{{{xref/se/citerus/dddsample/interfaces/handling/ws/HandlingReportServiceImpl.html}HandlingReportService}}>>> webservice implementation. The number of events can be very high and it is important that our webservice can dispatch the remote calls quickly. To be able to support this use case we need to handle the remote webservice calls asynchronously, i.e. we do not want to load the big cargo structure synchronously for each received <<<HandlingEvent>>>. Since all relationships in an aggregate must be handled synchronously we put the <<<HandlingEvent>>> in an aggregate of its own and we are able processes the events quickly and at the same time eliminate dead-locking situations in the system.
|
|
41
|
39
|
|
|
42
|
40
|
[images/aggregates.gif]
|
|
43
|
41
|
|
|
44
|
42
|
{Repositories}
|
|
45
|
43
|
|
|
46
|
|
- With the aggregates and their roots defined its typically trivial to define the <Repositories>. The <Repositories> work on aggregate roots and in the sample application there is one <Repository> per aggregate root, the <<<{{{xref/se/citerus/dddsample/domain/model/cargo/CargoRepository.html}CargoRepository}}>>> is responsible for finding and storing <<<Cargo>>> aggregates. The finders return <<<Cargo>>> instances or lists of <<<Cargo>>> instances.
|
|
|
44
|
+ With the aggregates and their roots defined its fairly trivial to define the <Repositories>. The <Repositories> work on aggregate roots and in the sample application there is one <Repository> per aggregate root, the <<<{{{xref/se/citerus/dddsample/domain/model/cargo/CargoRepository.html}CargoRepository}}>>> is responsible for finding and storing <<<Cargo>>> aggregates. The finders return <<<Cargo>>> instances or lists of <<<Cargo>>> instances.
|
|
|
45
|
+
|
|
|
46
|
+ The <Repository> interfaces are part of the domain layer, their implementations are part of the infrastructure layer. E.g. <<<CargoRepository>>> has an Hibernate implementation in the infrastructure layer: <<<{{{xref/se/citerus/dddsample/infrastructure/persistence/hibernate/CargoRepositoryHibernate.html}CargoRepositoryHibernate}}>>>.
|
|
47
|
47
|
|
|
48
|
48
|
{Services}
|
|
49
|
49
|
|
|
|
@@ -58,17 +58,16 @@
|
|
58
|
58
|
Domain services are expressed in terms of the ubiquitous language and the domain types, i.e.
|
|
59
|
59
|
the method arguments and the return values are proper domain classes. Sometimes, only the service
|
|
60
|
60
|
interface (<what> the service does) is part of the domain layer, but the implementation (<how> the service does it)
|
|
61
|
|
- is part of the application layer. This is analogous to how repository interfaces are part of the domain layer, but the
|
|
62
|
|
- Hibernate implementations are not.
|
|
|
61
|
+ is part of the infrastructure layer. This is analogous to how repository interfaces are part of the domain layer,
|
|
|
62
|
+ but the Hibernate implementations are not.
|
|
63
|
63
|
|
|
64
|
64
|
A good example of that is the <<<{{{xref/se/citerus/dddsample/domain/service/RoutingService.html}RoutingService}}>>>,
|
|
65
|
65
|
which provides access to the routing system and is used to find possible routes for a give specification.
|
|
66
|
|
- The {{{xref/se/citerus/dddsample/application/routing/ExternalRoutingService.html}implementation}} communicates with
|
|
67
|
|
- another team's context and translates to and from an external API and data model.
|
|
|
66
|
+ The {{{xref/se/citerus/dddsample/infrastructure/routing/ExternalRoutingService.html}implementation}} communicates
|
|
|
67
|
+ with another team's context and translates to and from an external API and data model.
|
|
68
|
68
|
|
|
69
|
69
|
On the other hand, if the service is possible to implement strictly using the domain layer,
|
|
70
|
|
- both the interface and the implementation are part of the domain layer. That is the case for the
|
|
71
|
|
- <<<{{{xref/se/citerus/dddsample/domain/service/impl/BookingServiceImpl.html}BookingService}}>>>, for example.
|
|
|
70
|
+ both the interface and the implementation could be part of the domain layer.
|
|
72
|
71
|
|
|
73
|
72
|
*Application services
|
|
74
|
73
|
|
|
|
@@ -82,4 +81,4 @@
|
|
82
|
81
|
The facades handle ORM session management issues and/or convert the domain objects to more portable
|
|
83
|
82
|
{{{http://martinfowler.com/eaaCatalog/dataTransferObject.html}Data Transfer Objects}}) that can be tailored
|
|
84
|
83
|
to specific use cases.
|
|
85
|
|
- See <<<{{{xref/se/citerus/dddsample/application/remoting/BookingServiceFacadeImpl.html}BookingServiceFacadeImpl}}>>> for an example.
|
|
|
84
|
+ See <<<{{{xref/se/citerus/dddsample/interfaces/booking/facade/internal/BookingServiceFacadeImpl.html}BookingServiceFacadeImpl}}>>> for an example.
|