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.