System Integration

Event-Driven Integration Patterns for Enterprise Systems

14 min read
#event-sourcing#cqrs#integration#azure-service-bus
Georg Pfeiffer
Georg Pfeiffer
Senior Solution Architect

In enterprise integration, there are many patterns for connecting systems. After integrating over a dozen enterprise applications — from Dynamics 365 to SAP to custom backends — I've developed a framework for choosing the right integration pattern for each use case.

The Integration Pattern Spectrum

Integration patterns exist on a spectrum from simple to complex:

text
Point-to-Point → API Gateway → Message Bus → Event Sourcing → Full CQRS
     simple                                                    complex
     coupled                                                   decoupled
     synchronous                                               asynchronous

Moving right adds decoupling and resilience but also adds operational complexity. The key is choosing the *simplest* pattern that meets your requirements.

Point-to-Point: When It's Enough

Direct API calls between two systems. Simple, fast, easy to understand.

csharp
public class OrderSyncService
{
    public async Task SyncOrder(Order order)
    {
        var erpPayload = MapToErpFormat(order);
        await _erpClient.PostAsync("/api/orders", erpPayload);
    }
}

**Use when:** Two systems, low volume, both are always available, you control both ends.

**Avoid when:** More than 2 systems need the data, either system has significant downtime, or you need guaranteed delivery.

Message-Based Integration

A message broker (Azure Service Bus, RabbitMQ) decouples sender and receiver. The sender publishes, the broker stores, the receiver processes at its own pace.

csharp
// publisher: fire and forget
await _topicClient.SendMessageAsync(new ServiceBusMessage
{
    Subject = "order.created",
    Body = BinaryData.FromObjectAsJson(order),
    ContentType = "application/json"
});

// subscriber: processes independently
await _processor.ProcessMessageAsync(async args =>
{
    var order = args.Message.Body.ToObjectFromJson<Order>();
    await SyncToErp(order);
    await args.CompleteMessageAsync(args.Message);
});

**Use when:** Systems have different availability windows, multiple consumers need the same data, you need guaranteed delivery with retry.

Event-Driven Integration

Events describe *what happened* rather than *what to do*. This is a subtle but important distinction:

csharp
// command (tells a system what to do)
new CreateInvoiceCommand { OrderId = 123, Amount = 500 }

// event (describes what happened)
new OrderCompletedEvent { OrderId = 123, CompletedAt = DateTime.UtcNow }

With events, the publisher doesn't know or care who is listening. New consumers can subscribe without changing the publisher. This is crucial in enterprise landscapes where new integrations are constantly added.

Designing Good Events

Events should be:

  • **Past tense** — `OrderCreated`, not `CreateOrder`
  • **Self-contained** — include enough data for consumers to act without callbacks
  • **Versioned** — plan for schema evolution from day one
csharp
[MessageVersion(2)]
public record OrderCreatedEvent
{
    public required Guid OrderId { get; init; }
    public required string CustomerNumber { get; init; }
    public required decimal TotalAmount { get; init; }
    public required string Currency { get; init; }
    public required IReadOnlyList<OrderLine> Lines { get; init; }
    public required DateTimeOffset CreatedAt { get; init; }
}

The API Gateway Pattern

When multiple external consumers need access to your internal services, an API gateway provides a single entry point with cross-cutting concerns:

  • Authentication and authorization
  • Rate limiting and throttling
  • Request/response transformation
  • Monitoring and analytics

Azure API Management works well for this in the Azure ecosystem. The gateway enforces message format contracts that all publishers and consumers agree on.

Choosing the Right Pattern

RequirementPattern
Simple A→B syncPoint-to-point
Guaranteed deliveryMessage bus
Multiple consumersEvents + topics
Different data modelsAPI gateway + transformation
Audit trail neededEvent sourcing
Read/write scaling differsCQRS

Key Takeaways

  • Start with the simplest pattern that works — premature complexity kills projects
  • Events beat commands for extensibility in enterprise landscapes
  • Design events as self-contained, versioned contracts
  • Use an API gateway when external consumers need controlled access
  • Document your integration patterns — the next developer will thank you

Share this article

Georg Pfeiffer

About the Author

Georg is a senior solution architect specializing in .NET, Azure, and Dynamics 365. He helps organizations design and build scalable, maintainable enterprise systems. When he's not writing code, he's writing about it here.

Learn more about Georg

© 2026 georgpfeiffer.dev. All rights reserved.

Built with SvelteKit & Tailwind CSS