Skip to content

Commit 2014f87

Browse files
committed
doc: wip: readme
1 parent 075d793 commit 2014f87

File tree

1 file changed

+30
-13
lines changed

1 file changed

+30
-13
lines changed

README.md

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -415,8 +415,8 @@ To make a distributed transaction atomic, we can split it into multiple
415415
individually atomic transactions with the [Saga](https://microservices.io/patterns/data/saga.html) pattern.
416416
Read more about the Saga pattern:
417417

418-
- <https://learn.microsoft.com/en-us/azure/architecture/reference-architectures/saga/saga>
419418
- <https://microservices.io/patterns/data/saga.html>
419+
- <https://learn.microsoft.com/en-us/azure/architecture/reference-architectures/saga/saga>
420420

421421
To make individual transactions that compose a Saga atomic, we can use Saga's Semantic Locks with two other patterns:
422422
[Unit of Work](https://martinfowler.com/eaaCatalog/unitOfWork.html) and
@@ -431,14 +431,23 @@ The Transactional Outbox ensures messages/events are reliably published to a mes
431431
Using the Transactional Outbox, the message is first stored in the database as part of the Unit of Work's transaction,
432432
and a separate process reads saved messages from the database and sends them to the message broker.
433433

434+
You can find a complete example of how to apply these patterns in another of my projects:
435+
<https://github.com/filipsnastins/transactional-messaging-patterns-with-aws-dynamodb-streams-sns-sqs-lambda>
436+
434437
#### Applying Optimistic Locking to the Payments System Example
435438

436-
... TODO we're distributing the 'charge PaymentIntent' into two steps -
437-
request the charge (set the semantic lock) and perform the actual charge request.
439+
To apply optimistic concurrency control to the Payments System,
440+
we'll create the "charge `PaymentIntent`" Saga consisting of two separate atomic operations:
441+
442+
1. Request the charge by applying the semantic lock that sets the `PaymentIntent` to the `CHARGE_REQUESTED` state
443+
and publish the `PaymentIntentChargeRequested` event as part of the Unit of Work transaction.
444+
445+
2. Listen to the `PaymentIntentChargeRequested` event, send the charge request to the Payment Gateway,
446+
and set the `PaymentIntent` state to either `CHARGED` or `CHARGE_FAILED`.
438447

439448
```mermaid
440449
---
441-
title: PaymentIntent states updated with semantic lock
450+
title: Updated PaymentIntent states with semantic lock
442451
---
443452
stateDiagram-v2
444453
CHARGE_REQUESTED: CHARGE_REQUESTED (semantic lock)
@@ -453,6 +462,23 @@ stateDiagram-v2
453462
CHARGE_FAILED --> [*]
454463
```
455464

465+
The sequence diagram below simulates how optimistic concurrency control and semantic lock prevent concurrency anomalies
466+
and enforce `PaymentIntent` object's invariants.
467+
468+
First, the user creates a new `PaymentIntent` and then sends the charge request. The charge request queries
469+
the created `PaymentIntent`, checks that it's in the `CREATED` state, applies the semantic lock by
470+
changing the state to `CHARGE_REQUESTED`, and writes the `PaymentIntentChargeRequested` event to the database.
471+
The database writes are wrapped in an atomic DynamoDB transaction.
472+
473+
While the DynamoDB transaction was committing, the user sent a new request to change the `PaymentIntent` amount.
474+
Since the previous transaction hasn't been committed yet, the `PaymentIntent` is still in the `CHARGED` state,
475+
which allows changing the amount. While the application issued another DynamoDB transaction to change the `PaymentIntent` amount,
476+
the first transaction that requested the charge was committed, incrementing optimistic lock's version number by one.
477+
Therefore, the second transaction that changes the amount fails because its version number (`0`)
478+
doesn't match with the current version stored in the database (`1`).
479+
The user can retry changing the amount, but the application will reject the request because
480+
the `PaymentIntent` is now in the `CHARGE_REQUESTED` state that prevents changing the amount.
481+
456482
```mermaid
457483
sequenceDiagram
458484
actor User
@@ -508,15 +534,6 @@ sequenceDiagram
508534
deactivate PaymentIntent
509535
```
510536

511-
TODO
512-
513-
- [ ] Incrementing version number
514-
- [ ] Semantic lock & transactional outbox for making charge request
515-
- [ ] When using optimistic locking, your business operation must be encapsulated in the unit of work
516-
- [ ] When using optimistic locking, the request must not issue any destructive operations outside of the
517-
transaction (unit of work), for example, making the charge request to the external Payment Gateway
518-
- [ ] Provides monotonic updates
519-
520537
#### Optimistic Locking with DynamoDB
521538

522539
TODO

0 commit comments

Comments
 (0)