Hexagonal architecture (Ports and Adapters) in .NET: driving and driven adapters in C#

.NET Architecture & Design Patterns November 13, 2026 csharp

The core (domain + use cases) doesn't know who calls it or what it calls.

csharp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// ======================================================================
// Q: What is Hexagonal architecture (Ports and Adapters)?
//
// The core (domain + use cases) doesn't know who calls it or what it calls. It
// communicates only through ports.
// - Driving (primary) adapters: they call the core. REST endpoints, message
//   consumers, scheduled jobs, tests.
// - Driven (secondary) adapters: the core calls them. EF repository, ERP client,
//   email sender.
// Onion, Clean and Hexagonal share the same idea: an independent core and
// dependency inversion. They differ in emphasis: layers (Onion), use cases
// (Clean), inputs / outputs (Hexagonal).
// ======================================================================

// Driven port: the core states what it needs
public interface IErpGateway { Task<string> SendOrderAsync(Order o, CancellationToken ct); }

public class SapErpAdapter(HttpClient http) : IErpGateway { /* SAP over HTTP */ }
public class FakeErpAdapter : IErpGateway { /* for tests and local dev */ }

// Two driving adapters, the same use case
app.MapPost("/orders/{id}/sync", (int id, SyncOrderHandler h, CancellationToken ct) =>
    h.Handle(id, ct));

public class OrderPlacedConsumer(SyncOrderHandler h) : IConsumer<OrderPlaced>
{
    public Task Consume(ConsumeContext<OrderPlaced> ctx) =>
        h.Handle(ctx.Message.OrderId, ctx.CancellationToken);
}