10

How should we create new aggregate roots in cqrs architecture? In this example I want to create new aggregate root AR2 that holds reference to first one AR1.

I'm creating AR2 using AR1 method as starting point. So far I see few options:

  1. Inside method in AR1 createAr2RootOpt1 I could call new AR2() and save this object to db imediatelly using domain service that has acces to repository.
  2. I could emit event in first aggregate root eg. SholdCreateAR2Event and then have stateless saga that reacts on this and issues command CreateAR2Command which is then handled and actually creates AR2 and emits AR2CreatedEvent. In case of using event sourcing SholdCreateAR2Event would not be preserved in event store, since it does not affect the state of first aggregate root. (Or should we still save this in event store?)

    class AR1{
        Integer id;
        DomainService ds;
    
        //OPTION 1
        void createAr2RootOpt1(){
            AR2 ar2 = new AR2();
            ds.saveToRepo(ar2);
        }
    
        //OPTION 2
        void createAr2RootOpt2(){
            publishEvent(new SholdCreateAR2Event());    //we don't need this event. Shoud it still be preserved in event store?
        }
    }
    
    class AR2{
        Integer id;
        Integer ar1Id;
    
        void handle(CreateAR2Command command){
            //init this AR with values and save
            publishEvent(AR2CreatedEvent());    //used for projections afterwards and saved inside AR2 event store
        }
    }
    
    class Saga{
        void handle(SholdCreateAR2Event ev){
            emitCommand(new CreateAR2Command());
        }
    }
    

Which is more proper way to do this?

2 Answers 2

2

I think that option no. 2 is the solution, with a small but important modification: AR1 should not emit a event who's purpose is to create the AR2, instead it should emit a AR1WasCreated event. This event should be persisted in the event store, as it is an important event marking the birth of AR1. Then, a Saga whould listent for AR1WasCreated event and generate a command to create AR2: CreateAR2Command.

Option no.1 is very wrong. You should never inject that kind of domain service in an Aggregate. Aggregates should be pure, with no side effects other that the generation of events.

P.S. I never emit events from the constructor of the Aggregate as there is a distinction between creating an instance of an object (in the programming language sense) and the creation (the birth if you want) of an Aggregate. I emit events only from a handle method (when handling a command).

3
  • What do you mean by AR1WasCreated? Should it be AR2WasCreated? Also, if I use your logic, I emit event AR2WasCreated before it is actually created? And saving this event inside event log of AR1 seems problematic, since I don't actually need this data inside AR1 (it does not modify anything inside AR1). Feb 22, 2017 at 15:16
  • OK, 3 years later. It goes AR1WasCreated -> SAGA(has rule if A1 was created then create A2) -> CreateAR2Command -> AR2WasCreated. Jan 19, 2020 at 20:51
  • @BojanVukasovic I'm glad it worked as I wrote :) Jan 23, 2020 at 8:20
2

How should we create new aggregate roots in cqrs architecture?

Creation patterns are weird.

Udi Dahan has some useful things to say about the general problem: Don't Create Aggregate Roots. The basic point being that aggregate don't just pop out of nowhere, and that there is domain language that describes how they appear, which should be captured in your domain model.

Where it tends to get twisted is that the entity in your domain model that is processing the command is not the entity getting modified by the transaction. That's not wrong; it's just weird (compared to the cases where you ask an entity to modify itself.

Your second approach is also OK. "Events that we raise without actually saving to the database" are sometimes referred to as "domain events"

Basic idea being that, within the same transaction, the command handler raises the event, which travels along the bus to an event handler that allows the second aggregate to create itself. You get somewhat better code cohesion, maybe.

Note: in event sourced systems, you don't usually use events this way.

In case of using event sourcing ShouldCreateAR2Event would not be preserved in event store, since it does not affect the state of first aggregate root.

Note: event names are usually in the past tense -- ShouldCrateAR2 has the wrong spelling.

Yes, if you are just throwing an event onto the synchronous bus to run remote code, then you shouldn't be saving that event in the book of record. It's just an implementation detail at this scale.

Or should we still save this in event store?

Avoid modifying two different event streams in the same transaction. If this creation also represents a change to AR1, then the usual answer would be to modify AR1 in this transaction, with an asynchronous subscriber to those events who is responsible for firing the command to create AR2.

Idempotent command handling helps a lot here.

2
  • thanks for answer. One more thing that is not 100% clear - later if i have to use AR2 as argument to AR1 how should I pass this - since CQRS states that AR should be only used for writing and not querying. But I have no other option than using AR1.doSmthn(AR2 param) since any read projection I create has no complete data I need (only AR2 has complete data). Feb 25, 2017 at 22:13
  • > "Yes, if you are just throwing an event onto the synchronous bus to run remote code, then you shouldn't be saving that event in the book of record." I think saving it has real value in that you know that the process was kicked off for something to happen, you can now also track wether this actually completed. But I guess it depends on the use case
    – Chaosekie
    Sep 17, 2017 at 16:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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