Xây dựng Event-Driven Architecture trong .NET 8 với DDD & Saga Orchestration (MassTransit)
1. Tại sao cần Event-Driven Architecture (EDA)?
Trong các hệ thống Microservices, việc quản lý giao tiếp giữa các service và xử lý giao dịch phân tán là thách thức lớn.
Event-Driven Architecture (EDA) mang lại các lợi ích chính:
Dễ dàng thêm tính năng mới mà không làm ảnh hưởng hệ thống cũ.
Giảm sự phụ thuộc (Decoupling) giữa các service.
Khả năng mở rộng cao (Scalability) vì event xử lý bất đồng bộ.
Đáp ứng tốt giao dịch phức tạp với Saga Pattern.
Xử lý rollback khi có lỗi.
2. Kiến trúc tổng thể (High-Level)
Các thành phần chính:
- Order Service: Tạo đơn hàng.
- Billing Service: Xử lý hóa đơn/thanh toán.
- Notification Service: Gửi thông báo.
- Saga Orchestrator: Điều phối workflow Saga.
- Event Bus: RabbitMQ sử dụng MassTransit.
Sơ đồ Diagram:

Luồng chính:
- OrderCreated Event được publish khi có đơn hàng mới.
- Saga Orchestrator nhận event, gửi CreateInvoiceCommand tới Billing Service.
- Billing Service trả về InvoiceCreated → Saga tiếp tục gửi SendNotificationCommand.
- Nếu bất kỳ bước nào thất bại → Saga publish OrderCompensation để rollback.
3. Cài đặt với MassTransit & .NET 8
3.1 Định nghĩa Event & Command
public record OrderCreated(Guid OrderId);
public record CreateInvoiceCommand(Guid OrderId);
public record InvoiceCreated(Guid OrderId);
public record NotificationSent(Guid OrderId);
public record OrderCompensation(Guid OrderId);
3.2 Saga State Machine
public class OrderState : SagaStateMachineInstance
{
public Guid CorrelationId { get; set; }
public string CurrentState { get; set; }
}
public class OrderStateMachine : MassTransitStateMachine<OrderState>
{
public State WaitingForInvoice { get; private set; }
public State WaitingForNotification { get; private set; }
public Event<OrderCreated> OrderCreated { get; private set; }
public Event<InvoiceCreated> InvoiceCreated { get; private set; }
public OrderStateMachine()
{
InstanceState(x => x.CurrentState);
Initially(
When(OrderCreated)
.Send(context => new CreateInvoiceCommand(context.Data.OrderId))
.TransitionTo(WaitingForInvoice)
);
During(WaitingForInvoice,
When(InvoiceCreated)
.Send(context => new SendNotificationCommand(context.Data.OrderId))
.TransitionTo(WaitingForNotification)
);
SetCompletedWhenFinalized();
}
}
3.3 Cấu hình MassTransit + RabbitMQ
builder.Services.AddMassTransit(x =>
{
x.AddSagaStateMachine<OrderStateMachine, OrderState>()
.InMemoryRepository();
x.UsingRabbitMq((context, cfg) =>
{
cfg.Host("rabbitmq://localhost");
cfg.ConfigureEndpoints(context);
});
});
4. Triển khai các Service
4.1 Order Service (API)
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly IPublishEndpoint _publish;
public OrdersController(IPublishEndpoint publish)
{
_publish = publish;
}
[HttpPost]
public async Task<IActionResult> CreateOrder()
{
var orderId = Guid.NewGuid();
await _publish.Publish(new OrderCreated(orderId));
return Ok(new { OrderId = orderId });
}
}
4.2 Billing Service (Consumer)
public class CreateInvoiceConsumer : IConsumer<CreateInvoiceCommand>
{
private readonly IPublishEndpoint _publish;
public CreateInvoiceConsumer(IPublishEndpoint publish)
{
_publish = publish;
}
public async Task Consume(ConsumeContext<CreateInvoiceCommand> context)
{
Console.WriteLine($"Creating invoice for order {context.Message.OrderId}");
await _publish.Publish(new InvoiceCreated(context.Message.OrderId));
}
}
4.3 Notification Service (Consumer)
public class SendNotificationConsumer : IConsumer<SendNotificationCommand>
{
public Task Consume(ConsumeContext<SendNotificationCommand> context)
{
Console.WriteLine($"Notification sent for order {context.Message.OrderId}");
return Task.CompletedTask;
}
}
5. Best Practices triển khai
Sử dụng Observability (OpenTelemetry) để trace luồng event.
Outbox Pattern + Idempotent Consumer để tránh gửi trùng event.
Dùng Dead Letter Queue (DLQ) để xử lý event lỗi.
Phân biệt Domain Event và Integration Event.
6. Kết luận
Với Event-Driven Architecture + DDD + Saga Orchestration, bạn có thể xây dựng hệ thống .NET 8:
- Hạn chế coupling và cải thiện khả năng phục hồi.
- Dễ dàng mở rộng hoặc thêm bước mới vào workflow.
- Đảm bảo transaction phân tán được điều phối chính xác.
- Dữ liệu nhất quán, phù hợp với hệ thống Microservices phức tạp.