Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/examples/enterprise-messaging-workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Example source:
| Source-generated content router | `ContentRouterGeneratorExample.cs` | Attribute-driven content routing without runtime scanning. |
| Resilient checkout orchestration | `ResilientCheckoutDemo.cs` | Route selection, routing-slip execution, command compensation, and fallback routes. |
| Collaborating service mailboxes | `ServiceCollaborationMailboxDemo.cs` | Inventory, payment, shipping, and notification mailboxes collaborating over correlated messages. |
| Backplane facade | `BackplaneFacadeDemo.cs` | MassTransit/MediatR-shaped request/reply and pub/sub over an application-owned transport boundary. |
| Backplane facade | `BackplaneFacadeDemo.cs` | MassTransit/MediatR-shaped host builder, typed client, request/reply, and pub/sub over an application-owned transport boundary. |

## Workflow Shape

Expand Down Expand Up @@ -77,7 +77,7 @@ The example tests use behavior-oriented assertions:
- Generator tests assert that generated factories compile and behave like the equivalent runtime builders.
- Resilient checkout tests assert rollback, fallback route selection, manual review, and side-effect boundaries.
- Mailbox collaboration tests assert service handoff, compensation, correlation propagation, and final notification outcomes.
- Backplane facade tests assert routed request/reply, publish/subscribe fanout, outbox dispatch, idempotent replay, and correlation propagation.
- Backplane facade tests assert startup-style host configuration, routed request/reply, publish/subscribe fanout, outbox dispatch, idempotent replay, and correlation propagation.

## Related Documentation

Expand Down
6 changes: 3 additions & 3 deletions docs/examples/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Welcome! This section collects small, focused demos that show **how to compose b
* **Snapshot history & undo/redo (Memento)** for document/editor style workflows.
* **Source-generated application wiring** for builders, factories, facades, proxies, observers, visitors, state machines, strategies, mementos, template methods, and messaging factories.
* **Enterprise messaging workflows** for envelopes, routers, recipient lists, splitters, aggregators, routing slips, sagas, mailboxes, idempotent receivers, inboxes, and outboxes.
* **Messaging backplane facade** for request/reply and publish/subscribe over an application-owned transport boundary.
* **Messaging backplane facade** for host-style setup, typed request/reply, and publish/subscribe over an application-owned transport boundary.

## Demos in this section

Expand Down Expand Up @@ -65,7 +65,7 @@ Welcome! This section collects small, focused demos that show **how to compose b
Application-shaped messaging demos: checkout route selection, routing-slip execution, command compensation, fallback routes, and service mailboxes collaborating over correlated messages. See [Resilient Checkout and Collaborating Mailboxes](resilient-checkout-and-mailboxes.md).

* **Messaging Backplane Facade**
Demonstrates how PatternKit can sit behind a MassTransit- or MediatR-style application API while RabbitMQ, Azure Service Bus, Postgres, MQTT, or another adapter remains infrastructure-owned. See [Messaging Backplane Facade](messaging-backplane-facade.md).
Demonstrates how PatternKit can sit behind a MassTransit- or MediatR-style host builder and typed client while RabbitMQ, Azure Service Bus, Postgres, MQTT, or another adapter remains infrastructure-owned. See [Messaging Backplane Facade](messaging-backplane-facade.md).

## How to run

Expand Down Expand Up @@ -111,7 +111,7 @@ dotnet test PatternKit.slnx -c Release
* **Enterprise Messaging:** `src/PatternKit.Examples/Messaging` (+ `PatternKit.Examples.Tests/Messaging`) — runtime and generated messaging workflows.
* **Resilient Checkout:** `ResilientCheckoutDemo` (+ `ResilientCheckoutDemoTests`) — fallback checkout routes with compensation.
* **Collaborating Mailboxes:** `ServiceCollaborationMailboxDemo` (+ `ServiceCollaborationMailboxDemoTests`) — inventory, payment, shipping, and notification services collaborating through serialized mailboxes.
* **Messaging Backplane Facade:** `BackplaneFacadeDemo` (+ `BackplaneFacadeDemoTests`) — request/reply, pub/sub, outbox, idempotency, and mailbox-backed transport subscribers.
* **Messaging Backplane Facade:** `BackplaneFacadeDemo` (+ `BackplaneFacadeDemoTests`) — host setup, request/reply, pub/sub, outbox, idempotency, and mailbox-backed transport subscribers.
* **Tests:** `PatternKit.Examples.Tests/*` use TinyBDD scenarios that read like specs.

## Why these demos exist
Expand Down
60 changes: 48 additions & 12 deletions docs/examples/messaging-backplane-facade.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,30 +38,54 @@ public interface IBackplaneTransport : IAsyncDisposable

