---
name: inter-service-communication
description: Giao tiếp liên dịch vụ. Use for Event Bus (RabbitMQ), Integration Events, HTTP Client với Polly, và gRPC patterns.
compatibility: ".NET 8+, MassTransit, RabbitMQ, Polly, gRPC"
metadata:
  author: Velik Ho
  version: "1.0"
---

# Inter-Service Communication / Giao Tiếp Liên Dịch Vụ

Patterns giao tiếp giữa các microservices trong GoodGo.

## When to Use This Skill / Khi Nào Sử Dụng

Use this skill when:
- Publishing domain events to other services / Publish domain events đến services khác
- Consuming integration events / Consume integration events
- Making HTTP calls to other services / Gọi HTTP đến services khác
- Implementing resilient communication / Triển khai giao tiếp có khả năng phục hồi

## Core Concepts / Khái Niệm Cốt Lõi

### Communication Patterns / Các Mẫu Giao Tiếp

```
┌─────────────────────────────────────────────────────────────┐
│                 COMMUNICATION PATTERNS                       │
├───────────────────────────────┬─────────────────────────────┤
│        SYNCHRONOUS            │        ASYNCHRONOUS          │
│     (Request/Response)        │       (Fire & Forget)        │
├───────────────────────────────┼─────────────────────────────┤
│ • HTTP/REST APIs              │ • Message Queue (RabbitMQ)   │
│ • gRPC                        │ • Event Bus                  │
│ • Direct service calls        │ • Pub/Sub                    │
├───────────────────────────────┼─────────────────────────────┤
│ Use for: Queries, UI          │ Use for: Commands, Events    │
│ Pro: Immediate response       │ Pro: Decoupled, resilient    │
│ Con: Tight coupling           │ Con: Eventual consistency    │
└───────────────────────────────┴─────────────────────────────┘
```

### Event Types / Các Loại Event

| Type | Scope | Transport | Example |
|------|-------|-----------|---------|
| **Domain Event** | Within aggregate | In-memory | OrderItemAdded |
| **Integration Event** | Between services | Message broker | OrderCreatedIntegrationEvent |

### Anti-patterns to Avoid / Anti-patterns Cần Tránh

```
❌ Synchronous HTTP Chains (Anti-pattern)

Service A ──HTTP──▶ Service B ──HTTP──▶ Service C ──HTTP──▶ DB
                    │                   │
                    └── Single point of failure ──┘

✅ Async Event-Driven (Recommended)

Service A ──Publish──▶ Event Bus ──Subscribe──▶ Service B
                          │
                          └────Subscribe──▶ Service C
```

## Key Patterns / Mẫu Chính

### Integration Event Definition

```csharp
/// <summary>
/// EN: Base interface for integration events.
/// VI: Interface cơ sở cho integration events.
/// </summary>
public interface IIntegrationEvent
{
    Guid Id { get; }
    DateTime OccurredOn { get; }
}

/// <summary>
/// EN: Event when order is created.
/// VI: Event khi order được tạo.
/// </summary>
public record OrderCreatedIntegrationEvent : IIntegrationEvent
{
    public Guid Id { get; init; } = Guid.NewGuid();
    public DateTime OccurredOn { get; init; } = DateTime.UtcNow;
    
    public Guid OrderId { get; init; }
    public string UserId { get; init; } = default!;
    public decimal TotalAmount { get; init; }
    public IReadOnlyList<OrderItemInfo> Items { get; init; } = Array.Empty<OrderItemInfo>();
}

public record OrderItemInfo(Guid ProductId, int Quantity, decimal UnitPrice);
```

### Event Publisher with MassTransit

