Search code examples
c#cqrs

CQRS - One command triggering other commands/ being composed of other commands


Can one command trigger other commands in CQRS or be composed of other commands?

The specific case I'm wondering about is I have a 'CreateAppointmentNote' command that creates a note for an existing appointment. I also have a 'CreateAppointment' command that creates an appointment(withoutNotes).

I need to add functionality such that if the object used in the 'CreateAppointment' command has values for a note then it needs to create the note too. Can the 'CreateAppointment' command call the 'CreateAppointmentNote' command to do this?

The ultimate goal behind this is to have one call to an appointments POST endpoint be capable of adding notes instead of having to call another endpoint for notes too at initial creation of an appointment that has notes.

I recognize I could call the appointment command then call the notes command but this could leave me with a strange partial success should the adding of the appointment to the db succeed but the adding of the notes fail.

I'm just trying to figure out the correct way of doing this cleanly in CQRS and avoid just copying code from one command into the other if possible.


Solution

  • How do appointments and notes relate? Are they part of the same aggregate, and if so, is your appointment acting as the aggregate root (and therefore responsible for "protecting" the notes)? Does the successful creation of an appointment without a successful creation of the note invalidate the transaction?

    If notes are a fundamental part of a transaction for the creation of a new appointment and are part of the appointment aggregate, then they reflect things that change together and could be part of the same command; CreateAppointment can contain whatever note related properties as part of the creation of the request, and then an AddNoteToAppointment could manage subsequent notes an existing appointment.

    If they're completely separate services/aggregates that have to be created together to be valid, then you can consider something like the saga pattern that can ensure the entire transaction either passes or is rolled back, but it might be worth a look at your aggregate boundaries to see whether or not they should, in fact, be under a single aggregate instead.

    Quoting cqrs.nu:

    I know aggregates are transaction boundaries, but I really need to transactionally update two aggregates in the same transaction. What should I do?

    You should re-think the following:

    • Your aggregate boundaries.
    • The responsibilities of each aggregate.
    • What you can get away with doing in a read side or in a saga.
    • The actual non-functional requirements of your domain.

    If you write a solution where two or more aggregates are transactionally coupled, you have not understood aggregates.