2

I've stumbled upon a problem: "I can't split my domain models into aggregate roots".

I'm a junior developer and novice at DDD. I really want to understand it, but sometimes it's really confusing.

From this point I want to describe my domain briefly.

My poject dedicates to provide users opportunity to create any kind of documents by themselve. Users can create a new type of document. Each new type consists of its attributes. Then a user of this application can create a concrete document based on its type. User can also send the document for approval. An approval flow is different for each types.

So, we have the following models:

  1. DocumentType/ DocumentTemplate - acts as a template based on which concrete documents are created. It has one to many relationship with Document.
  2. DocumentsAttribute - represents an attribute of document. It has many to many relationship with DocumentType.
  3. AttributeValue - when a concrete document is created, It looks at its type and creates values for attributes, which has its type. Many to many relationship with Document and Attribute.
  4. Document - represents a concrete document that is created by users.

There are others models but I don't think that they make sense.

As you understand, here I apply Entity Attribute Value (EAV) pattern of data model. You can see a diagram that shows relationships in the database.

And my problems are:

I have a lot of entities in my model besides I have described.

I think that Document is definitely an aggregate root in my Domain. Because such things as ApprovalProcess which is aggregate cannot live out of it.

Here is the first question:

ApprovalProcess consists of its steps. Each step is an entity since it is mutable. A step has its state that can be changed. ApprvalProcess's state depends on its steps. Here we have a business invariant: "ApprovalProcess can be approved only if all its steps is approved".

I think that it is an aggregate root because it has the business invariant and contains entities that cannot live out of it. And we don't want to allow to have direct access to its steps in order to keep ApprovalProcess consistent.

Am I mistaken that ApprovalProcess is an aggregate root? May it is just an aggregate? Can one aggregate root exist within another one as it's part? Does it mean that ApprovalProcess is just aggregate because Document is responsible for access to its parts? But when ApprovalProcess's step is approved, Document delegates an operation to ApprovalProcess.

For example:

Document doc = new Document(...);
doc.SendForAooroval(); //ApprovalProcess is created.

doc.ApproveStep(int stepId); // Inside the method Document delegates responsibility for approvement to ApprovalProcess.

Or I should leave Document and ApprovalProcess separately. Hence Document is going to refer to ApprovalProcess by Identity. And we have the following scenario:

Document doc = documentRepository.Get(docId);
doc.SendForAooroval();// A domain event "DocumentCreatedEvent" is raised.

DocumentCreatedEventHandler:

ApprovalProcess approvalProcess = new ApprovalProcess(event.DocId); // ApprovalProcessCreatedEvent is raised

approvalProcessRepository.Add(approvalProcess);
approvalProcessRepositroy.UnitOfWork.Save(); //commit 

But if ApprovalProcess's state changes, Document's state also changes. ApprovalProcess is approved then Document is also approved. Another word ApprovalProcess is kind of part of Document's state. Only thanks to it we can know that Document is approved.

And the biggest problem that I'm experiencing:

DocumentType is also an aggregate root. It consists of its attributes and ApprovalScheme. I haven't mentioned ApprovalScheme yet on purpose to keep my explanation as simple as possible. ApporvalScheme consists also from some entities. It's just an approval flow for DocumentType. ApprovalProcess is created according to ApprovalScheme of DocumentType which has Document. ApprovalScheme cannot exist without DocumentType. One to one relationship.

Document refers by identity to its DocumentType. Is it correctly?

At the begining of this task I thought that DocumentType should be a part of Document.

DocumentType has many Documents but in my domain It doesn't make sense. It doesn't represent the state of DocumentType. DocumentType can be marked as deleted but can't be deleted.

Document and DocumentType are two different aggregate roots. Am I right?

Thank you so much If you read it. Thank you a lot for you attention and help! Sorry for my terrible English.

Ivan avn
  • 77
  • 7

1 Answers1

3

Am I mistaken that ApprovalProcess is an aggregate root? May it is just an aggregate? Can one aggregate root exist within another one as it's part?

These questions doesnt make any sense to me. An aggregate is a group of entities and value objects, where one of the entities is the parent of the group. The aggregate root is the parent entity of an aggregate. A particular case is when the aggregate is just an entity. The entity alone is an aggregate and the entity is the aggregate root of course.

I think that I would try to model your problem from another point of view: as a state machine.

I see ApprovalProcess as a flow a document follows, not as an entity. I don't know the flow diagram of the process, but I guess that what you call "steps" would be the "states" a document can have during the process, and you have transitions between steps, so that first when you create a new document, it is at a starting step, and through the lifetime of the document, it pass from a step to another, till it reaches a final step (e.g. document approved).

So the document entity would have behaviour that changes its a state.

For example, in Java you can implement the state pattern (a state machine) with enums.

choquero70
  • 4,470
  • 2
  • 28
  • 48
  • What if you have approval processes for multiple operations, such as approving the release, approving the archiving, etc. It seems to me that making the concept of `ApprovalProcess` explicit rather than implicit would add value, no? What is less clear in my mind is whether the process should live inside or outside of the AR and ultimately what should trigger the AR mutations when the `ApprovalProcess` is rejected or approved. Implicitly modeling the approval process as a series of state changes of the AR wouldn't allow you to easily answer questions such as who rejected the 2nd publish req.? – plalx Feb 20 '19 at 16:34
  • For instance, if the `Document` has a status, is it really important that the status is moving from `waiting for publishing approval step 1`, `waiting for publishing approval step 2` and so on rather than having the status set to `waiting for publishing approval` and then the `ApprovalProcess` would have the state machine to conduct the process until the final step where any decision would impact the `Document` status? The `ApprovalProcess` can have an ID allowing to tie all the steps together as well. – plalx Feb 20 '19 at 16:39
  • @voiceofunreason I really want your input on this one please ;) – plalx Feb 20 '19 at 16:41
  • @plax yes you are right, if the process/processes are complex I think I should model it/them apart from the document aggregate. I launched the idea without knowing in detail how complex the flow definition is, since in the question is hard to understand. I think that the flow could be modeled as well an aggregate, and there would exists relationship between steps(states) and documents. I will try to model a concrete example of a generic flow to put it here – choquero70 Feb 20 '19 at 17:06