I am currently building a microservice-based system to learn CQRS/ES, Docker, AMQP and all the other goodies that goes with it.
I have never asked a question online before as I am usually pretty good at finding answers to my queries by reading what that others have asked.
This time, I am stumped by what seems to be very simple.
Business documents such as invoices, purchase order, credit memos, etc. usually have an identifying number. (i.e. Invoice No.: 5707)
My question is how would I produce a sequential number for each of these types of documents in an event-sourced system?
I'm concerned about race conditions where an invoice number may inadvertently be duplicated or skipped. What is the best practice for this?
Thank you for your time.
EDIT: Here is what I tried:
I have an aggregate root (AR) called Invoice that has an associated saga called newInvoiceInitialization. The Invoice receives a command CreateNewInvoice. The Invoice AR processes this command and emits an event called NewInvoiceWasCreated.
The newInvoiceInitialization saga handles the NewInvoiceWasCreated event and starts the saga to initialize the invoice AR. The newInvoiceInitialization saga in-turn sends a CheckoutNextInvoiceNumber command to another AR called SequenceNumberGenerator with value objects: CorrelationId and ResourceTypeName.
The SequenceNumberGenerator AR works with a saga called SequenceNumberCheckoutSaga. These two will "check-out" the next InvoiceNumber in the sequence and supply that to the Invoice AR via a SequenceNumberWasCheckoutOut event.
The newInvoiceInitialization saga receives the SequenceNumberWasCheckoutOut event and sends the InvoiceNumber to the Invoice AR with a AssignInvoiceNumberToInvoice command.
When the Invoice AR completes the newInvoiceInitialization saga successfully, it emits the event InvoiceNumberWasAssignedToInvoice. This event triggers the SequenceNumberCheckoutSaga to offers the command FinalizeNumberCheckout to the SequenceNumberGenerator AR which ends the SequenceNumberCheckoutSaga and finalizes the usage of that number.
If the Invoice fails to accept the InvoiceNumber or has other problems it sends the InvoiceNumberAssignmentFailed event which causes the SequenceNumberCheckoutSaga to command the SequenceNumberGenerator AR with a ResetNumberCheckout command to roll-back the checkout-out sequence number and ends the SequenceNumberCheckoutSaga.
This all just seems overly complicated to produce invoices with numbers that aren't missed or duplicated. I am probably missing something.