EngineeringSenior DeveloperArchitecture and Patterns

09 - Enterprise & Integration Patterns

Patterns cho hệ thống enterprise thực chiến: Repository, Unit of Work, Outbox, CQRS, Event Sourcing, Circuit Breaker, Retry, Saga. Đây là patterns Senior/Architect dùng HÀNG NGÀY.


Repository Pattern

csharp
// Interface (Application layer)
public interface IOrderRepository
{
    Task<Order?> GetByIdAsync(Guid id, CancellationToken ct = default);
    Task<List<Order>> GetByCustomerAsync(Guid customerId, CancellationToken ct = default);
    Task AddAsync(Order order, CancellationToken ct = default);
}

// Implementation (Infrastructure layer)
public class EfOrderRepository(AppDbContext db) : IOrderRepository
{
    public async Task<Order?> GetByIdAsync(Guid id, CancellationToken ct)
        => await db.Orders.Include(o => o.Items).FirstOrDefaultAsync(o => o.Id == id, ct);

    public async Task AddAsync(Order order, CancellationToken ct)
        => await db.Orders.AddAsync(order, ct);
}

Tranh luận: "Cần Repository khi đã có DbContext?"

  • ✅ Cần: khi muốn swap ORM (hiếm), khi cần custom query abstraction, khi cần mock cho test
  • ❌ Không cần: Simple CRUD, DbContext ĐÃ LÀ Repository + Unit of Work

Unit of Work

csharp
// DbContext chính là Unit of Work!
public interface IUnitOfWork
{
    Task<int> CommitAsync(CancellationToken ct = default);
}

// AppDbContext implements UoW
public class AppDbContext : DbContext, IUnitOfWork
{
    public async Task<int> CommitAsync(CancellationToken ct)
        => await SaveChangesAsync(ct);
    // SaveChanges = 1 transaction cho TẤT CẢ changes
}

Outbox Pattern (Reliable Event Publishing)

csharp
// Outbox table
public class OutboxMessage
{
    public Guid Id { get; set; }
    public string Type { get; set; } = "";
    public string Payload { get; set; } = "";
    public DateTime CreatedAt { get; set; }
    public DateTime? PublishedAt { get; set; }
}

// Background Worker
public class OutboxProcessor(AppDbContext db, IPublishEndpoint bus) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        while (!ct.IsCancellationRequested)
        {
            var messages = await db.OutboxMessages
                .Where(m => m.PublishedAt == null)
                .OrderBy(m => m.CreatedAt)
                .Take(50)
                .ToListAsync(ct);

            foreach (var msg in messages)
            {
                var @event = JsonSerializer.Deserialize(msg.Payload, Type.GetType(msg.Type)!);
                await bus.Publish(@event!, ct);
                msg.PublishedAt = DateTime.UtcNow;
            }

            await db.SaveChangesAsync(ct);
            await Task.Delay(1000, ct);
        }
    }
}

Giải quyết: Dual-write problem (save DB + publish event phải atomic).


Circuit Breaker & Retry

csharp
// .NET 8+: Microsoft.Extensions.Http.Resilience
builder.Services.AddHttpClient("PaymentApi")
    .AddStandardResilienceHandler();
// ↑ Bao gồm: Retry, Circuit Breaker, Timeout, Rate Limiter - tự động!

// Custom configuration
builder.Services.AddHttpClient("ExternalApi")
    .AddResilienceHandler("custom", pipeline =>
    {
        pipeline.AddRetry(new HttpRetryStrategyOptions
        {
            MaxRetryAttempts = 3,
            BackoffType = DelayBackoffType.Exponential,
            Delay = TimeSpan.FromMilliseconds(500)
        });

        pipeline.AddCircuitBreaker(new HttpCircuitBreakerStrategyOptions
        {
            FailureRatio = 0.5,              // 50% fails → open circuit
            SamplingDuration = TimeSpan.FromSeconds(30),
            MinimumThroughput = 10,
            BreakDuration = TimeSpan.FromSeconds(15)
        });

        pipeline.AddTimeout(TimeSpan.FromSeconds(10));
    });

Saga Pattern (Distributed Transaction)

csharp
// MassTransit State Machine Saga
public class OrderSaga : MassTransitStateMachine<OrderSagaState>
{
    public OrderSaga()
    {
        InstanceState(x => x.CurrentState);

        Event(() => OrderCreated, x => x.CorrelateById(m => m.Message.OrderId));
        Event(() => StockReserved, x => x.CorrelateById(m => m.Message.OrderId));
        Event(() => PaymentProcessed, x => x.CorrelateById(m => m.Message.OrderId));
        Event(() => PaymentFailed, x => x.CorrelateById(m => m.Message.OrderId));

        Initially(
            When(OrderCreated)
                .Then(ctx => ctx.Saga.OrderId = ctx.Message.OrderId)
                .Publish(ctx => new ReserveStock(ctx.Saga.OrderId))
                .TransitionTo(AwaitingStock));

        During(AwaitingStock,
            When(StockReserved)
                .Publish(ctx => new ProcessPayment(ctx.Saga.OrderId))
                .TransitionTo(AwaitingPayment));

        During(AwaitingPayment,
            When(PaymentProcessed)
                .Publish(ctx => new ScheduleShipping(ctx.Saga.OrderId))
                .TransitionTo(Completed),
            When(PaymentFailed)
                .Publish(ctx => new ReleaseStock(ctx.Saga.OrderId))     // Compensate!
                .Publish(ctx => new CancelOrder(ctx.Saga.OrderId))
                .TransitionTo(Failed));
    }
}

Event Sourcing (Advanced)

csharp
// Thay vì lưu STATE hiện tại, lưu TẤT CẢ EVENTS
// Rebuild state bằng cách replay events
public class Order
{
    private readonly List<IDomainEvent> _events = [];

    public void Apply(OrderCreatedEvent e) { Id = e.OrderId; Status = OrderStatus.Draft; }
    public void Apply(ItemAddedEvent e) { Items.Add(new OrderItem(e.ProductId, e.Quantity)); }
    public void Apply(DiscountAppliedEvent e) { Discount = e.Percentage; }

    // Rebuild from events
    public static Order FromHistory(IEnumerable<IDomainEvent> events)
    {
        var order = new Order();
        foreach (var e in events) order.Apply((dynamic)e);
        return order;
    }
}

Dùng khi: Audit trail BẮT BUỘC (finance, insurance), time-travel debugging, undo/redo.
KHÔNG dùng khi: CRUD đơn giản, team chưa hiểu rõ eventual consistency.


Decision Matrix - Chọn Pattern Nào?

PatternComplexityWhen to LearnUsed In
Repository⭐⭐Day 1Mọi project
Unit of Work⭐⭐Day 1Mọi project
Outbox⭐⭐⭐Event-driven projectseCommerce, Fintech
Circuit Breaker⭐⭐External integrationsMicroservices
Saga⭐⭐⭐⭐Distributed workflowsBooking, Payment flows
Event Sourcing⭐⭐⭐⭐⭐Audit-heavy domainsBanking, Insurance