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
41 changes: 24 additions & 17 deletions docs/examples/messaging-backplane-facade.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,28 +44,33 @@ The example uses `InMemoryBackplaneTransport` for deterministic tests. A product
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);
})
await using var host = await GeneratedBackplaneTopology.Configure(
BackplaneHost.Create()
.UseTransport(() => transport)
.UseOutbox(outbox)
.UseIdempotencyStore(idempotency),
services)
.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`.

The generated topology comes from declarative attributes on a partial class:

```csharp
[GenerateBackplaneTopology(typeof(BackplaneDemoServices), HostBuilderType = typeof(BackplaneHostBuilder))]
[BackplaneRequestReply(typeof(SubmitOrder), typeof(BackplaneOrderAccepted), "orders.priority", nameof(BackplaneDemoServices.AcceptPriorityOrderAsync), PredicateMethodName = nameof(IsVipOrder))]
[BackplaneRequestReply(typeof(SubmitOrder), typeof(BackplaneOrderAccepted), "orders.standard", nameof(BackplaneDemoServices.AcceptStandardOrderAsync))]
[BackplaneSubscription(typeof(BackplaneOrderSubmitted), "orders.submitted", "billing-service", nameof(BackplaneDemoServices.CapturePaymentAsync))]
public static partial class GeneratedBackplaneTopology
{
private static bool IsVipOrder(Message<SubmitOrder> message, MessageContext context)
=> message.Payload.CustomerTier == CustomerTier.Vip;
}
```

The same host builder also remains fluent, so applications can mix generated, reviewed topology with environment-specific transport, outbox, idempotency, and observability wiring.

## Request/Reply

The client exposes a typed request/reply API:
Expand Down Expand Up @@ -118,7 +123,9 @@ BackplaneHost.Create()
The tests assert that:

- Standard orders route to `orders.standard` and VIP orders route to `orders.priority`.
- The generated topology registers request/reply routes and publish/subscribe endpoints.
- The host builder configures transport, outbox, idempotency, endpoint topology, and the typed client surface.
- The example can be imported through `IServiceCollection` with `AddMessagingBackplaneFacadeExample`.
- 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
7 changes: 7 additions & 0 deletions docs/generators/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ PatternKit includes a Roslyn incremental generator package (`PatternKit.Generato
| [**Saga**](messaging.md#generated-saga) | Typed process-manager transition factories | `[GenerateSaga]` |
| [**Mailbox**](messaging.md#generated-mailbox) | Serialized in-process inbox factories | `[GenerateMailbox]` |
| [**Reliability Pipeline**](messaging.md#generated-reliability-pipeline) | Idempotent receiver, inbox, and outbox factories | `[GenerateReliabilityPipeline]` |
| [**Backplane Topology**](messaging.md#generated-backplane-topology) | Request/reply routes and publish/subscribe endpoint topology | `[GenerateBackplaneTopology]` |

## Quick Reference

Expand Down Expand Up @@ -165,6 +166,12 @@ public static partial class OrderMailbox { }
[GenerateReliabilityPipeline(typeof(AcceptOrder), typeof(string), typeof(OrderAccepted))]
public static partial class OrderReliability { }

// Backplane topology - generated request/reply and pub/sub host wiring
[GenerateBackplaneTopology(typeof(OrderBackplaneServices), HostBuilderType = typeof(OrderBackplaneHostBuilder))]
[BackplaneRequestReply(typeof(SubmitOrder), typeof(OrderAccepted), "orders", nameof(OrderBackplaneServices.AcceptAsync))]
[BackplaneSubscription(typeof(OrderSubmitted), "orders.submitted", "audit-service", nameof(OrderBackplaneServices.AuditAsync))]
public static partial class OrderBackplane { }

// Routing slip - generated ordered itinerary factory
[GenerateRoutingSlip(typeof(Order))]
public static partial class OrderSlip { }
Expand Down
32 changes: 31 additions & 1 deletion docs/generators/messaging.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Messaging Generators

PatternKit includes nine messaging-oriented source generators:
PatternKit includes ten messaging-oriented source generators:

- <xref:PatternKit.Generators.Messaging.GenerateDispatcherAttribute> for source-generated mediator dispatchers.
- <xref:PatternKit.Generators.Messaging.GenerateMessageEnvelopeAttribute> for required message-envelope contracts.
Expand All @@ -11,6 +11,7 @@ PatternKit includes nine messaging-oriented source generators:
- <xref:PatternKit.Generators.Messaging.GenerateSagaAttribute> for typed saga/process-manager factories.
- <xref:PatternKit.Generators.Messaging.GenerateMailboxAttribute> for serialized in-process inbox factories.
- <xref:PatternKit.Generators.Messaging.GenerateReliabilityPipelineAttribute> for idempotent receiver, inbox, and outbox factories.
- <xref:PatternKit.Generators.Messaging.GenerateBackplaneTopologyAttribute> for compile-time request/reply and publish/subscribe host topology.

Use these generators when the message topology is known at compile time and should remain explicit, AOT-friendly, and validated by the compiler. They generate factories and fluent builders; they do not discover handlers from assemblies at runtime and they do not replace brokers, durable queues, or workflow engines.

Expand Down Expand Up @@ -235,6 +236,33 @@ Example files:
- `src/PatternKit.Examples/Messaging/ReliabilityExample.cs`
- `test/PatternKit.Examples.Tests/Messaging/ReliabilityExampleTests.cs`

## Generated Backplane Topology

`[GenerateBackplaneTopology]` wires request/reply routes and publish/subscribe endpoints into a `BackplaneHostBuilder` from declarative attributes:

```csharp
using PatternKit.Generators.Messaging;
using PatternKit.Messaging;

[GenerateBackplaneTopology(typeof(OrderBackplaneServices), HostBuilderType = typeof(OrderBackplaneHostBuilder))]
[BackplaneRequestReply(typeof(SubmitOrder), typeof(OrderAccepted), "orders.priority", nameof(OrderBackplaneServices.AcceptPriorityAsync), PredicateMethodName = nameof(IsPriority))]
[BackplaneRequestReply(typeof(SubmitOrder), typeof(OrderAccepted), "orders.standard", nameof(OrderBackplaneServices.AcceptStandardAsync))]
[BackplaneSubscription(typeof(OrderSubmitted), "orders.submitted", "billing-service", nameof(OrderBackplaneServices.CapturePaymentAsync))]
public static partial class OrderBackplane
{
private static bool IsPriority(Message<SubmitOrder> message, MessageContext context)
=> message.Payload.CustomerTier == CustomerTier.Vip;
}
```

The generated `Configure` method applies content-router request routes before default routes, registers command handlers, and registers topic subscriptions. It keeps broker infrastructure application-owned: the caller still supplies the transport, outbox, idempotency store, service object, and host-builder type used at the composition root.

Example files:

- `src/PatternKit.Examples/Messaging/BackplaneFacadeDemo.cs`
- `test/PatternKit.Examples.Tests/Messaging/BackplaneFacadeDemoTests.cs`
- `test/PatternKit.Generators.Tests/BackplaneTopologyGeneratorTests.cs`

## Generated Saga

`[GenerateSaga]` emits a process-manager factory from typed transition methods:
Expand Down Expand Up @@ -276,6 +304,7 @@ Example source:
| `PKSG001`-`PKSG004` | Saga | Non-partial host, missing transitions, invalid transition signatures, or invalid completion checks. |
| `PKMB001`-`PKMB005` | Mailbox | Non-partial host, missing handler, invalid handler signatures, or invalid configuration. |
| `PKRP001`-`PKRP005` | Reliability Pipeline | Non-partial host, missing handler, invalid handler/key selector signatures, or invalid configuration. |
| `PKBT001`-`PKBT005` | Backplane Topology | Non-partial host, missing topology, invalid route/subscription signatures, or duplicate default routes. |

## Related Runtime Patterns

Expand All @@ -285,3 +314,4 @@ Example source:
- [Saga / Process Manager](../patterns/messaging/saga.md)
- [Mailbox](../patterns/messaging/mailbox.md)
- [Idempotent Receiver, Inbox, and Outbox](../patterns/messaging/reliability.md)
- [Messaging Backplane Facade](../examples/messaging-backplane-facade.md)
4 changes: 2 additions & 2 deletions docs/guides/pattern-coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ The source of truth is `PatternKitPatternCatalog` in `src/PatternKit.Examples/Pr
| Messaging Reliability | Idempotent Receiver | `IdempotentReceiver<TPayload, TResult>` | Reliability pipeline generator |
| Messaging Reliability | Inbox | `InboxProcessor<TPayload, TResult>` | Reliability pipeline generator |
| Messaging Reliability | Outbox | `InMemoryOutbox<TPayload>` and dispatcher contracts | Reliability pipeline generator |
| Enterprise Integration | Request-Reply | Messaging backplane facade example | Tracked in [#214](https://github.com/JerrettDavis/PatternKit/issues/214) |
| Enterprise Integration | Publish-Subscribe | Messaging backplane facade example | Tracked in [#214](https://github.com/JerrettDavis/PatternKit/issues/214) |
| Enterprise Integration | Request-Reply | Messaging backplane facade example | Backplane topology generator |
| Enterprise Integration | Publish-Subscribe | Messaging backplane facade example | Backplane topology generator |
| Application Architecture | CQRS | Mediator/dispatcher command-query split | Dispatcher generator |

## Research Baselines
Expand Down
11 changes: 10 additions & 1 deletion docs/patterns/messaging/enterprise-generators.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

PatternKit source generators remove repetitive registration code for explicit enterprise integration patterns. They do not scan assemblies implicitly; each generated factory is opt-in through attributes on a partial type.

Use generators when routes, recipient lists, splitter/aggregator contracts, routing-slip steps, saga transitions, or mailbox inbox policies are static enough to validate at compile time and you want AOT-friendly factories without reflection.
Use generators when routes, recipient lists, splitter/aggregator contracts, routing-slip steps, saga transitions, mailbox inbox policies, or request/reply and publish/subscribe host topology are static enough to validate at compile time and you want AOT-friendly factories without reflection.

## Generated Content Router

Expand Down Expand Up @@ -79,6 +79,8 @@ Mailbox generation is documented in [Mailbox](mailbox.md). It discovers one `[Ma

Reliability helpers also have a generated path through `[GenerateReliabilityPipeline]`, which emits idempotent receiver, inbox, and outbox factories while keeping durable storage implementation owned by the application.

Backplane topology generation is documented in [Messaging Generators](../../generators/messaging.md#generated-backplane-topology). It discovers `[BackplaneRequestReply]` and `[BackplaneSubscription]` declarations on a partial topology type and emits a public `Configure` method for wiring the declared host-builder type at the application composition root.

## Diagnostics

| ID | Meaning |
Expand All @@ -96,13 +98,15 @@ Reliability helpers also have a generated path through `[GenerateReliabilityPipe
| `PKRS001`-`PKRS003` | Routing-slip generator validation. |
| `PKSG001`-`PKSG004` | Saga generator validation. |
| `PKMB001`-`PKMB005` | Mailbox generator validation. |
| `PKBT001`-`PKBT005` | Backplane topology generator validation. |

## Troubleshooting

- Make the generated host type `partial`.
- Keep route, step, and saga methods `static`; generated factories reference them directly.
- Use `nameof(PredicateMethod)` in `[ContentRoute]` and `[RecipientListRecipient]` so renames remain compile-time safe.
- Use unique route and recipient names and orders. Content routers are first-match, and recipient lists are ordered fan-out, so ambiguous ordering should fail at build time.
- Use one default request/reply route per request type in generated backplane topology; predicate routes are emitted before defaults so first-match routing stays deterministic.
- Ensure generated code builds under nullable enabled; the tests compile generated examples with Release settings.

## API
Expand All @@ -121,6 +125,9 @@ Reliability helpers also have a generated path through `[GenerateReliabilityPipe
- <xref:PatternKit.Generators.Messaging.MailboxHandlerAttribute>
- <xref:PatternKit.Generators.Messaging.MailboxErrorHandlerAttribute>
- <xref:PatternKit.Generators.Messaging.MailboxEventSinkAttribute>
- <xref:PatternKit.Generators.Messaging.GenerateBackplaneTopologyAttribute>
- <xref:PatternKit.Generators.Messaging.BackplaneRequestReplyAttribute>
- <xref:PatternKit.Generators.Messaging.BackplaneSubscriptionAttribute>
- <xref:PatternKit.Messaging.Routing.ContentRouter`2>
- <xref:PatternKit.Messaging.Routing.RecipientList`1>

Expand All @@ -130,3 +137,5 @@ Reliability helpers also have a generated path through `[GenerateReliabilityPipe
- `test/PatternKit.Examples.Tests/Messaging/ContentRouterGeneratorExampleTests.cs`
- `src/PatternKit.Examples/Messaging/RecipientListGeneratorExample.cs`
- `test/PatternKit.Examples.Tests/Messaging/RecipientListGeneratorExampleTests.cs`
- `src/PatternKit.Examples/Messaging/BackplaneFacadeDemo.cs`
- `test/PatternKit.Examples.Tests/Messaging/BackplaneFacadeDemoTests.cs`
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ public static IServiceCollection AddResilientCheckoutMailboxesExample(this IServ
public static IServiceCollection AddMessagingBackplaneFacadeExample(this IServiceCollection services)
{
services.AddSingleton(new MessagingBackplaneFacadeExample(BackplaneFacadeDemo.RunAsync));
return services.RegisterExample<MessagingBackplaneFacadeExample>("Messaging Backplane Facade", ExampleIntegrationSurface.GenericHost | ExampleIntegrationSurface.Messaging | ExampleIntegrationSurface.ExternalInfrastructure | ExampleIntegrationSurface.DependencyInjection);
return services.RegisterExample<MessagingBackplaneFacadeExample>("Messaging Backplane Facade", ExampleIntegrationSurface.GenericHost | ExampleIntegrationSurface.Messaging | ExampleIntegrationSurface.SourceGenerator | ExampleIntegrationSurface.ExternalInfrastructure | ExampleIntegrationSurface.DependencyInjection);
}

public static IServiceCollection AddPrototypeGameCharacterFactoryExample(this IServiceCollection services)
Expand Down
Loading
Loading