Implementing DDD Aggregates that have dependencies.

802 views
Skip to first unread message

kho...@tdstickets.com

unread,
Jan 18, 2021, 10:00:28 AM1/18/21
to DDD/CQRS
I'm using DDD and implementing it via ES/CQRS for this project.

Company has multiple offices and will open and close these offices
from time to time.

So I have modeled two aggregates that also represent two streams:

Company Aggregate = Company Stream
Office Aggregate = Office Stream

Company would hold a set of office references and the office would 
hold a reference back to company.  This reason for a separate Office aggregate
is because I do not want to be updating the Company aggregate every time the office
hours could change since offices are open at different times.

Company Aggregate
  ID id
  Set<ID> offices
  ...
  
Office Aggregate
  ID id
  ID companyId
  ....  


I know that the Company is responsible for opening and closing offices so I implemented a
aggregate factory method in Company to open an office with a method openOffice.  This 
method will create an aggregate office for the company. I created a command of 
open office with 

class OpenOfficeCommand {
   ID officeID;
   ID companyID;
   String name;
   String code;
}

In my handler I'm looking up the company aggregate from the company stream, after that I call the openOffice method on the company aggregate to open the office.  Inside 
the aggregate method an Office aggregate is created and an event is generated ("OfficeOpened") and applied to the Company aggregate and the offices set is updated with the officeID the result of the method is the Office Aggregate.  The issue that I'm wrestling with is that I will execute two saves that would need to be under one transaction.  The save of the Company aggregate root to persist the new state of the Company and a save of the new Office aggregate to its stream.  

Is this a correct process?

Thank you

-kurt

Tomas P.

unread,
Jan 18, 2021, 11:49:47 AM1/18/21
to ddd...@googlegroups.com
Hi Kurt,

is the "OfficeOpened" event part of the Company or Office stream/aggregate? or both?

Best
Tomas

--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dddcqrs/956dc32b-32b7-4bb2-827e-58c337cb2052n%40googlegroups.com.

Mateusz Nowak

unread,
Jan 18, 2021, 1:47:52 PM1/18/21
to DDD/CQRS
Hi Kurt,
with the rule "save one aggregate per transaction" you need to be pragmatic :) I'd rather say that it's applicable for modifying two aggregates, but not for modifying one and creating the other one. 

There are 2 solutions:
- for your domain is probably sufficient to do it in one transaction. Because is MODIFICATION of one aggregate (Company) and CREATION of the second one (Office). It's done by one person - so there is no issue with availability (optimistic locking) which we want to achieve with designing small aggregates and saving only one per transaction. So it's totally fine.
- you can publish the OfficeOpened event from Company stream and handle it asynchronous in event handler during another transaction. The event handler will be responsible for creating the Office aggregate. 

Is that makes sense :) ? 

Good luck in the fascinating DDD way!
Mateusz

kho...@tdstickets.com

unread,
Jan 18, 2021, 4:14:47 PM1/18/21
to DDD/CQRS
Thank you for the feedback and reading what y'all provided help build my confidence that I was understanding the DDD way of thinking, a lot of times I start trying to solve the problem with columns and rows and not how would the business do it.  So after I posted this and the responses and more reading in the blue, red, white, and chartreuse (not sure if this is real) books.  

I put this together... 

1. The company says open Office
2. Pass OpenOffice command to the Office Handler.
3. The Office aggregate is created.
3. That generates an event of OfficeCreated with a status of PENDING. 
4. That creates a stream for the office aggregate. -- Transaction
5. The OfficeCreated event is sent to the Company Domain handler.
6. Then an ApproveOffice command is created sent to the Company aggregate.
7. That generates an OfficeApproved event if valid or OfficeRejected if not and updates the offices set only if approved.
8. The Company aggregate is updated with the reference to the new office.  -- Transaction
9.  The OfficeApproved or OfficeRejected event is sent
10.  The Office handler receives the OfficeApproved  or OfficeRejected event 
11.  The Office aggregate is updated to a status of APPROVED or REJECTED.  -- Transaction

Thank you for the assistance on this!

-kurt

Samuel Francisco

unread,
Jun 23, 2021, 10:11:46 AM6/23/21
to DDD/CQRS
Hi Kurt,

Do you really need a Company Aggregate? Witch invariants are kept by the Company Aggregate? Maybe your problem is of the same kind of usernames unicity, ie, an inter aggregate business rule witch need a Domain Service instead a new aggregate. 

Ben Kloosterman

unread,
Jun 24, 2021, 3:12:15 AM6/24/21
to ddd...@googlegroups.com
Hi Kurt

You're looking at how a transactional /RDBS  system with a company / Office table would look at this and we generally want to avoid distributed / cross aggregate transactions and they are actually really rare in the real human world - they normally work on allowing them then fixing the problem later .

If human beings were to do this process how would they do this without computers ?  That's normally the best place to start , id then look at the parts that don't work well and are technically simple  eg transaction on a single aggregate and leave the rest as a more real world solution - is it a real problem , is it worth fixing and fix after the fact it's pretty easy to write a small Lambda / function to detect duplicates and handle them if the chance is very low , is the cost justified ?

UI's read model validation normally prevents most such cases so they tend to be around make believe problems that rarely exist in the real work but could happen . If the cost of failure is low, why solve it .. 

Once its human modelled then there are often some very interesting insights eg is offices an array inside the company aggregate  ?   ( it probably isnt but its an example) 

Ben
Reply all
Reply to author
Forward
0 new messages