Skip to main content
added 1446 characters in body
Source Link
RibaldEddie
  • 3.3k
  • 1
  • 17
  • 17

If by DDD Purist you mean someone following Eric Evans’ book to the letter then no it’s not OK. Evans makes clear that classes that are not aggregate roots cannot be retrieved from any place other than through an aggregate root. From page 92:

Now, to translate that conceptual AGGREGATE into the implementation, we need a set of rules to apply to all transactions:

  • The root ENTITY has global identity, and is ultimately responsible for checking invariants.
  • Root ENTITIES have global identity. ENTITIES inside the boundary have local identity, unique only within the AGGREGATE.
  • Nothing outside the AGGREGATE boundary can hold a reference to anything inside, except to the root ENTITY. The root ENTITY can hand references to the internal ENTITIES to other objects, but those objects can only use them transiently, and may not hold onto the reference. The root may hand a copy of a value to another object, and it doesn’t matter what happens to it, since it’s just a value and no longer will have any association with the AGGREGATE.
  • As a corollary to the above rule, only AGGREGATE roots can be obtained directly with database queries. All other objects must be found by traversal of associations.
  • Objects within the AGGREGATE can hold references to other AGGREGATE roots.
  • A delete operation must remove everything within the AGGREGATE boundary at once. (With garbage collection, this is easy. Since there are no outside references to anything but the root, delete the root and everything else will be collected.)
  • When a change to any object within the AGGREGATE boundary is committed, all invariants of the whole AGGREGATE must be satisfied.

Oddly enough one of his next examples is of a class similar to an invoice, and he points out that Invoice is an aggregate root in his example. If you’re finding yourself having this problem, perhaps it is a sign that an Invoice is an aggregate root in your system too.

Not only that, but your question specifically seems to be specifically about whether or not the Application Service layer can create a domain object. I think pretty clearly the answer is No. Evans has a section of the book devoted to the object lifecycle of domain objects and is fairly clear that Factories have this responsibility. Further, I would direct your attention to page 76 and the section titled SERVICES and the Isolated Domain Layer wherein he provides the following table:

Partitioning Services into Layers

Application

Funds Transfer App Service:

  1. Digests input (e.g. XML request) 2. Sends message to domain service for fulfillment 3. listens for confirmation 4. decides to send notification using infrastructure service.

Domain

Funds Transfer Domain Service:

Interacts with necessary Account and Ledger objects, making appropriate debits and credits, supplies confirmation of result (transfer allowed or not, etc.)

Infrastructure

Send Notification Service:

Sends emails, letters, etc. as directed by application.

Based on this understanding of Services, I think a DDD purist would recoil at the idea of an Invoice object being created from within an Application Service. Of course, this all presupposes that your use of the term "Application Service" matches Evans' own use. But I think the answer from Evans is quite clear.

If by DDD Purist you mean someone following Eric Evans’ book to the letter then no it’s not OK. Evans makes clear that classes that are not aggregate roots cannot be retrieved from any place other than through an aggregate root. From page 92:

Now, to translate that conceptual AGGREGATE into the implementation, we need a set of rules to apply to all transactions:

  • The root ENTITY has global identity, and is ultimately responsible for checking invariants.
  • Root ENTITIES have global identity. ENTITIES inside the boundary have local identity, unique only within the AGGREGATE.
  • Nothing outside the AGGREGATE boundary can hold a reference to anything inside, except to the root ENTITY. The root ENTITY can hand references to the internal ENTITIES to other objects, but those objects can only use them transiently, and may not hold onto the reference. The root may hand a copy of a value to another object, and it doesn’t matter what happens to it, since it’s just a value and no longer will have any association with the AGGREGATE.
  • As a corollary to the above rule, only AGGREGATE roots can be obtained directly with database queries. All other objects must be found by traversal of associations.
  • Objects within the AGGREGATE can hold references to other AGGREGATE roots.
  • A delete operation must remove everything within the AGGREGATE boundary at once. (With garbage collection, this is easy. Since there are no outside references to anything but the root, delete the root and everything else will be collected.)
  • When a change to any object within the AGGREGATE boundary is committed, all invariants of the whole AGGREGATE must be satisfied.

Oddly enough one of his next examples is of a class similar to an invoice, and he points out that Invoice is an aggregate root in his example. If you’re finding yourself having this problem, perhaps it is a sign that an Invoice is an aggregate root in your system too.

