@mytechtoday/augment-extensions 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +35 -3
- package/README.md +3 -3
- package/augment-extensions/domain-rules/software-architecture/README.md +143 -0
- package/augment-extensions/domain-rules/software-architecture/examples/banking-layered.md +961 -0
- package/augment-extensions/domain-rules/software-architecture/examples/ecommerce-microservices.md +990 -0
- package/augment-extensions/domain-rules/software-architecture/examples/iot-eventdriven.md +882 -0
- package/augment-extensions/domain-rules/software-architecture/examples/monolith-to-microservices-migration.md +703 -0
- package/augment-extensions/domain-rules/software-architecture/examples/serverless-imageprocessing.md +957 -0
- package/augment-extensions/domain-rules/software-architecture/examples/trading-eventdriven.md +747 -0
- package/augment-extensions/domain-rules/software-architecture/module.json +119 -0
- package/augment-extensions/domain-rules/software-architecture/rules/challenges-solutions.md +763 -0
- package/augment-extensions/domain-rules/software-architecture/rules/definitions-terminology.md +409 -0
- package/augment-extensions/domain-rules/software-architecture/rules/design-principles.md +684 -0
- package/augment-extensions/domain-rules/software-architecture/rules/evaluation-testing.md +1381 -0
- package/augment-extensions/domain-rules/software-architecture/rules/event-driven-architecture.md +616 -0
- package/augment-extensions/domain-rules/software-architecture/rules/fundamentals.md +306 -0
- package/augment-extensions/domain-rules/software-architecture/rules/industry-architectures.md +554 -0
- package/augment-extensions/domain-rules/software-architecture/rules/layered-architecture.md +776 -0
- package/augment-extensions/domain-rules/software-architecture/rules/microservices-architecture.md +503 -0
- package/augment-extensions/domain-rules/software-architecture/rules/modeling-documentation.md +1199 -0
- package/augment-extensions/domain-rules/software-architecture/rules/monolithic-architecture.md +351 -0
- package/augment-extensions/domain-rules/software-architecture/rules/principles.md +556 -0
- package/augment-extensions/domain-rules/software-architecture/rules/quality-attributes.md +797 -0
- package/augment-extensions/domain-rules/software-architecture/rules/scalability-performance.md +1345 -0
- package/augment-extensions/domain-rules/software-architecture/rules/security-architecture.md +1039 -0
- package/augment-extensions/domain-rules/software-architecture/rules/serverless-architecture.md +711 -0
- package/augment-extensions/domain-rules/software-architecture/rules/skills-development.md +568 -0
- package/augment-extensions/domain-rules/software-architecture/rules/tools-methodologies.md +961 -0
- package/augment-extensions/workflows/beads/examples/complete-workflow-example.md +8 -8
- package/augment-extensions/workflows/beads/rules/best-practices.md +2 -2
- package/augment-extensions/workflows/beads/rules/file-format.md +4 -4
- package/augment-extensions/workflows/beads/rules/manual-setup.md +4 -4
- package/augment-extensions/workflows/beads/rules/workflow.md +3 -3
- package/modules.md +40 -3
- package/package.json +1 -1
package/augment-extensions/domain-rules/software-architecture/rules/event-driven-architecture.md
ADDED
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
# Event-Driven Architecture
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document covers event-driven architecture patterns, message queues, event sourcing, and best practices for building reactive, loosely-coupled systems.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Knowledge
|
|
10
|
+
|
|
11
|
+
### What is Event-Driven Architecture?
|
|
12
|
+
|
|
13
|
+
**Definition**
|
|
14
|
+
- Architecture where components communicate through events
|
|
15
|
+
- Events represent state changes or significant occurrences
|
|
16
|
+
- Producers emit events without knowing consumers
|
|
17
|
+
- Consumers react to events independently
|
|
18
|
+
- Asynchronous, loosely-coupled communication
|
|
19
|
+
|
|
20
|
+
**Core Concepts**
|
|
21
|
+
|
|
22
|
+
**Event**
|
|
23
|
+
- Immutable record of something that happened
|
|
24
|
+
- Contains: event type, timestamp, payload
|
|
25
|
+
- Examples: OrderPlaced, UserRegistered, PaymentProcessed
|
|
26
|
+
|
|
27
|
+
**Event Producer**
|
|
28
|
+
- Component that detects and publishes events
|
|
29
|
+
- Doesn't know who consumes events
|
|
30
|
+
- Fire-and-forget pattern
|
|
31
|
+
|
|
32
|
+
**Event Consumer**
|
|
33
|
+
- Component that subscribes to and processes events
|
|
34
|
+
- Can have multiple consumers per event
|
|
35
|
+
- Processes events independently
|
|
36
|
+
|
|
37
|
+
**Event Channel**
|
|
38
|
+
- Medium for transmitting events
|
|
39
|
+
- Examples: message queue, event stream, event bus
|
|
40
|
+
|
|
41
|
+
### Event Types
|
|
42
|
+
|
|
43
|
+
**Domain Events**
|
|
44
|
+
- Business-significant occurrences
|
|
45
|
+
- Examples: OrderPlaced, CustomerRegistered
|
|
46
|
+
- Trigger business workflows
|
|
47
|
+
|
|
48
|
+
**Integration Events**
|
|
49
|
+
- Cross-system communication
|
|
50
|
+
- Examples: OrderShipped (from warehouse system)
|
|
51
|
+
- Enable system integration
|
|
52
|
+
|
|
53
|
+
**System Events**
|
|
54
|
+
- Technical occurrences
|
|
55
|
+
- Examples: ServiceStarted, CacheCleared
|
|
56
|
+
- Support operations and monitoring
|
|
57
|
+
|
|
58
|
+
### When to Use Event-Driven Architecture
|
|
59
|
+
|
|
60
|
+
**Good Fit**
|
|
61
|
+
- Complex workflows with multiple steps
|
|
62
|
+
- Need for loose coupling
|
|
63
|
+
- Asynchronous processing requirements
|
|
64
|
+
- Real-time data processing
|
|
65
|
+
- Microservices communication
|
|
66
|
+
- Integration with external systems
|
|
67
|
+
|
|
68
|
+
**Poor Fit**
|
|
69
|
+
- Simple CRUD applications
|
|
70
|
+
- Strong consistency requirements
|
|
71
|
+
- Synchronous request-response needed
|
|
72
|
+
- Simple linear workflows
|
|
73
|
+
- Limited scalability needs
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Skills
|
|
78
|
+
|
|
79
|
+
### Message Queue Patterns
|
|
80
|
+
|
|
81
|
+
**Point-to-Point (Queue)**
|
|
82
|
+
```
|
|
83
|
+
Producer → Queue → Consumer
|
|
84
|
+
```
|
|
85
|
+
- One message, one consumer
|
|
86
|
+
- Message removed after consumption
|
|
87
|
+
- Load balancing across consumers
|
|
88
|
+
- Use for: task distribution, work queues
|
|
89
|
+
|
|
90
|
+
**Publish-Subscribe (Topic)**
|
|
91
|
+
```
|
|
92
|
+
Producer → Topic → [Consumer A, Consumer B, Consumer C]
|
|
93
|
+
```
|
|
94
|
+
- One message, multiple consumers
|
|
95
|
+
- Each subscriber gets copy
|
|
96
|
+
- Broadcast pattern
|
|
97
|
+
- Use for: notifications, event broadcasting
|
|
98
|
+
|
|
99
|
+
**Request-Reply**
|
|
100
|
+
```
|
|
101
|
+
Client → Request Queue → Server
|
|
102
|
+
Client ← Reply Queue ← Server
|
|
103
|
+
```
|
|
104
|
+
- Asynchronous request-response
|
|
105
|
+
- Correlation ID links request/reply
|
|
106
|
+
- Use for: async RPC, long-running operations
|
|
107
|
+
|
|
108
|
+
### Event Streaming
|
|
109
|
+
|
|
110
|
+
**Event Stream Characteristics**
|
|
111
|
+
- Ordered sequence of events
|
|
112
|
+
- Events persisted (not deleted after consumption)
|
|
113
|
+
- Multiple consumers can read independently
|
|
114
|
+
- Replay capability
|
|
115
|
+
- Examples: Apache Kafka, AWS Kinesis
|
|
116
|
+
|
|
117
|
+
**Stream Processing**
|
|
118
|
+
- Real-time event processing
|
|
119
|
+
- Stateful computations
|
|
120
|
+
- Windowing and aggregation
|
|
121
|
+
- Complex event processing
|
|
122
|
+
|
|
123
|
+
**Kafka Example**
|
|
124
|
+
```
|
|
125
|
+
Topic: orders
|
|
126
|
+
├── Partition 0: [Event1, Event2, Event3]
|
|
127
|
+
├── Partition 1: [Event4, Event5, Event6]
|
|
128
|
+
└── Partition 2: [Event7, Event8, Event9]
|
|
129
|
+
|
|
130
|
+
Consumer Group A: [Consumer1, Consumer2]
|
|
131
|
+
Consumer Group B: [Consumer3]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Event Sourcing
|
|
135
|
+
|
|
136
|
+
**Concept**
|
|
137
|
+
- Store events, not current state
|
|
138
|
+
- State is derived from event history
|
|
139
|
+
- Complete audit trail
|
|
140
|
+
- Time travel (replay to any point)
|
|
141
|
+
|
|
142
|
+
**Event Store**
|
|
143
|
+
- Append-only log of events
|
|
144
|
+
- Events are immutable
|
|
145
|
+
- Optimized for writes
|
|
146
|
+
- Examples: EventStore, Kafka, custom DB
|
|
147
|
+
|
|
148
|
+
**Event Sourcing Pattern**
|
|
149
|
+
```
|
|
150
|
+
Command → Aggregate → Events → Event Store
|
|
151
|
+
↓
|
|
152
|
+
Event Handler → Read Model
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Aggregate Example**
|
|
156
|
+
```java
|
|
157
|
+
public class Order {
|
|
158
|
+
private String id;
|
|
159
|
+
private OrderStatus status;
|
|
160
|
+
private List<OrderItem> items;
|
|
161
|
+
private List<DomainEvent> uncommittedEvents = new ArrayList<>();
|
|
162
|
+
|
|
163
|
+
// Command handler
|
|
164
|
+
public void placeOrder(List<OrderItem> items) {
|
|
165
|
+
// Validate
|
|
166
|
+
if (items.isEmpty()) {
|
|
167
|
+
throw new IllegalArgumentException("Order must have items");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Apply event
|
|
171
|
+
apply(new OrderPlacedEvent(this.id, items, Instant.now()));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Event handler
|
|
175
|
+
private void apply(OrderPlacedEvent event) {
|
|
176
|
+
this.items = event.getItems();
|
|
177
|
+
this.status = OrderStatus.PLACED;
|
|
178
|
+
uncommittedEvents.add(event);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Reconstitute from events
|
|
182
|
+
public static Order fromEvents(List<DomainEvent> events) {
|
|
183
|
+
Order order = new Order();
|
|
184
|
+
events.forEach(order::apply);
|
|
185
|
+
return order;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### CQRS (Command Query Responsibility Segregation)
|
|
191
|
+
|
|
192
|
+
**Concept**
|
|
193
|
+
- Separate read and write models
|
|
194
|
+
- Commands: change state
|
|
195
|
+
- Queries: read state
|
|
196
|
+
- Often combined with event sourcing
|
|
197
|
+
|
|
198
|
+
**Architecture**
|
|
199
|
+
```
|
|
200
|
+
Command → Write Model → Events → Event Store
|
|
201
|
+
↓
|
|
202
|
+
Event Handler
|
|
203
|
+
↓
|
|
204
|
+
Query ← Read Model ← Projection ← Events
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Benefits**
|
|
208
|
+
- Optimize read and write independently
|
|
209
|
+
- Scale read and write separately
|
|
210
|
+
- Different data models for different needs
|
|
211
|
+
- Simplified queries
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Examples
|
|
216
|
+
|
|
217
|
+
### Order Processing with Events
|
|
218
|
+
|
|
219
|
+
**Event Definitions**
|
|
220
|
+
```java
|
|
221
|
+
// Base event
|
|
222
|
+
public abstract class DomainEvent {
|
|
223
|
+
private final String eventId;
|
|
224
|
+
private final Instant timestamp;
|
|
225
|
+
|
|
226
|
+
protected DomainEvent() {
|
|
227
|
+
this.eventId = UUID.randomUUID().toString();
|
|
228
|
+
this.timestamp = Instant.now();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Order events
|
|
233
|
+
public class OrderPlacedEvent extends DomainEvent {
|
|
234
|
+
private final String orderId;
|
|
235
|
+
private final String customerId;
|
|
236
|
+
private final List<OrderItem> items;
|
|
237
|
+
private final BigDecimal total;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
public class OrderPaidEvent extends DomainEvent {
|
|
241
|
+
private final String orderId;
|
|
242
|
+
private final String paymentId;
|
|
243
|
+
private final BigDecimal amount;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
public class OrderShippedEvent extends DomainEvent {
|
|
247
|
+
private final String orderId;
|
|
248
|
+
private final String trackingNumber;
|
|
249
|
+
private final Instant shippedAt;
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Event Publisher**
|
|
254
|
+
```java
|
|
255
|
+
@Service
|
|
256
|
+
public class OrderService {
|
|
257
|
+
private final OrderRepository orderRepository;
|
|
258
|
+
private final EventPublisher eventPublisher;
|
|
259
|
+
|
|
260
|
+
@Transactional
|
|
261
|
+
public Order placeOrder(PlaceOrderCommand command) {
|
|
262
|
+
// Create order
|
|
263
|
+
Order order = new Order(command.getCustomerId());
|
|
264
|
+
order.addItems(command.getItems());
|
|
265
|
+
|
|
266
|
+
// Save order
|
|
267
|
+
order = orderRepository.save(order);
|
|
268
|
+
|
|
269
|
+
// Publish event
|
|
270
|
+
OrderPlacedEvent event = new OrderPlacedEvent(
|
|
271
|
+
order.getId(),
|
|
272
|
+
order.getCustomerId(),
|
|
273
|
+
order.getItems(),
|
|
274
|
+
order.getTotal()
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
eventPublisher.publish("orders", event);
|
|
278
|
+
|
|
279
|
+
return order;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
@Component
|
|
284
|
+
public class KafkaEventPublisher implements EventPublisher {
|
|
285
|
+
private final KafkaTemplate<String, DomainEvent> kafkaTemplate;
|
|
286
|
+
|
|
287
|
+
@Override
|
|
288
|
+
public void publish(String topic, DomainEvent event) {
|
|
289
|
+
kafkaTemplate.send(topic, event.getEventId(), event);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**Event Consumers**
|
|
295
|
+
```java
|
|
296
|
+
// Inventory Service - Reserve items
|
|
297
|
+
@Component
|
|
298
|
+
public class InventoryEventHandler {
|
|
299
|
+
private final InventoryService inventoryService;
|
|
300
|
+
|
|
301
|
+
@KafkaListener(topics = "orders", groupId = "inventory-service")
|
|
302
|
+
public void handleOrderPlaced(OrderPlacedEvent event) {
|
|
303
|
+
inventoryService.reserveItems(
|
|
304
|
+
event.getOrderId(),
|
|
305
|
+
event.getItems()
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Notification Service - Send confirmation
|
|
311
|
+
@Component
|
|
312
|
+
public class NotificationEventHandler {
|
|
313
|
+
private final EmailService emailService;
|
|
314
|
+
|
|
315
|
+
@KafkaListener(topics = "orders", groupId = "notification-service")
|
|
316
|
+
public void handleOrderPlaced(OrderPlacedEvent event) {
|
|
317
|
+
emailService.sendOrderConfirmation(
|
|
318
|
+
event.getCustomerId(),
|
|
319
|
+
event.getOrderId()
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Analytics Service - Track metrics
|
|
325
|
+
@Component
|
|
326
|
+
public class AnalyticsEventHandler {
|
|
327
|
+
private final AnalyticsService analyticsService;
|
|
328
|
+
|
|
329
|
+
@KafkaListener(topics = "orders", groupId = "analytics-service")
|
|
330
|
+
public void handleOrderPlaced(OrderPlacedEvent event) {
|
|
331
|
+
analyticsService.recordOrderPlaced(
|
|
332
|
+
event.getOrderId(),
|
|
333
|
+
event.getTotal(),
|
|
334
|
+
event.getTimestamp()
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Event Sourcing Implementation
|
|
341
|
+
|
|
342
|
+
**Event Store**
|
|
343
|
+
```java
|
|
344
|
+
@Repository
|
|
345
|
+
public class EventStore {
|
|
346
|
+
private final JdbcTemplate jdbcTemplate;
|
|
347
|
+
|
|
348
|
+
public void saveEvents(String aggregateId, List<DomainEvent> events) {
|
|
349
|
+
String sql = """
|
|
350
|
+
INSERT INTO events (aggregate_id, event_type, event_data, version, timestamp)
|
|
351
|
+
VALUES (?, ?, ?::jsonb, ?, ?)
|
|
352
|
+
""";
|
|
353
|
+
|
|
354
|
+
for (DomainEvent event : events) {
|
|
355
|
+
jdbcTemplate.update(
|
|
356
|
+
sql,
|
|
357
|
+
aggregateId,
|
|
358
|
+
event.getClass().getSimpleName(),
|
|
359
|
+
toJson(event),
|
|
360
|
+
event.getVersion(),
|
|
361
|
+
event.getTimestamp()
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
public List<DomainEvent> getEvents(String aggregateId) {
|
|
367
|
+
String sql = """
|
|
368
|
+
SELECT event_type, event_data
|
|
369
|
+
FROM events
|
|
370
|
+
WHERE aggregate_id = ?
|
|
371
|
+
ORDER BY version ASC
|
|
372
|
+
""";
|
|
373
|
+
|
|
374
|
+
return jdbcTemplate.query(sql, (rs, rowNum) -> {
|
|
375
|
+
String eventType = rs.getString("event_type");
|
|
376
|
+
String eventData = rs.getString("event_data");
|
|
377
|
+
return fromJson(eventType, eventData);
|
|
378
|
+
}, aggregateId);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
**Aggregate with Event Sourcing**
|
|
384
|
+
```java
|
|
385
|
+
public class Order {
|
|
386
|
+
private String id;
|
|
387
|
+
private String customerId;
|
|
388
|
+
private List<OrderItem> items = new ArrayList<>();
|
|
389
|
+
private OrderStatus status;
|
|
390
|
+
private int version = 0;
|
|
391
|
+
private List<DomainEvent> uncommittedEvents = new ArrayList<>();
|
|
392
|
+
|
|
393
|
+
// Command: Place order
|
|
394
|
+
public void placeOrder(String customerId, List<OrderItem> items) {
|
|
395
|
+
if (this.status != null) {
|
|
396
|
+
throw new IllegalStateException("Order already placed");
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
apply(new OrderPlacedEvent(this.id, customerId, items));
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Command: Pay for order
|
|
403
|
+
public void pay(String paymentId, BigDecimal amount) {
|
|
404
|
+
if (this.status != OrderStatus.PLACED) {
|
|
405
|
+
throw new IllegalStateException("Order not in PLACED status");
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
apply(new OrderPaidEvent(this.id, paymentId, amount));
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Event handlers
|
|
412
|
+
private void apply(OrderPlacedEvent event) {
|
|
413
|
+
this.customerId = event.getCustomerId();
|
|
414
|
+
this.items = new ArrayList<>(event.getItems());
|
|
415
|
+
this.status = OrderStatus.PLACED;
|
|
416
|
+
this.version++;
|
|
417
|
+
uncommittedEvents.add(event);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
private void apply(OrderPaidEvent event) {
|
|
421
|
+
this.status = OrderStatus.PAID;
|
|
422
|
+
this.version++;
|
|
423
|
+
uncommittedEvents.add(event);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Reconstitute from events
|
|
427
|
+
public static Order fromEvents(String id, List<DomainEvent> events) {
|
|
428
|
+
Order order = new Order();
|
|
429
|
+
order.id = id;
|
|
430
|
+
|
|
431
|
+
for (DomainEvent event : events) {
|
|
432
|
+
if (event instanceof OrderPlacedEvent e) {
|
|
433
|
+
order.apply(e);
|
|
434
|
+
} else if (event instanceof OrderPaidEvent e) {
|
|
435
|
+
order.apply(e);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
order.uncommittedEvents.clear();
|
|
440
|
+
return order;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
public List<DomainEvent> getUncommittedEvents() {
|
|
444
|
+
return new ArrayList<>(uncommittedEvents);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
public void markEventsAsCommitted() {
|
|
448
|
+
uncommittedEvents.clear();
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Saga Pattern for Distributed Transactions
|
|
454
|
+
|
|
455
|
+
**Choreography-Based Saga**
|
|
456
|
+
```java
|
|
457
|
+
// Order Service
|
|
458
|
+
@Component
|
|
459
|
+
public class OrderSagaHandler {
|
|
460
|
+
private final OrderRepository orderRepository;
|
|
461
|
+
private final EventPublisher eventPublisher;
|
|
462
|
+
|
|
463
|
+
@KafkaListener(topics = "payments")
|
|
464
|
+
public void handlePaymentProcessed(PaymentProcessedEvent event) {
|
|
465
|
+
Order order = orderRepository.findById(event.getOrderId());
|
|
466
|
+
order.markAsPaid();
|
|
467
|
+
orderRepository.save(order);
|
|
468
|
+
|
|
469
|
+
// Trigger next step
|
|
470
|
+
eventPublisher.publish("shipments",
|
|
471
|
+
new ShipOrderEvent(order.getId())
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
@KafkaListener(topics = "payments")
|
|
476
|
+
public void handlePaymentFailed(PaymentFailedEvent event) {
|
|
477
|
+
Order order = orderRepository.findById(event.getOrderId());
|
|
478
|
+
order.cancel();
|
|
479
|
+
orderRepository.save(order);
|
|
480
|
+
|
|
481
|
+
// Compensate: Release inventory
|
|
482
|
+
eventPublisher.publish("inventory",
|
|
483
|
+
new ReleaseInventoryEvent(order.getId())
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
491
|
+
## Understanding
|
|
492
|
+
|
|
493
|
+
### Advantages of Event-Driven Architecture
|
|
494
|
+
|
|
495
|
+
**Loose Coupling**
|
|
496
|
+
- Producers don't know consumers
|
|
497
|
+
- Add/remove consumers without affecting producers
|
|
498
|
+
- Independent evolution
|
|
499
|
+
- Easier testing
|
|
500
|
+
|
|
501
|
+
**Scalability**
|
|
502
|
+
- Asynchronous processing
|
|
503
|
+
- Load leveling with queues
|
|
504
|
+
- Independent scaling of consumers
|
|
505
|
+
- Parallel processing
|
|
506
|
+
|
|
507
|
+
**Resilience**
|
|
508
|
+
- Failure isolation
|
|
509
|
+
- Retry mechanisms
|
|
510
|
+
- Dead letter queues
|
|
511
|
+
- Graceful degradation
|
|
512
|
+
|
|
513
|
+
**Flexibility**
|
|
514
|
+
- Easy to add new features (new consumers)
|
|
515
|
+
- Support multiple workflows
|
|
516
|
+
- Real-time and batch processing
|
|
517
|
+
- Event replay for recovery
|
|
518
|
+
|
|
519
|
+
**Auditability**
|
|
520
|
+
- Complete event history
|
|
521
|
+
- Audit trail
|
|
522
|
+
- Debugging and troubleshooting
|
|
523
|
+
- Compliance and reporting
|
|
524
|
+
|
|
525
|
+
### Challenges and Disadvantages
|
|
526
|
+
|
|
527
|
+
**Complexity**
|
|
528
|
+
- Distributed system challenges
|
|
529
|
+
- Eventual consistency
|
|
530
|
+
- Event ordering issues
|
|
531
|
+
- Debugging difficulties
|
|
532
|
+
|
|
533
|
+
**Event Schema Evolution**
|
|
534
|
+
- Backward compatibility needed
|
|
535
|
+
- Versioning strategy required
|
|
536
|
+
- Consumer updates coordination
|
|
537
|
+
- Schema registry management
|
|
538
|
+
|
|
539
|
+
**Duplicate Events**
|
|
540
|
+
- At-least-once delivery
|
|
541
|
+
- Idempotent consumers required
|
|
542
|
+
- Deduplication logic
|
|
543
|
+
- Event ID tracking
|
|
544
|
+
|
|
545
|
+
**Event Ordering**
|
|
546
|
+
- No global ordering (distributed systems)
|
|
547
|
+
- Partition-level ordering only
|
|
548
|
+
- Out-of-order events
|
|
549
|
+
- Causality tracking needed
|
|
550
|
+
|
|
551
|
+
**Operational Overhead**
|
|
552
|
+
- Message broker management
|
|
553
|
+
- Monitoring and alerting
|
|
554
|
+
- Dead letter queue handling
|
|
555
|
+
- Event store maintenance
|
|
556
|
+
|
|
557
|
+
### Best Practices
|
|
558
|
+
|
|
559
|
+
**Event Design**
|
|
560
|
+
- Events are immutable
|
|
561
|
+
- Include all necessary data (avoid lookups)
|
|
562
|
+
- Use meaningful event names (past tense)
|
|
563
|
+
- Version events from the start
|
|
564
|
+
- Include correlation IDs
|
|
565
|
+
|
|
566
|
+
**Consumer Design**
|
|
567
|
+
- Idempotent event handlers
|
|
568
|
+
- Handle duplicate events
|
|
569
|
+
- Use dead letter queues
|
|
570
|
+
- Implement retry with backoff
|
|
571
|
+
- Monitor consumer lag
|
|
572
|
+
|
|
573
|
+
**Error Handling**
|
|
574
|
+
- Retry transient failures
|
|
575
|
+
- Dead letter queue for poison messages
|
|
576
|
+
- Compensating transactions
|
|
577
|
+
- Circuit breakers
|
|
578
|
+
- Alerting and monitoring
|
|
579
|
+
|
|
580
|
+
**Event Store**
|
|
581
|
+
- Append-only design
|
|
582
|
+
- Partition by aggregate ID
|
|
583
|
+
- Snapshot for performance
|
|
584
|
+
- Archive old events
|
|
585
|
+
- Backup and recovery
|
|
586
|
+
|
|
587
|
+
**Monitoring**
|
|
588
|
+
- Event throughput
|
|
589
|
+
- Consumer lag
|
|
590
|
+
- Processing time
|
|
591
|
+
- Error rates
|
|
592
|
+
- Dead letter queue size
|
|
593
|
+
|
|
594
|
+
---
|
|
595
|
+
|
|
596
|
+
## References
|
|
597
|
+
|
|
598
|
+
- **Books**
|
|
599
|
+
- "Designing Event-Driven Systems" by Ben Stopford
|
|
600
|
+
- "Enterprise Integration Patterns" by Gregor Hohpe
|
|
601
|
+
- "Implementing Domain-Driven Design" by Vaughn Vernon
|
|
602
|
+
|
|
603
|
+
- **Patterns**
|
|
604
|
+
- Event Sourcing Pattern
|
|
605
|
+
- CQRS Pattern
|
|
606
|
+
- Saga Pattern
|
|
607
|
+
- Outbox Pattern
|
|
608
|
+
- Event Notification Pattern
|
|
609
|
+
|
|
610
|
+
- **Technologies**
|
|
611
|
+
- Message Brokers: Apache Kafka, RabbitMQ, AWS SNS/SQS
|
|
612
|
+
- Event Stores: EventStore, Axon Server
|
|
613
|
+
- Stream Processing: Apache Flink, Kafka Streams
|
|
614
|
+
- Schema Registry: Confluent Schema Registry, AWS Glue
|
|
615
|
+
|
|
616
|
+
|