1

Say I have an aggregate root called Customer. In this example, the customer has a collection of invoices.

Is it ever "acceptable" for the Application Service layer to create an instance of the Invoice class? The invoice class is not the aggregate root.

I realise it is not "acceptable" for a none aggregate root from another aggregate to access the Customer class, however I wandered if this also applies to the Application Service.

I am talking from the perspective of a DDD purist. I realise that DDD is not always the best choice to solve a problem - I am just trying to improve my thinking in this specific area.

Update

Please see the code below:

public class InvoiceService
{

 Invoice Invoice;

 public InvoiceService(Invoice invoice)
 {
   Invoice=invoice;
 }

 public void DoSomething()
 {
   Invoice.DoSomething();
 }

}

I am asking if it is "acceptable" to access the Invoice domain object from an application service even though it is not an aggregate root i.e. must all Invoice actions go through Customer.Invoices instead of Invoices.

  • Isn't the Application Service Layer supposed to provide... um, application services? Are you suggesting that creating an invoice is not a valid application service? – Robert Harvey Nov 7 '17 at 15:49
  • @RobertHarvey, I have added some code to try and explain it better. – w0051977 Nov 7 '17 at 15:55
  • It appears that your InvoiceService is the aggregate root. – Robert Harvey Nov 7 '17 at 16:15
  • @Robert Harvey, InvoiceService is a class in the Application Service layer (zankavtaskin.com/2013/11/…). Not the Domain Layer. – w0051977 Nov 7 '17 at 16:18
  • Have you identified your aggregate root? – Robert Harvey Nov 7 '17 at 16:43
2

TLDR; "non-aggregate root" is not the same as "component of another aggregate"

Your invoice class might not be an aggregate root, but not because it is component of the Customer aggregate (or at least it should not be a component). It is (or should be) a simple domain object, and when it is (currently) not an aggregate (root), then because it does not consist of more than one class and does not consist of other components.

Just because there is 1:n association between customers and invoices does not make invoices automatically components of a customer. You obviously have requirements to deal with invoices as objects on its own, without loading them through a customer repository first, that proofs this point.

So by seeing an invoice as a simple domain object, accessing such an object from some service layer is pretty fine, even from a DDD purists point of view.

  • Thanks. Is there a set of guidelines that can be used to design DDD classes? When designing databases you have Normalisation. Is there something similar for the Domain Classes? (which would perhaps show that Invoice is not a valid property of Customer). – w0051977 Nov 7 '17 at 16:15
  • @w0051977: standard Normalisation rules can be applied as well to DDD classes, but for this case, it makes no difference: lets assume for a moment you model an Invoice as an aggregate of a Customer and ignore the fact this is suboptimal. That would give you the same tables on the database. However, you would deal with those tables differently in your application when loading records into domain objects, and you might add an "ON DELETE CASCADE" constraint to the CustomerID foreign key column in the Invoice table – Doc Brown Nov 7 '17 at 16:29
  • Thanks. However, I don't understand why it is suboptimal. There is a relationship between Customer and Invoice in the database so a Customer should have a collection of Invoices as a property. Could you explain why this is not right? + 1 for the first line of the answer. – w0051977 Nov 7 '17 at 16:38
  • @w0051977: I did not say a Customer cannot have a collection of Invoices (actually, it could have a collection of references to the related invoices, if you mean that). But that does not mean the lifetime of the Invoice objects are coupled to the related Customer. If you have requirements for dealing with Customers without invoices, and vice versa, model them both as invidual Domain objects with an association between them, not one as a component of the other. – Doc Brown Nov 7 '17 at 16:52
  • Thanks. Could you confirm what you mean by a collection of references? Do you mean a collection of GUIDs (assuming the Primary Key field of Invoice is a GUID) or a collection of Invoices that are lazy loaded from the database? – w0051977 Nov 7 '17 at 17:10
2

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.

  • "Evans makes clear that classes that are not aggregate roots cannot be retrieved from any place other than through an aggregate root". I don't think so. IMHO Evans makes clear that classes that are components cannot be retrieved from any other place. But simple domain objects that are no aggregates are not necessarily components. – Doc Brown Nov 7 '17 at 16:33
  • @DocBrown To be clear, I think Evans identifies, in the domain, just a few types of objects: Value Objects, Entities, and Services. It's true that within the Domain Service, which is NOT the same as an Infrastructure Service or an Application Service, references to domain objects are held, but it's pretty clear that object creation of something like an Invoice would belong to a Factory, not to the Service. – RibaldEddie Nov 7 '17 at 16:55
  • Yes, that's correct, but the OP was not asking for object creation within the service. He asked for dealing with Invoices inside the service, independently from Customer objects, so I deduce there will be an InvoiceRepo and/or InvoiceFactory as well. – Doc Brown Nov 7 '17 at 17:02
  • The OP's question needs a bit of clarification but even so, Application Services are not the same as Domain Services and the former should not be holding references to domain entities. – RibaldEddie Nov 7 '17 at 17:59
  • Yep, you are right, the title talks about creation, but the question text including the example does not. However, I think this is not the gist of the question. – Doc Brown Nov 8 '17 at 6:31
0

The reason why every access to the root entities or value objects should happen through the aggregate is that the aggregate root needs to be responsible for maintaining the invariants and making sure the aggregate is always in a valid state.

In the customer example, let's think about a business rule related to invoices. Let's say a customer cannot have more than 10 invoices. If you allow creating invoices from different places in the app you will need to check the business rules as well. This can easily create a big mess when the rules are changing and there is more than one place where these rules are being checked.

It might seem easy and simple to add an entity from the application service but if you are expecting the application to grow and change, it is always better to apply DDD principles.

  • Thanks. Are you saying that the application service layer should not access domain objects that are not an aggregate root? – w0051977 Nov 7 '17 at 16:18
  • Exactly. And that's mainly because the aggregate root need to be responsible for maintaining the invariants of the aggregate – Mohamed Bouallegue Nov 7 '17 at 16:24
  • Thanks. Is there a set of guidelines that can be used to design DDD classes? When designing databases you have Normalisation. Is there something similar for the Domain Classes? (which would perhaps show that Invoice is not a valid property of Customer and perhaps an aggregate root in its own right). – w0051977 Nov 7 '17 at 16:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.