System Design
Event-Driven Architecture
Event-Driven Architecture (EDA) — tizim komponentlari event’lar orqali muloqot qiladi.
Traditional vs Event-Driven
Traditional (Synchronous)
// Order Service
async function createOrder(order) {
await db.saveOrder(order);
await inventoryService.decreaseStock(order.items); // Tight coupling
await emailService.sendReceipt(order.email);
await analyticsService.track(order);
}
Tight coupling: Har bir service to’g’ridan-to’g’ri chaqiriladi
Event-Driven (Asynchronous)
// Order Service
async function createOrder(order) {
await db.saveOrder(order);
await eventBus.publish('order.created', order); // Loose coupling
}
// Other services listen
inventoryService.on('order.created', decreaseStock);
emailService.on('order.created', sendReceipt);
analyticsService.on('order.created', track);
Loose coupling: Services mustaqil
Benefits
1. Scalability
Order Service: 1 instance
Email Service: 10 instances (slow)
Analytics: 5 instances
Each scales independently
2. Reliability
Email Service down → Order still processed
Event queued → Email sent when service recovers
3. Extensibility
// Add new feature - no code change in Order Service
notificationService.on('order.created', sendPushNotification);
Event Types
1. Domain Events
// Business events
'user.registered'
'order.placed'
'payment.completed'
'product.outOfStock'
2. System Events
// Technical events
'cache.invalidated'
'database.slow'
'service.healthCheck'
3. Integration Events
// External system events
'stripe.payment.succeeded'
'aws.s3.object.created'
Event Structure
{
id: 'evt_123abc',
type: 'order.created',
timestamp: '2024-01-15T10:30:00Z',
version: 1,
data: {
orderId: 'ord_456',
userId: 'usr_789',
total: 99.99,
items: [...]
},
metadata: {
correlationId: 'req_xyz',
source: 'order-service-v2'
}
}
Event Sourcing
Store events, not current state:
// Traditional (state-based)
users = { id: 123, balance: 500 }
// Event Sourcing (event-based)
events = [
{ type: 'account.created', balance: 0 },
{ type: 'deposit', amount: 300 },
{ type: 'deposit', amount: 200 }
]
// Rebuild state
balance = events.reduce((sum, e) => sum + (e.amount || 0), 0); // 500
Benefits:
- Full audit trail
- Time travel (replay events)
- Never lose data
CQRS (Command Query Responsibility Segregation)
Commands (Write) Queries (Read)
↓ ↑
Write Model Read Model
↓ ↑
Events ─────────────→
// Write Model (normalized)
commands.createOrder(order) → events.orderCreated
// Read Model (denormalized for queries)
events.orderCreated → updateOrderSummaryView
Best Practices
1. Idempotent event handlers
eventBus.on('order.created', async (event) => {
const processed = await db.exists(`event:${event.id}`);
if (processed) return; // Skip duplicate
await processOrder(event.data);
await db.set(`event:${event.id}`, true);
});
2. Event versioning
// v1
{ type: 'user.created', name: 'Ali' }
// v2 (backward compatible)
{
type: 'user.created',
version: 2,
firstName: 'Ali',
lastName: 'Valiyev' // New field
}
3. Correlation ID
// Track request across services
{
correlationId: 'req_abc123',
causationId: 'evt_xyz456'
}
Xulosa
Event-Driven:
- Loose coupling
- Scalability
- Extensibility
Patterns:
- Event Sourcing
- CQRS
- Pub/Sub
Keyingi dars: Microservices vs Monolith.