پیاده‌سازی سیستم رویدادمحور (Event-Driven) در معماری مایکروسرویس

در سیستم‌های سنتی، سرویس‌ها معمولاً برای انجام یک کار باید مستقیماً با یکدیگر در ارتباط باشند؛ نتیجه‌ی چنین رویکردی، افزایش وابستگی و کاهش انعطاف‌پذیری است. در معماری‌های مدرن، رویکرد رویدادمحور (Event-Driven) راه‌حلی برای کاهش این وابستگی است. در این مدل، هر سرویس هنگام وقوع یک اتفاق، آن را به‌صورت «رویداد» منتشر می‌کند و سایر سرویس‌ها، در صورت نیاز، به آن واکنش نشان می‌دهند — بدون آنکه از جزئیات داخلی یکدیگر اطلاع داشته باشند.

معماری رویدادمحور چیست؟

در این معماری، رویدادها به‌عنوان پیام‌هایی عمل می‌کنند که نشان‌دهنده‌ی وقوع یک اتفاق معنادار در سیستم هستند؛ مثلاً رویداد OrderCreated به معنی ثبت یک سفارش جدید است. سرویس صادرکننده‌ی این رویداد (Producer) آن را در یک واسط ارتباطی مثل Kafka منتشر می‌کند و سرویس‌های دیگر (Consumerها) با دریافت آن، منطق مربوط به خود را اجرا می‌کنند.

جریان داده در معماری رویدادمحور
Order Service  ──►  Kafka (topic: order-events)  ◄──  Inventory Service
                                       ▲
                                       └──────────────  Notification Service

مزایای معماری رویدادمحور

استفاده از الگوی Event-Driven باعث می‌شود سرویس‌ها به‌صورت مستقل کار کنند و توسعه یا تغییر یک سرویس تأثیری بر دیگران نگذارد. همچنین به‌دلیل استفاده از واسط پیام‌رسان، سیستم توانایی مدیریت بار بالا و پردازش همزمان را پیدا می‌کند.

مزیت توضیح
کاهش وابستگی سرویس‌ها فقط از طریق پیام با هم در ارتباط‌اند، نه تماس مستقیم.
مقیاس‌پذیری بالا هر سرویس می‌تواند مستقل از بقیه توسعه یا افزایش ظرفیت پیدا کند.
پایداری بیشتر در صورت بروز خطا، پیام‌ها در صف باقی می‌مانند تا دوباره پردازش شوند.
امکان مانیتورینگ با رصد Topicها می‌توان جریان داده‌ها را به‌صورت زنده دنبال کرد.

الگوهای ارتباطی در سیستم‌های رویدادمحور

  • Event Notification: فقط وقوع یک رویداد اطلاع داده می‌شود (مثلاً سفارش ثبت شد).
  • Event-Carried State Transfer: اطلاعات مورد نیاز همراه رویداد ارسال می‌شود.
  • Event Sourcing: وضعیت سیستم از تاریخچه‌ی رویدادها بازسازی می‌شود.

راه‌اندازی Kafka با Docker

برای اجرای Kafka در محیط توسعه، می‌توانید از پیکربندی ساده‌ی زیر استفاده کنید:

version: '3.8'
services:
  zookeeper:
    image: bitnami/zookeeper:latest
    environment:
      - ALLOW_ANONYMOUS_LOGIN=yes
    ports: ["2181:2181"]

  kafka:
    image: bitnami/kafka:latest
    environment:
      - KAFKA_BROKER_ID=1
      - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181
      - ALLOW_PLAINTEXT_LISTENER=yes
      - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092
    ports: ["9092:9092"]

نوشتن Producer با C#

در این بخش یک Producer ساده می‌سازیم که رویداد OrderCreated را روی Topic مربوطه منتشر کند:

using System.Text.Json;
using Confluent.Kafka;

public record OrderCreatedEvent(Guid OrderId, int CustomerId, decimal Total);

public class OrderProducer
{
    private readonly IProducer<string, string> _producer;

    public OrderProducer(string bootstrap = "localhost:9092")
    {
        var config = new ProducerConfig { BootstrapServers = bootstrap };
        _producer = new ProducerBuilder<string, string>(config).Build();
    }

    public async Task PublishAsync(OrderCreatedEvent evt)
    {
        var message = new Message<string, string>
        {
            Key = evt.OrderId.ToString(),
            Value = JsonSerializer.Serialize(evt)
        };

        await _producer.ProduceAsync("order-events", message);
        Console.WriteLine($"Event sent for order {evt.OrderId}");
    }
}

نوشتن Consumer با C#

در این قسمت مصرف‌کننده‌ای پیاده‌سازی می‌کنیم که پیام‌های مربوط به ثبت سفارش را دریافت کرده و مثلاً عملیات کاهش موجودی را انجام دهد:

using System.Text.Json;
using Confluent.Kafka;

public class InventoryConsumer
{
    public void Listen(string bootstrap = "localhost:9092")
    {
        var config = new ConsumerConfig
        {
            GroupId = "inventory-group",
            BootstrapServers = bootstrap,
            AutoOffsetReset = AutoOffsetReset.Earliest
        };

        using var consumer = new ConsumerBuilder<string, string>(config).Build();
        consumer.Subscribe("order-events");

        while (true)
        {
            var cr = consumer.Consume();
            var evt = JsonSerializer.Deserialize<OrderCreatedEvent>(cr.Message.Value);
            Console.WriteLine($"Order received: {evt?.OrderId}");
        }
    }
}

الگوهای پیشرفته در سیستم‌های رویدادمحور

  • Saga Pattern: برای هماهنگی بین چند سرویس در تراکنش‌های توزیع‌شده.
  • Outbox Pattern: برای اطمینان از ارسال رویداد هم‌زمان با ثبت تراکنش در دیتابیس.
  • Event Replay: برای بازسازی وضعیت سیستم از روی تاریخچه‌ی رویدادها.
  • Dead Letter Queue: برای نگهداری پیام‌هایی که قابل پردازش نیستند.

جمع‌بندی

در این مقاله دیدیم که معماری رویدادمحور چطور می‌تواند وابستگی سرویس‌ها را کاهش دهد و همزمان پایداری و مقیاس‌پذیری سیستم را افزایش دهد. در ادامه، در مقاله بعدی به سراغ الگوی Outbox خواهیم رفت تا نحوه‌ی ارسال مطمئن رویدادها در کنار تراکنش‌های پایگاه داده را بررسی کنیم.