The example uses `InMemoryBackplaneTransport` for deterministic tests. A production adapter could implement the same boundary over RabbitMQ exchanges, Azure Service Bus topics/queues, Postgres-backed tables and notifications, MQTT topics, or another transport.

## Application Startup

The demo now starts from a host builder, which is the shape a production application would usually expose from its composition root:

```csharp
await using var host = await BackplaneHost.Create()
.UseTransport(() => transport)
.UseOutbox(outbox)
.UseIdempotencyStore(idempotency)
.MapCommand<SubmitOrder, BackplaneOrderAccepted>(
static (message, _) => message.Payload.CustomerTier == CustomerTier.Vip,
"orders.priority")
.MapDefaultCommand<SubmitOrder, BackplaneOrderAccepted>("orders.standard")
.ReceiveEndpoint("orders.standard", endpoint =>
endpoint.HandleCommand<SubmitOrder, BackplaneOrderAccepted>(services.AcceptStandardOrderAsync))
.ReceiveEndpoint("billing-service", endpoint =>
endpoint.Subscribe<BackplaneOrderSubmitted>("orders.submitted", services.CapturePaymentAsync))
.ReceiveEndpoint("notification-service", endpoint =>
{
endpoint.Subscribe<PaymentDeclined>("payments.declined", services.NotifyPaymentDeclinedAsync);
endpoint.Subscribe<ShipmentScheduled>("shipments.scheduled", services.NotifyShipmentScheduledAsync);
})
.BuildAsync(cancellationToken);
```

`BackplaneHost` owns the bus, typed client, transport, endpoint subscriptions, outbox, idempotency store, and topology metadata. Application code uses `host.Client`, while advanced integrations can still reach the lower-level `host.Bus`.

## Request/Reply

The bus facade exposes a typed request/reply API:
The client exposes a typed request/reply API:

```csharp
bus.Route<SubmitOrder>(
static (message, _) => message.Payload.CustomerTier == CustomerTier.Vip,
"orders.priority");
bus.RouteDefault<SubmitOrder>("orders.standard");

await bus.HandleAsync<SubmitOrder, BackplaneOrderAccepted>(
"orders.standard",
AcceptOrderAsync,
idempotencyStore);
var accepted = await host.Client.RequestAsync<SubmitOrder, BackplaneOrderAccepted>(
Message<SubmitOrder>
.Create(new SubmitOrder("order-42", 90m, CustomerTier.Standard))
.WithCorrelationId("corr-order-42")
.WithIdempotencyKey("idem-order-42"),
cancellationToken);
```

`BackplaneBus.RequestAsync<TRequest, TResponse>` creates a temporary reply address, enriches the message with a reply header, routes the command with the content router, sends it through the transport, and waits for the typed response. Duplicate requests with the same idempotency key replay the stored `BackplaneOrderAccepted` response without republishing `BackplaneOrderSubmitted`.
`BackplaneClient.RequestAsync<TRequest, TResponse>` creates a temporary reply address, enriches the message with a reply header, routes the command with the content router, sends it through the transport, and waits for the typed response. Duplicate requests with the same idempotency key replay the stored `BackplaneOrderAccepted` response without republishing `BackplaneOrderSubmitted`.

## Publish/Subscribe

The order service publishes an event through the outbox:

```csharp
await bus.PublishAsync(
await client.PublishAsync(
"orders.submitted",
new BackplaneOrderSubmitted(message.Payload.OrderId, message.Payload.Total, message.Payload.CustomerTier),
context.Headers,
Expand All @@ -77,11 +101,23 @@ The transport uses a recipient list so every matching subscriber receives the en

Each subscriber runs behind a bounded mailbox, so stateful handlers process one message at a time with explicit backpressure.

## Transport Adapter Boundary

The application chooses the transport at startup:

```csharp
BackplaneHost.Create()
.UseTransport(() => new RabbitMqBackplaneTransport(/* connection settings */));
```

`RabbitMqBackplaneTransport` is not part of PatternKit; it would be application infrastructure that implements `IBackplaneTransport`. The same host, routes, endpoints, command handlers, and event subscribers can run over any adapter that honors the transport contract.

## Tested Behavior

The tests assert that:

- Standard orders route to `orders.standard` and VIP orders route to `orders.priority`.
- The host builder configures transport, outbox, idempotency, endpoint topology, and the typed client surface.
- Duplicate commands replay the original response and do not duplicate outbox side effects.
- Published events fan out to independent services.
- Every event is recorded in the outbox before transport dispatch.
Expand Down
Loading
Loading