You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: architectures/event-driven-architecture/implementation-guide.md
+18-7Lines changed: 18 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -90,11 +90,11 @@ class PaymentEventHandler {
90
90
91
91
92
92
### ⚠️ Problem
93
-
[Analysis of the risks]
93
+
Without tracking `eventId` locally, duplicate deliveries from the broker (due to consumer rebalancing, network drops, or retries) result in duplicate side effects. This leads to double-billing customers, duplicate orders, and compromised system integrity.
94
94
95
95
96
96
### 🚀 Solution
97
-
[Architectural justification of the solution]
97
+
Implementing an Idempotent Receiver pattern with a unique constraint on `eventId` ensures that duplicate events are safely ignored. Wrapping the deduplication check and the business logic within an atomic database transaction guarantees state consistency even under high concurrency.
98
98
---
99
99
## 2. The Transactional Outbox Pattern
100
100
@@ -143,11 +143,11 @@ class OrderService {
143
143
144
144
145
145
### ⚠️ Problem
146
-
[Analysis of the risks]
146
+
The Dual-Write Anti-Pattern occurs when an application attempts to update a database and publish a message to a broker sequentially. Without distributed transactions, a failure between the two operations leaves the system in an inconsistent state (e.g., the DB is updated, but the event is never published).
147
147
148
148
149
149
### 🚀 Solution
150
-
[Architectural justification of the solution]
150
+
The Transactional Outbox pattern guarantees atomic operations. By saving the event to a local `outbox` table within the same DB transaction as the domain change, we achieve atomicity. A separate, reliable process (like a Debezium Connector or background poller) then reads the outbox table and guarantees "at-least-once" delivery to the broker.
151
151
---
152
152
## 3. Strictly Typed Schemas (Schema Registry)
153
153
@@ -186,15 +186,26 @@ class OrderKafkaPublisher {
186
186
187
187
188
188
### ❌ Bad Practice
189
-
[Need to fill in example of non-optimal code]
189
+
```typescript
190
+
classOrderKafkaPublisher {
191
+
async publish(event:DomainEvent) {
192
+
// ❌ Publishing raw, untyped JSON objects
193
+
// Downstream consumers might break if 'amount' is suddenly renamed to 'totalAmount'
Publishing unstructured JSON couples microservices dangerously. If the upstream service changes a field name or type, downstream consumers will crash unexpectedly, causing cascading failures and data corruption across the distributed system.
194
205
195
206
196
207
### 🚀 Solution
197
-
[Architectural justification of the solution]
208
+
A Schema Registry enforces a strict, versioned contract (e.g., Avro, Protobuf, JSON Schema) between producers and consumers. It serializes data efficiently, rejects non-compliant payloads at the producer level, and ensures backward/forward compatibility rules are respected across all teams.
Keep the Core Domain completely pure and agnostic of any external frameworks, databases, or UI. Use mapping layers (`Adapters`) to translate the pure Domain Entities to and from database-specific representations (like ORM entities or raw SQL rows). This ensures the business logic remains portable, independently testable, and strictly focused on business rules.
239
+
Isolate the Core Domain from infrastructure. The `User` entity must be a pure TypeScript class devoid of external imports or decorators. The mapping between the database rows and the Domain Entity must be handled strictly by the Secondary Adapter (e.g., via a Mapper or DTO class), ensuring the Core remains clean, testable, and completely vendor-agnostic.
0 commit comments