```csharp
/// <summary>
/// EN: Publish integration events via MassTransit.
/// VI: Publish integration events qua MassTransit.
/// </summary>
public class IntegrationEventPublisher : IIntegrationEventPublisher
{
    private readonly IPublishEndpoint _publishEndpoint;
    private readonly ILogger<IntegrationEventPublisher> _logger;

    public IntegrationEventPublisher(
        IPublishEndpoint publishEndpoint,
        ILogger<IntegrationEventPublisher> logger)
    {
        _publishEndpoint = publishEndpoint;
        _logger = logger;
    }

    public async Task PublishAsync<T>(T @event, CancellationToken ct = default)
        where T : class, IIntegrationEvent
    {
        _logger.LogInformation(
            "Publishing event {EventType} with Id {EventId}",
            typeof(T).Name,
            @event.Id);

        await _publishEndpoint.Publish(@event, ct);
    }
}

// EN: Usage in command handler / VI: Sử dụng trong command handler
public class CreateOrderCommandHandler : IRequestHandler<CreateOrderCommand, OrderResult>
{
    private readonly IOrderRepository _orderRepository;
    private readonly IIntegrationEventPublisher _eventPublisher;

    public async Task<OrderResult> Handle(CreateOrderCommand request, CancellationToken ct)
    {
        var order = new Order(request.UserId, request.ShippingAddress);
        // ... add items

        await _orderRepository.AddAsync(order, ct);
        await _orderRepository.UnitOfWork.SaveChangesAsync(ct);

        // EN: Publish integration event
        // VI: Publish integration event
        await _eventPublisher.PublishAsync(new OrderCreatedIntegrationEvent
        {
            OrderId = order.Id,
            UserId = order.UserId,
            TotalAmount = order.TotalAmount,
            Items = order.OrderItems.Select(i => new OrderItemInfo(
                i.ProductId, i.Quantity, i.UnitPrice)).ToList()
        }, ct);

        return new OrderResult(order.Id);
    }
}
```

### Event Consumer

```csharp
/// <summary>
/// EN: Consumer for OrderCreatedIntegrationEvent in Inventory Service.
/// VI: Consumer cho OrderCreatedIntegrationEvent trong Inventory Service.
/// </summary>
public class OrderCreatedConsumer : IConsumer<OrderCreatedIntegrationEvent>
{
    private readonly IInventoryService _inventoryService;
    private readonly ILogger<OrderCreatedConsumer> _logger;

    public OrderCreatedConsumer(
        IInventoryService inventoryService,
        ILogger<OrderCreatedConsumer> logger)
    {
        _inventoryService = inventoryService;
        _logger = logger;
    }

    public async Task Consume(ConsumeContext<OrderCreatedIntegrationEvent> context)
    {
        var @event = context.Message;

        _logger.LogInformation(
            "Received OrderCreatedEvent: {OrderId}",
            @event.OrderId);

        try
        {
            // EN: Reserve inventory for order items
            // VI: Đặt trước inventory cho order items
            foreach (var item in @event.Items)
            {
                await _inventoryService.ReserveAsync(
                    item.ProductId,
                    item.Quantity,
                    @event.OrderId,
                    context.CancellationToken);
            }

            _logger.LogInformation(
                "Inventory reserved for Order: {OrderId}",
                @event.OrderId);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex,
                "Failed to reserve inventory for Order: {OrderId}",
                @event.OrderId);
            throw; // EN: Will retry based on retry policy
        }
    }
}
```

### MassTransit Configuration

```csharp
/// <summary>
/// EN: Configure MassTransit with RabbitMQ.
/// VI: Cấu hình MassTransit với RabbitMQ.
/// </summary>

// Program.cs
builder.Services.AddMassTransit(x =>
{
    // EN: Register consumers
    // VI: Đăng ký consumers
    x.AddConsumer<OrderCreatedConsumer>();
    x.AddConsumer<ProductPriceChangedConsumer>();

    x.UsingRabbitMq((context, cfg) =>
    {
        cfg.Host(builder.Configuration["RabbitMQ:Host"], "/", h =>
        {
            h.Username(builder.Configuration["RabbitMQ:Username"]!);
            h.Password(builder.Configuration["RabbitMQ:Password"]!);
        });

        // EN: Configure retry policy
        // VI: Cấu hình retry policy
        cfg.UseMessageRetry(r => r.Intervals(
            TimeSpan.FromSeconds(1),
            TimeSpan.FromSeconds(5),
            TimeSpan.FromSeconds(15)));

        // EN: Configure endpoints
        // VI: Cấu hình endpoints
        cfg.ConfigureEndpoints(context);
    });
});
```