If by DDD Purist you mean someone following Eric Evans’ book to the letter then no it’s not OK. Evans makes clear that classes that are not aggregate roots cannot be retrieved from any place other than through an aggregate root. From page 92:

Now, to translate that conceptual AGGREGATE into the implementation, we need a set of rules to apply to all transactions:

  • The root ENTITY has global identity, and is ultimately responsible for checking invariants.
  • Root ENTITIES have global identity. ENTITIES inside the boundary have local identity, unique only within the AGGREGATE.
  • Nothing outside the AGGREGATE boundary can hold a reference to anything inside, except to the root ENTITY. The root ENTITY can hand references to the internal ENTITIES to other objects, but those objects can only use them transiently, and may not hold onto the reference. The root may hand a copy of a value to another object, and it doesn’t matter what happens to it, since it’s just a value and no longer will have any association with the AGGREGATE.
  • As a corollary to the above rule, only AGGREGATE roots can be obtained directly with database queries. All other objects must be found by traversal of associations.
  • Objects within the AGGREGATE can hold references to other AGGREGATE roots.
  • A delete operation must remove everything within the AGGREGATE boundary at once. (With garbage collection, this is easy. Since there are no outside references to anything but the root, delete the root and everything else will be collected.)
  • When a change to any object within the AGGREGATE boundary is committed, all invariants of the whole AGGREGATE must be satisfied.

Oddly enough one of his next examples is of a class similar to an invoice, and he points out that Invoice is an aggregate root in his example. If you’re finding yourself having this problem, perhaps it is a sign that an Invoice is an aggregate root in your system too.

Not only that, but your question specifically seems to be specifically about whether or not the Application Service layer can create a domain object. I think pretty clearly the answer is No. Evans has a section of the book devoted to the object lifecycle of domain objects and is fairly clear that Factories have this responsibility. Further, I would direct your attention to page 76 and the section titled SERVICES and the Isolated Domain Layer wherein he provides the following table:

Partitioning Services into Layers

Application

Funds Transfer App Service:

  1. Digests input (e.g. XML request) 2. Sends message to domain service for fulfillment 3. listens for confirmation 4. decides to send notification using infrastructure service.

Domain

Funds Transfer Domain Service:

Interacts with necessary Account and Ledger objects, making appropriate debits and credits, supplies confirmation of result (transfer allowed or not, etc.)

Infrastructure

Send Notification Service:

Sends emails, letters, etc. as directed by application.

Based on this understanding of Services, I think a DDD purist would recoil at the idea of an Invoice object being created from within an Application Service. Of course, this all presupposes that your use of the term "Application Service" matches Evans' own use. But I think the answer from Evans is quite clear.

Source Link
RibaldEddie
  • 3.3k
  • 1
  • 17
  • 17

If by DDD Purist you mean someone following Eric Evans’ book to the letter then no it’s not OK. Evans makes clear that classes that are not aggregate roots cannot be retrieved from any place other than through an aggregate root. From page 92:

Now, to translate that conceptual AGGREGATE into the implementation, we need a set of rules to apply to all transactions:

  • The root ENTITY has global identity, and is ultimately responsible for checking invariants.
  • Root ENTITIES have global identity. ENTITIES inside the boundary have local identity, unique only within the AGGREGATE.
  • Nothing outside the AGGREGATE boundary can hold a reference to anything inside, except to the root ENTITY. The root ENTITY can hand references to the internal ENTITIES to other objects, but those objects can only use them transiently, and may not hold onto the reference. The root may hand a copy of a value to another object, and it doesn’t matter what happens to it, since it’s just a value and no longer will have any association with the AGGREGATE.
  • As a corollary to the above rule, only AGGREGATE roots can be obtained directly with database queries. All other objects must be found by traversal of associations.
  • Objects within the AGGREGATE can hold references to other AGGREGATE roots.
  • A delete operation must remove everything within the AGGREGATE boundary at once. (With garbage collection, this is easy. Since there are no outside references to anything but the root, delete the root and everything else will be collected.)
  • When a change to any object within the AGGREGATE boundary is committed, all invariants of the whole AGGREGATE must be satisfied.

Oddly enough one of his next examples is of a class similar to an invoice, and he points out that Invoice is an aggregate root in his example. If you’re finding yourself having this problem, perhaps it is a sign that an Invoice is an aggregate root in your system too.