Event-Driven Architecture for Sovereignty

Asynchronous event processing patterns with data residency compliance and regional event routing.

Table of Contents

  1. Table of Contents
  2. Overview
  3. Learning Objectives
  4. Event-Driven Architecture
  5. Regional Event Routing
    1. Event Grid Configuration
    2. Event Routing Pattern
  6. Service Bus Patterns
    1. Ordered Message Processing
    2. Dead Letter Handling
  7. Event Store Design
    1. Cosmos DB Configuration
    2. Event Schema
  8. Cross-Region Choreography
    1. Saga Pattern with Sovereignty
    2. Compensating Transactions
  9. Implementation Checklist
  10. Next Steps

Overview

Event-driven architectures enable loose coupling and scalability but introduce complexity for data sovereignty. Events may contain regulated data and must be processed within approved boundaries. This module covers sovereign-aware event patterns.

Learning Objectives

After completing this section, you will be able to:

  • ✅ Design event-driven architectures with sovereignty controls
  • ✅ Implement regional event routing
  • ✅ Configure event stores with data residency
  • ✅ Handle cross-region event choreography

Event-Driven Architecture

Component Purpose Sovereignty Role
Event Grid Event routing Regional deployment
Service Bus Ordered messaging Topic-based routing
Event Hubs Streaming ingestion Regional capture
Cosmos DB Event store Multi-region writes disabled
Azure Functions Serverless processing Regional execution

Regional Event Routing

Event Grid Configuration

# Create regional Event Grid topic
New-AzEventGridTopic `
    -Name "eu-events" `
    -ResourceGroupName "events-rg" `
    -Location "westeurope" `
    -InputSchema "CloudEventSchemaV1_0"

# Create subscription with regional filter
New-AzEventGridSubscription `
    -TopicName "eu-events" `
    -ResourceGroupName "events-rg" `
    -EventSubscriptionName "eu-processor" `
    -Endpoint "/subscriptions/{sub}/resourceGroups/functions-rg/providers/Microsoft.Web/sites/eu-processor/functions/ProcessEvent" `
    -EndpointType "AzureFunction" `
    -AdvancedFilter @(
        @{
            Key = "data.region"
            OperatorType = "StringIn"
            Values = @("EU", "westeurope", "northeurope")
        }
    )

Event Routing Pattern

graph TB
    subgraph Sources ["Event Sources"]
        A[Application A]
        B[Application B]
        C[IoT Devices]
    end

    subgraph Router ["Regional Router"]
        D{Event Grid<br/>Regional Filter}
    end

    subgraph EU ["EU Region"]
        E[EU Event Grid]
        F[EU Processor]
        G[EU Event Store]
    end

    subgraph US ["US Region"]
        H[US Event Grid]
        I[US Processor]
        J[US Event Store]
    end

    A --> D
    B --> D
    C --> D
    D -->|"region=EU"| E
    D -->|"region=US"| H
    E --> F --> G
    H --> I --> J

    style EU fill:#E8F4FD,stroke:#0078D4
    style US fill:#FFF4E6,stroke:#FF8C00

Service Bus Patterns

Ordered Message Processing

// Session-based ordering for sovereignty compliance
await using var processor = serviceBusClient.CreateSessionProcessor(
    queueName: "sovereign-events",
    new ServiceBusSessionProcessorOptions
    {
        MaxConcurrentSessions = 10,
        MaxConcurrentCallsPerSession = 1, // Strict ordering
        SessionIdleTimeout = TimeSpan.FromMinutes(5)
    });

processor.ProcessMessageAsync += async args =>
{
    var message = args.Message;
    var region = message.ApplicationProperties["DataRegion"].ToString();

    // Validate regional processing
    if (!IsApprovedRegion(region, Environment.GetEnvironmentVariable("DEPLOYMENT_REGION")))
    {
        throw new InvalidOperationException($"Cannot process {region} data in this region");
    }

    await ProcessEventAsync(message);
    await args.CompleteMessageAsync(message);
};

Dead Letter Handling

# Dead letter configuration for compliance
deadLetterConfiguration:
  maxDeliveryCount: 3
  deadLetterQueue: "sovereignty-violations-dlq"

  alerting:
    enabled: true
    threshold: 1  # Alert on first violation
    notification: "security-team@contoso.com"

  retention:
    days: 90  # Regulatory retention
    archiveToStorage: true

Event Store Design

Cosmos DB Configuration

# Create sovereign event store
New-AzCosmosDBAccount `
    -ResourceGroupName "events-rg" `
    -Name "eu-event-store" `
    -Location "West Europe" `
    -DefaultConsistencyLevel "Strong" `
    -EnableMultipleWriteLocations $false `  # Prevent cross-region writes
    -PublicNetworkAccess "Disabled"

# Create event container with TTL
New-AzCosmosDBSqlContainer `
    -AccountName "eu-event-store" `
    -ResourceGroupName "events-rg" `
    -DatabaseName "events" `
    -Name "event-store" `
    -PartitionKeyPath "/aggregateId" `
    -DefaultTtl 2592000  # 30 days

Event Schema

{
  "id": "evt-12345",
  "aggregateId": "order-67890",
  "aggregateType": "Order",
  "eventType": "OrderPlaced",
  "eventVersion": 1,
  "timestamp": "2024-01-15T10:30:00Z",
  "dataRegion": "EU",
  "dataClassification": "Confidential",
  "causationId": "cmd-11111",
  "correlationId": "session-22222",
  "metadata": {
    "userId": "user-33333",
    "clientIp": "10.0.0.1"
  },
  "payload": {
    "orderId": "order-67890",
    "customerId": "cust-44444",
    "total": 150.00
  }
}

Cross-Region Choreography

Saga Pattern with Sovereignty

sequenceDiagram
    participant EU as EU Order Service
    participant Saga as Saga Coordinator
    participant US as US Inventory Service

    EU->>Saga: Order Created (EU data)
    Saga->>Saga: Extract non-PII metadata
    Saga->>US: Reserve Inventory (metadata only)
    US->>Saga: Inventory Reserved
    Saga->>EU: Complete Order (EU)

    Note over EU,US: PII remains in EU region<br/>Only metadata crosses regions

Compensating Transactions

// Saga with regional compensation
public class SovereignOrderSaga
{
    public async Task ExecuteAsync(OrderCommand command)
    {
        try
        {
            // Step 1: Create order in origin region
            var order = await _euOrderService.CreateOrderAsync(command);

            // Step 2: Reserve inventory (metadata only)
            var reservation = await _inventoryService.ReserveAsync(
                new ReservationRequest
                {
                    OrderReference = order.Id, // Reference, not data
                    Items = command.Items.Select(i => i.Sku).ToList()
                });

            // Step 3: Complete in origin region
            await _euOrderService.ConfirmOrderAsync(order.Id, reservation.Id);
        }
        catch (Exception ex)
        {
            // Compensate in reverse order
            await _euOrderService.CancelOrderAsync(order.Id);
            throw;
        }
    }
}

Implementation Checklist

  • Deploy regional Event Grid topics
  • Configure Service Bus with sessions
  • Create Cosmos DB event store
  • Implement regional filters
  • Set up dead letter handling
  • Configure event archival
  • Implement saga coordinators
  • Set up cross-region monitoring
  • Define retry policies

Next Steps


Reference: Azure Event Grid — Microsoft Learn