Consider this simplified example: In an online ticket sale website, tickets have variable prices that change over time.
User searches for tickets. Once he finds a ticket he wants, clicks "Buy" and then a quote is issued. Quotes are persisted and they have they own locator that is sent by email. The quote page opens, showing the breakdown of what he is going to pay, and a button to pay. At this point, the ticket is still available for others. If the user clicks on "Pay now", a payment process starts and a Purchase Order is issued with his details.
When a purchase order is paid, the quote status must be set as "Accepted" and reference the purchase order; the ticket status is set as "Sold".
As per requirements, this must happen in an atomic fashion, meaning that it is not acceptable to have a paid purchase order referencing a non accepted quote, or a non sold ticket at any given time. Essentially, eventual consistency is not allowed.
There are two big aggregates, Quote
and PurchaseOrder
. The first manages the breakdown calculation, taxes, etc... The second manages payment details for a quote, payment event raising, etc... The problem I have is that right now it seems I have to use a transaction across ticket, quote and purchase order when the last is paid.
How may I model this honoring the "Transactions should not cross aggregate boundaries" rule?
-
I'm going to be shallow and drop some links for you: lostechies.com/gabrielschenker/2015/05/25/ddd-the-aggregate and vaughnvernon.co/?p=838Robzor– Robzor06/27/2017 22:07:54Commented Jun 27, 2017 at 22:07
-
If they need to be in the same transaction boundary, then Quote and Purchase Order are not likely to be aggregates. They are artifacts (perhaps entities) managed by the aggregate. They actually sound like documents (without behavior) which are produced in the process of executing aggregate behaviors.Kasey Speakman– Kasey Speakman06/27/2017 22:27:05Commented Jun 27, 2017 at 22:27
-
Would not that lead to the "too big aggregate" issue? I thought about a ReservationAggregate, however a quote is not a reservation.NullOrEmpty– NullOrEmpty06/28/2017 07:31:58Commented Jun 28, 2017 at 7:31
1 Answer 1
You need a process manager/saga that do exactly what you need without using transaction that spawn multiple aggregates.
Basically that Saga would reserve
the ticket right before the payment begins and would markTicketAsSold
after the payment is successful or releaseTheReservation
after the payment is failed.
This process and the invariant that a reserved
or sold
ticket cannot be reserved
again would ensure that the system as a hole is in the correct state at all times.
As per requirements, this must happen in an atomic fashion, meaning that it is not acceptable to have a paid purchase order referencing a non accepted quote, or a non sold ticket at any given time. Essentially, eventual consistency is not allowed.
This does not seem to be a valid invariant. The invariant should be "a sold ticket cannot be sold again". Carefully defining the business invariants will help you in designing the aggregate boundaries and correctly modeling the long running processes.
-
But when you commit the saga, still have to modify ticket, quote and purchase order state at the same time. Same problem again.NullOrEmpty– NullOrEmpty06/28/2017 07:20:29Commented Jun 28, 2017 at 7:20
-
@NullOrEmpty I don't understand what you mean by commiting the SagaConstantin Galbenu– Constantin Galbenu06/28/2017 07:21:35Commented Jun 28, 2017 at 7:21
-
@NullOrEmpty I think my
commit
term from the answer is not ok. I will edit itConstantin Galbenu– Constantin Galbenu06/28/2017 07:24:42Commented Jun 28, 2017 at 7:24 -
@NullOrEmpty You must carefully define the invariants. In your case "it is not acceptable to have a paid purchase order referencing a non accepted quote" does not seem to be a valid invariant but "a sold ticket can not be sold again" isConstantin Galbenu– Constantin Galbenu06/28/2017 07:28:13Commented Jun 28, 2017 at 7:28
-
1Well it is a requirement that in the purchase order page, where purchase order, quote and ticket data are shown, states are consistent at any given time.NullOrEmpty– NullOrEmpty06/28/2017 07:34:40Commented Jun 28, 2017 at 7:34
Explore related questions
See similar questions with these tags.