### Resilient HTTP Client with Polly

```csharp
/// <summary>
/// EN: Configure resilient HTTP client for inter-service calls.
/// VI: Cấu hình HTTP client có khả năng phục hồi cho gọi liên dịch vụ.
/// </summary>

// Program.cs
builder.Services.AddHttpClient<IIamServiceClient, IamServiceClient>(client =>
{
    client.BaseAddress = new Uri(builder.Configuration["Services:Iam:BaseUrl"]!);
    client.DefaultRequestHeaders.Add("Accept", "application/json");
})
.AddResilienceHandler("iam-service", builder =>
{
    // EN: Retry with exponential backoff
    builder.AddRetry(new HttpRetryStrategyOptions
    {
        MaxRetryAttempts = 3,
        Delay = TimeSpan.FromMilliseconds(500),
        BackoffType = DelayBackoffType.Exponential,
        ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
            .HandleResult(r => (int)r.StatusCode >= 500)
            .Handle<HttpRequestException>()
    });

    // EN: Circuit breaker
    builder.AddCircuitBreaker(new HttpCircuitBreakerStrategyOptions
    {
        FailureRatio = 0.5,
        SamplingDuration = TimeSpan.FromSeconds(30),
        BreakDuration = TimeSpan.FromSeconds(30)
    });

    // EN: Timeout
    builder.AddTimeout(TimeSpan.FromSeconds(10));
});
```

## Common Mistakes / Lỗi Thường Gặp

### 1. Synchronous HTTP Chains

```csharp
// ❌ BAD: Chain of HTTP calls
public async Task ProcessOrder(Order order)
{
    await _inventoryService.ReserveAsync(order);    // HTTP
    await _paymentService.ChargeAsync(order);        // HTTP
    await _shippingService.CreateShipmentAsync(order); // HTTP
}

// ✅ GOOD: Event-driven, async
public async Task ProcessOrder(Order order)
{
    await _eventPublisher.PublishAsync(new OrderCreatedEvent(order));
    // Each service subscribes and processes independently
}
```

### 2. Missing Idempotency

```csharp
// ❌ BAD: No idempotency check
public async Task Consume(ConsumeContext<OrderCreatedEvent> context)
{
    await _service.CreateShipment(context.Message.OrderId);
}

// ✅ GOOD: Idempotent processing
public async Task Consume(ConsumeContext<OrderCreatedEvent> context)
{
    var eventId = context.Message.Id;
    if (await _processedEvents.ExistsAsync(eventId))
        return; // Already processed

    await _service.CreateShipment(context.Message.OrderId);
    await _processedEvents.MarkProcessedAsync(eventId);
}
```

### 3. No Retry Policy

```csharp
// ❌ BAD: Raw HTTP call without resilience
var response = await _httpClient.GetAsync("/api/users/123");

// ✅ GOOD: Use resilient HTTP client (configured at DI level)
// Retries, circuit breaker, and timeout are handled automatically
var response = await _resilientClient.GetAsync("/api/users/123");
```

## Quick Reference / Tham Chiếu Nhanh

### When to Use What

| Scenario | Pattern | Transport |
|----------|---------|-----------|
| Get data for UI | Sync HTTP | REST/gRPC |
| Notify state change | Async Event | RabbitMQ |
| Long-running process | Async Event | RabbitMQ |
| Real-time updates | Pub/Sub | RabbitMQ/SignalR |

### Message Broker vs Direct Call

| Aspect | Message Broker | Direct HTTP |
|--------|----------------|-------------|
| Coupling | Loose | Tight |
| Failure handling | Built-in retry | Manual |
| Scalability | High | Medium |
| Debugging | Harder | Easier |

## Resources / Tài Nguyên

- [Detailed Examples](./references/REFERENCE.md) - Full code examples
- [Error Handling](../error-handling-patterns/SKILL.md) - Polly patterns
- [CQRS MediatR](../cqrs-mediatr/SKILL.md) - Domain events
- [Docker Traefik](../docker-traefik/SKILL.md) - RabbitMQ container
