|
1 | | -# 🔄 Saga Pattern with BPMN Compensation |
| 1 | +# ⏪ Saga Pattern with BPMN Compensation |
2 | 2 |
|
3 | 3 | This example demonstrates how to handle **distributed transaction rollbacks** using the saga pattern with BPMN |
4 | 4 | compensation events. When a later step in a process fails, compensation handlers can reliably undo previously completed |
@@ -49,40 +49,53 @@ non-persistent state: |
49 | 49 |
|
50 | 50 | **Spot Management (Limited Resource):** |
51 | 51 |
|
52 | | -- **[InMemoryNewsletterSpotManager](src/main/kotlin/de/emaarco/example/adapter/out/memory/InMemoryNewsletterSpotManager.kt)** - In-memory manager tracking 50 subscriber spots using `mutableSetOf<Email>` |
53 | | -- Spots are reserved or released (compensated) based on payment outcome |
54 | | -- Demonstrates realistic limited resource constraint |
| 52 | +- [InMemoryNewsletterSpotManager](src/main/kotlin/de/emaarco/example/adapter/out/memory/InMemoryNewsletterSpotManager.kt): |
| 53 | + In-memory manager tracking 50 subscriber spots using `mutableSetOf<Email>`. |
| 54 | +- Spots are reserved or released (compensated) based on payment outcome. Demonstrates realistic limited resource |
| 55 | + constraint. |
55 | 56 |
|
56 | 57 | **Key Services:** |
57 | 58 |
|
58 | | -1. **[SubscribeToPayedNewsletterService](src/main/kotlin/de/emaarco/example/application/service/SubscribeToPayedNewsletterService.kt)** - Entry point that checks spot availability, persists subscription to DB, and publishes message to Zeebe |
59 | | -2. **[ReserveSpotService](src/main/kotlin/de/emaarco/example/application/service/ReserveSpotService.kt)** - Reserves spot in memory (compensatable operation) |
60 | | -3. **[ProcessPaymentService](src/main/kotlin/de/emaarco/example/application/service/ProcessPaymentService.kt)** - Uses `Random.nextBoolean()` to simulate payment success/failure, persists result to DB |
61 | | -4. **[CancelReservationService](src/main/kotlin/de/emaarco/example/application/service/CancelReservationService.kt)** - **Compensation handler** that releases spot back to available pool |
62 | | -5. **[SendWelcomeMailService](src/main/kotlin/de/emaarco/example/application/service/SendWelcomeMailService.kt)** - Logs welcome mail on successful payment |
| 59 | +1. [SubscribeToPayedNewsletterService](src/main/kotlin/de/emaarco/example/application/service/SubscribeToPayedNewsletterService.kt): |
| 60 | + Entry point that checks spot availability, persists subscription to DB, and publishes message to Zeebe |
| 61 | + |
| 62 | +2. [ReserveSpotService](src/main/kotlin/de/emaarco/example/application/service/ReserveSpotService.kt): Reserves spot |
| 63 | + in memory (compensatable operation) |
| 64 | +3. [ProcessPaymentService](src/main/kotlin/de/emaarco/example/application/service/ProcessPaymentService.kt): Uses |
| 65 | + `Random.nextBoolean()` to simulate payment success/failure, persists result to DB |
| 66 | +4. [CancelReservationService](src/main/kotlin/de/emaarco/example/application/service/CancelReservationService.kt): |
| 67 | + Compensation handler that releases spot back to available pool |
| 68 | +5. [SendWelcomeMailService](src/main/kotlin/de/emaarco/example/application/service/SendWelcomeMailService.kt): Logs |
| 69 | + welcome mail on successful payment |
63 | 70 |
|
64 | 71 | **Zeebe Job Workers:** |
65 | 72 |
|
66 | | -- **[ReserveSpotWorker](src/main/kotlin/de/emaarco/example/adapter/in/zeebe/ReserveSpotWorker.kt)** - Handles `newsletter.reserveSpot` job type |
67 | | -- **[ProcessPaymentWorker](src/main/kotlin/de/emaarco/example/adapter/in/zeebe/ProcessPaymentWorker.kt)** - Handles `newsletter.processPayment` job type |
68 | | -- **[CancelReservationWorker](src/main/kotlin/de/emaarco/example/adapter/in/zeebe/CancelReservationWorker.kt)** - Handles `newsletter.cancelSpot` compensation job type, triggered automatically by Zeebe |
69 | | -- **[SendWelcomeMailWorker](src/main/kotlin/de/emaarco/example/adapter/in/zeebe/SendWelcomeMailWorker.kt)** - Handles `newsletter.sendWelcomeMail` job type |
| 73 | +- **[ReserveSpotWorker](src/main/kotlin/de/emaarco/example/adapter/in/zeebe/ReserveSpotWorker.kt)**: Handles |
| 74 | + `newsletter.reserveSpot` job type |
| 75 | +- **[ProcessPaymentWorker](src/main/kotlin/de/emaarco/example/adapter/in/zeebe/ProcessPaymentWorker.kt)**: Handles |
| 76 | + `newsletter.processPayment` job type |
| 77 | +- **[CancelReservationWorker](src/main/kotlin/de/emaarco/example/adapter/in/zeebe/CancelReservationWorker.kt)**: Handles |
| 78 | + `newsletter.cancelSpot` compensation job type, triggered automatically by Zeebe |
| 79 | +- **[SendWelcomeMailWorker](src/main/kotlin/de/emaarco/example/adapter/in/zeebe/SendWelcomeMailWorker.kt)**: Handles |
| 80 | + `newsletter.sendWelcomeMail` job type |
70 | 81 |
|
71 | 82 | **REST API:** |
72 | 83 |
|
73 | | -- **[SubscribeToPayedNewsletterController](src/main/kotlin/de/emaarco/example/adapter/in/rest/SubscribeToPayedNewsletterController.kt)** - Exposes `POST /api/payed-newsletter/subscribe` endpoint |
| 84 | +- [SubscribeToPayedNewsletterController](src/main/kotlin/de/emaarco/example/adapter/in/rest/SubscribeToPayedNewsletterController.kt): |
| 85 | + Exposes `POST /api/payed-newsletter/subscribe` endpoint |
74 | 86 |
|
75 | 87 | **Process Adapter:** |
76 | 88 |
|
77 | | -- **[PayedNewsletterProcessAdapter](src/main/kotlin/de/emaarco/example/adapter/out/zeebe/PayedNewsletterProcessAdapter.kt)** - Publishes `Message_FormSubmitted` to start the process instance |
| 89 | +- [PayedNewsletterProcessAdapter](src/main/kotlin/de/emaarco/example/adapter/out/zeebe/PayedNewsletterProcessAdapter.kt): |
| 90 | + Publishes `Message_FormSubmitted` to start the process instance |
78 | 91 |
|
79 | 92 | **BPMN Configuration:** |
80 | 93 |
|
81 | 94 | The [`payed-newsletter.bpmn`](../../configuration/payed-newsletter.bpmn) model defines the compensation structure: |
82 | 95 |
|
83 | | -- `serviceTask_reserveSpot` - Compensatable activity with boundary compensation event |
84 | | -- `serviceTask_cancelReservation` - Compensation handler marked with `isForCompensation="true"` |
85 | | -- `endEvent_paymentFailed` - End event that triggers compensation via `compensateEventDefinition` |
| 96 | +- `serviceTask_reserveSpot`: Compensatable activity with boundary compensation event |
| 97 | +- `serviceTask_cancelReservation`: Compensation handler marked with `isForCompensation="true"` |
| 98 | +- `endEvent_paymentFailed`: End event that triggers compensation via `compensateEventDefinition` |
86 | 99 |
|
87 | 100 | ## **Sequence Flow** 📊 |
88 | 101 |
|
@@ -187,4 +200,4 @@ rollbacks when failures occur, ensuring consistency across your distributed syst |
187 | 200 |
|
188 | 201 | While more complex than simple database transactions, sagas are essential for building resilient microservice |
189 | 202 | architectures where operations span multiple independent systems that cannot participate in traditional ACID |
190 | | -transactions. |
| 203 | +transactions. |
0 commit comments