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
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -446,15 +446,15 @@ var cachedRemoteProxy = Proxy<int, string>.Create(id => remoteProxy.Execute(id))
---

## Patterns Table
PatternKit currently tracks 94 production-readiness patterns. Each catalog pattern is represented in tests, documentation, real-world examples, IoC integration, and the BenchmarkDotNet coverage matrix.
PatternKit currently tracks 95 production-readiness patterns. Each catalog pattern is represented in tests, documentation, real-world examples, IoC integration, and the BenchmarkDotNet coverage matrix.

| Category | Count | Patterns |
| --- | ---: | --- |
| Application Architecture | 16 | Activity Tracker, Anti-Corruption Layer, Audit Log, CQRS, Data Mapper, Domain Event, Event Sourcing, Feature Toggle, Identity Map, Materialized View, Repository, Service Layer, Specification, Table Data Gateway, Transaction Script, Unit of Work |
| Behavioral | 11 | Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor |
| Cloud Architecture | 17 | Ambassador, Backends for Frontends, Bulkhead, Cache-Aside, Circuit Breaker, External Configuration Store, Gateway Aggregation, Gateway Routing, Health Endpoint Monitoring, Leader Election, Priority Queue, Queue-Based Load Leveling, Rate Limiting, Retry, Scheduler Agent Supervisor, Sidecar, Strangler Fig |
| Creational | 5 | Abstract Factory, Builder, Factory Method, Prototype, Singleton |
| Enterprise Integration | 35 | Aggregator, Canonical Data Model, Channel Adapter, Channel Purger, Claim Check, Competing Consumers, Content-Based Router, Control Bus, Dead Letter Channel, Durable Subscriber, Dynamic Router, Event Notification, Event-Carried State Transfer, Event-Driven Consumer, Invalid Message Channel, Mailbox, Message Bus, Message Channel, Message Envelope, Message Filter, Message Store, Message Translator, Messaging Gateway, Pipes and Filters, Polling Consumer, Publish-Subscribe, Recipient List, Request-Reply, Resequencer, Routing Slip, Saga / Process Manager, Scatter-Gather, Service Activator, Splitter, Wire Tap |
| Enterprise Integration | 36 | Aggregator, Canonical Data Model, Channel Adapter, Channel Purger, Claim Check, Competing Consumers, Content-Based Router, Control Bus, Dead Letter Channel, Durable Subscriber, Dynamic Router, Event Notification, Event-Carried State Transfer, Event-Driven Consumer, Invalid Message Channel, Mailbox, Message Bus, Message Channel, Message Envelope, Message Filter, Message Store, Message Translator, Messaging Bridge, Messaging Gateway, Pipes and Filters, Polling Consumer, Publish-Subscribe, Recipient List, Request-Reply, Resequencer, Routing Slip, Saga / Process Manager, Scatter-Gather, Service Activator, Splitter, Wire Tap |
| Messaging Reliability | 3 | Idempotent Receiver, Inbox, Outbox |
| Structural | 7 | Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy |

Expand Down Expand Up @@ -520,6 +520,8 @@ BenchmarkDotNet guidance is documented in [docs/guides/benchmarks.md](docs/guide
| Dynamic Router | Execution | 406.7 ns | 2.83 KB | 415.2 ns | 2.83 KB | Same allocation; fluent was slightly faster for fulfillment route-table dispatch. |
| Message Bus | Construction | 186.7 ns | 1,264 B | 160.0 ns | 904 B | Generated reduced construction time and allocation for topic bus topology setup. |
| Message Bus | Execution | 503.8 ns | 3,192 B | 587.7 ns | 3,232 B | Fluent was faster and allocated slightly less for publishing order events to topic subscribers. |
| Messaging Bridge | Construction | 184.0 ns | 1,328 B | 185.5 ns | 1,328 B | Same allocation; fluent and generated bridge construction were effectively equivalent. |
| Messaging Bridge | Execution | 666.8 ns | 3,912 B | 670.8 ns | 3,912 B | Same allocation; fluent was slightly faster for partner order imports. |
| Decorator | Construction | 34.293 ns | 264 B | 17.669 ns | 168 B | Generated decorator composition was faster and allocated less. |
| Decorator | Execution | 60.765 ns | 384 B | 35.551 ns | 304 B | Generated decorator execution was faster and allocated less for decorated storage reads. |
| Domain Event | Construction | 199.5 ns | 1.34 KB | 157.6 ns | 1.04 KB | Generated reduced construction time and allocation in this microbenchmark. |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using BenchmarkDotNet.Attributes;
using PatternKit.Examples.Messaging;
using PatternKit.Messaging.Bridges;
using PatternKit.Messaging.Channels;

namespace PatternKit.Benchmarks.Messaging;

[BenchmarkCategory("EnterpriseIntegration", "Messaging", "MessagingBridge")]
public class MessagingBridgeBenchmarks
{
private static readonly PartnerBridgeOrder[] Orders =
[
new("P-100", "accepted", 125m),
new("P-101", "paid", 250m)
];

[Benchmark(Baseline = true, Description = "Fluent: create messaging bridge")]
[BenchmarkCategory("Fluent", "Construction")]
public MessagingBridge<PartnerBridgeOrder, CommerceBridgeOrderEvent> Fluent_CreateMessagingBridge()
{
var partnerOrders = MessageChannel<PartnerBridgeOrder>.Create("partner-orders").Build();
var commerceEvents = MessageChannel<CommerceBridgeOrderEvent>.Create("commerce-events").Build();
var commerceBus = MessageBus<CommerceBridgeOrderEvent>.Create("commerce-bus")
.Route("accepted", commerceEvents)
.Route("paid", commerceEvents)
.Build();
return PartnerOrderMessagingBridges.Create(partnerOrders, commerceBus);
}

[Benchmark(Description = "Generated: create messaging bridge")]
[BenchmarkCategory("Generated", "Construction")]
public MessagingBridge<PartnerBridgeOrder, CommerceBridgeOrderEvent> Generated_CreateMessagingBridge()
{
var partnerOrders = MessageChannel<PartnerBridgeOrder>.Create("partner-orders").Build();
var commerceEvents = MessageChannel<CommerceBridgeOrderEvent>.Create("commerce-events").Build();
var commerceBus = MessageBus<CommerceBridgeOrderEvent>.Create("commerce-bus")
.Route("accepted", commerceEvents)
.Route("paid", commerceEvents)
.Build();
return GeneratedPartnerOrderMessagingBridge.Create()
.From(partnerOrders)
.To(commerceBus)
.PreserveHeaders(static order => new CommerceBridgeOrderEvent(order.PartnerOrderId, order.State, order.Amount))
.SelectTopic(static message => message.Payload.State == "paid" ? "paid" : "accepted")
.Build();
}

[Benchmark(Description = "Fluent: bridge partner orders")]
[BenchmarkCategory("Fluent", "Execution")]
public PartnerOrderMessagingBridgeSummary Fluent_BridgePartnerOrders()
=> PartnerOrderMessagingBridgeExampleRunner.RunFluent(Orders);

[Benchmark(Description = "Generated: bridge partner orders")]
[BenchmarkCategory("Generated", "Execution")]
public PartnerOrderMessagingBridgeSummary Generated_BridgePartnerOrders()
=> PartnerOrderMessagingBridgeExampleRunner.RunGeneratedStatic(Orders);
}
11 changes: 11 additions & 0 deletions docs/examples/partner-order-messaging-bridge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Partner Order Messaging Bridge

This example models a partner order stream feeding an internal commerce bus.

- Partner messages arrive on `MessageChannel<PartnerBridgeOrder>`.
- `MessagingBridge<PartnerBridgeOrder, CommerceBridgeOrderEvent>` maps partner payloads into commerce events.
- Existing message headers are preserved, including correlation identifiers.
- The bridge selects `accepted` or `paid` target topics and publishes into `MessageBus<CommerceBridgeOrderEvent>`.
- `AddPartnerOrderMessagingBridgeDemo()` imports the generated bridge path through `IServiceCollection`.

The example is implemented in `src/PatternKit.Examples/Messaging/PartnerOrderMessagingBridgeExample.cs` and validated by TinyBDD tests in `test/PatternKit.Examples.Tests/Messaging/PartnerOrderMessagingBridgeExampleTests.cs`.
3 changes: 3 additions & 0 deletions docs/examples/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@
- name: Order Dynamic Router
href: order-dynamic-router.md

- name: Partner Order Messaging Bridge
href: partner-order-messaging-bridge.md

- name: Order Wire Tap
href: order-wire-tap.md

Expand Down
1 change: 1 addition & 0 deletions docs/generators/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ PatternKit includes a Roslyn incremental generator package (`PatternKit.Generato
| [**Dispatcher**](dispatcher.md) | Mediator pattern with commands, notifications, and streams | `[GenerateDispatcher]` |
| [**Message Channel**](message-channel.md) | Typed channel factories for in-process message queues | `[GenerateMessageChannel]` |
| [**Message Bus**](message-bus.md) | Topic bus topology factories over message channels | `[GenerateMessageBus]` |
| [**Messaging Bridge**](messaging-bridge.md) | Channel-to-bus bridge factories for topology boundaries | `[GenerateMessagingBridge]` |
| [**Channel Purger**](channel-purger.md) | Channel maintenance purger factories for in-process message queues | `[GenerateChannelPurger]` |
| [**Invalid Message Channel**](invalid-message-channel.md) | Invalid-message channel builder factories for validation boundaries | `[GenerateInvalidMessageChannel]` |
| [**Polling Consumer**](polling-consumer.md) | Pull-based message consumer factories | `[GeneratePollingConsumer]` |
Expand Down
30 changes: 30 additions & 0 deletions docs/generators/messaging-bridge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Messaging Bridge Generator

`[GenerateMessagingBridge]` emits the typed bridge factory for a channel-to-bus integration boundary.

```csharp
[GenerateMessagingBridge(
typeof(PartnerBridgeOrder),
typeof(CommerceBridgeOrderEvent),
FactoryName = "Create",
BridgeName = "partner-order-bridge")]
public static partial class GeneratedPartnerOrderMessagingBridge;
```

The generated factory returns a `MessagingBridge<TInbound,TOutbound>.Builder`, so application code still supplies concrete channels, target bus, translation, and topic selection through fluent composition:

```csharp
var bridge = GeneratedPartnerOrderMessagingBridge.Create()
.From(partnerOrders)
.To(commerceBus)
.PreserveHeaders(order => new CommerceBridgeOrderEvent(order.PartnerOrderId, order.State, order.Amount))
.SelectTopic(message => message.Payload.State)
.Build();
```

Diagnostics:

| ID | Meaning |
| --- | --- |
| `PKMBR001` | The host type must be `partial`. |
| `PKMBR002` | `FactoryName` and `BridgeName` must be non-empty. |
3 changes: 3 additions & 0 deletions docs/generators/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@
- name: Message Bus
href: message-bus.md

- name: Messaging Bridge
href: messaging-bridge.md

- name: Channel Purger
href: channel-purger.md

Expand Down
10 changes: 7 additions & 3 deletions docs/guides/benchmark-results.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ The latest measured timings below were captured on Windows 11, Intel Core i9-149
| Dynamic Router | Execution | 406.7 ns | 2.83 KB | 415.2 ns | 2.83 KB | Same allocation; fluent was slightly faster for fulfillment route-table dispatch. |
| Message Bus | Construction | 186.7 ns | 1,264 B | 160.0 ns | 904 B | Generated reduced construction time and allocation for topic bus topology setup. |
| Message Bus | Execution | 503.8 ns | 3,192 B | 587.7 ns | 3,232 B | Fluent was faster and allocated slightly less for publishing order events to topic subscribers. |
| Messaging Bridge | Construction | 184.0 ns | 1,328 B | 185.5 ns | 1,328 B | Same allocation; fluent and generated bridge construction were effectively equivalent. |
| Messaging Bridge | Execution | 666.8 ns | 3,912 B | 670.8 ns | 3,912 B | Same allocation; fluent was slightly faster for partner order imports. |
| Decorator | Construction | 34.293 ns | 264 B | 17.669 ns | 168 B | Generated decorator composition was faster and allocated less. |
| Decorator | Execution | 60.765 ns | 384 B | 35.551 ns | 304 B | Generated decorator execution was faster and allocated less for decorated storage reads. |
| Domain Event | Construction | 199.5 ns | 1.34 KB | 157.6 ns | 1.04 KB | Generated reduced construction time and allocation in this microbenchmark. |
Expand Down Expand Up @@ -206,19 +208,19 @@ The latest measured timings below were captured on Windows 11, Intel Core i9-149

## Coverage Matrix Summary

The coverage matrix currently publishes 94 catalog patterns and 376 pattern route results. Each pattern has four BenchmarkDotNet routes: fluent construction, fluent execution, source-generated construction, and source-generated execution.
The coverage matrix currently publishes 95 catalog patterns and 380 pattern route results. Each pattern has four BenchmarkDotNet routes: fluent construction, fluent execution, source-generated construction, and source-generated execution.

| Category | Patterns | Published route results |
| --- | ---: | ---: |
| Application Architecture | 16 | 64 |
| Behavioral | 11 | 44 |
| Cloud Architecture | 17 | 68 |
| Creational | 5 | 20 |
| Enterprise Integration | 34 | 136 |
| Enterprise Integration | 36 | 144 |
| Messaging Reliability | 3 | 12 |
| Structural | 7 | 28 |

The generator matrix currently publishes 90 generator source route results.
The generator matrix currently publishes 91 generator source route results.

## Pattern Matrix Results

Expand Down Expand Up @@ -286,6 +288,7 @@ The generator matrix currently publishes 90 generator source route results.
| Enterprise Integration | Durable Subscriber | Covered | Covered | Covered | Covered |
| Enterprise Integration | Dynamic Router | Covered | Covered | Covered | Covered |
| Enterprise Integration | Message Bus | Covered | Covered | Covered | Covered |
| Enterprise Integration | Messaging Bridge | Covered | Covered | Covered | Covered |
| Enterprise Integration | Event Notification | Covered | Covered | Covered | Covered |
| Enterprise Integration | Event-Carried State Transfer | Covered | Covered | Covered | Covered |
| Enterprise Integration | Event-Driven Consumer | Covered | Covered | Covered | Covered |
Expand Down Expand Up @@ -375,6 +378,7 @@ The generator matrix currently publishes 90 generator source route results.
| EventDrivenConsumerGenerator | `src/PatternKit.Generators/Messaging/EventDrivenConsumerGenerator.cs` | Covered |
| MailboxGenerator | `src/PatternKit.Generators/Messaging/MailboxGenerator.cs` | Covered |
| MessageBusGenerator | `src/PatternKit.Generators/Messaging/MessageBusGenerator.cs` | Covered |
| MessagingBridgeGenerator | `src/PatternKit.Generators/Messaging/MessagingBridgeGenerator.cs` | Covered |
| MessageChannelGenerator | `src/PatternKit.Generators/Messaging/MessageChannelGenerator.cs` | Covered |
| MessageEnvelopeGenerator | `src/PatternKit.Generators/Messaging/MessageEnvelopeGenerator.cs` | Covered |
| MessageFilterGenerator | `src/PatternKit.Generators/Messaging/MessageFilterGenerator.cs` | Covered |
Expand Down
2 changes: 2 additions & 0 deletions docs/guides/benchmarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ The following numbers were captured on Windows 11, Intel Core i9-14900K, .NET SD
| Durable Subscriber | Execution | 711.81 ns | 3,912 B | 605.65 ns | 3,128 B | Generated reduced catch-up projection replay time and allocation in this microbenchmark. |
| Dynamic Router | Construction | 213.4 ns | 1.29 KB | 214.0 ns | 1.29 KB | Fluent and generated route-table construction were effectively equivalent. |
| Dynamic Router | Execution | 406.7 ns | 2.83 KB | 415.2 ns | 2.83 KB | Same allocation; fluent was slightly faster for fulfillment route-table dispatch. |
| Messaging Bridge | Construction | 184.0 ns | 1,328 B | 185.5 ns | 1,328 B | Same allocation; fluent and generated bridge construction were effectively equivalent. |
| Messaging Bridge | Execution | 666.8 ns | 3,912 B | 670.8 ns | 3,912 B | Same allocation; fluent was slightly faster for partner order imports. |
| Decorator | Construction | 34.293 ns | 264 B | 17.669 ns | 168 B | Generated decorator composition was faster and allocated less. |
| Decorator | Execution | 60.765 ns | 384 B | 35.551 ns | 304 B | Generated decorator execution was faster and allocated less for decorated storage reads. |
| Domain Event | Construction | 199.5 ns | 1.34 KB | 157.6 ns | 1.04 KB | Generated reduced construction time and allocation in this microbenchmark. |
Expand Down
1 change: 1 addition & 0 deletions docs/guides/pattern-coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ The source of truth is `PatternKitPatternCatalog` in `src/PatternKit.Examples/Pr
| Enterprise Integration | Content-Based Router | `ContentRouter<TPayload, TResult>` | Messaging generator |
| Enterprise Integration | Dynamic Router | `DynamicRouter<TPayload, TResult>` | Dynamic Router generator |
| Enterprise Integration | Message Bus | `MessageBus<TPayload>` | Message Bus generator |
| Enterprise Integration | Messaging Bridge | `MessagingBridge<TInbound,TOutbound>` | Messaging Bridge generator |
| Enterprise Integration | Message Filter | `MessageFilter<TPayload>` | Message Filter generator |
| Enterprise Integration | Message Store | `MessageStore<TPayload>` | Message Store generator |
| Enterprise Integration | Wire Tap | `WireTap<TPayload>` | Wire Tap generator |
Expand Down
19 changes: 19 additions & 0 deletions docs/patterns/messaging/messaging-bridge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Messaging Bridge

Messaging Bridge connects two independent message topologies through an explicit translation boundary. Use it when an external channel, partner bus, or legacy integration stream must feed an internal `MessageBus<T>` without leaking transport-specific payloads into the target model.

PatternKit's bridge reads from a `MessageChannel<TInbound>`, translates the full message envelope to `Message<TOutbound>`, selects the target topic, and publishes into the target bus. The `PreserveHeaders` helper keeps message metadata such as correlation identifiers while mapping only the payload.

```csharp
var bridge = MessagingBridge<PartnerBridgeOrder, CommerceBridgeOrderEvent>
.Create("partner-order-bridge")
.From(partnerOrders)
.To(commerceBus)
.PreserveHeaders(order => new CommerceBridgeOrderEvent(order.PartnerOrderId, order.State, order.Amount))
.SelectTopic(message => message.Payload.State == "paid" ? "paid" : "accepted")
.Build();

var results = bridge.BridgeAll();
```

Prefer this pattern when the important design decision is the boundary between topologies. If the payload type stays the same and only channel endpoints change, use Channel Adapter. If the main concern is payload normalization, use Message Translator or Canonical Data Model.
4 changes: 4 additions & 0 deletions docs/patterns/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,10 @@
href: messaging/durable-subscriber.md
- name: Dynamic Router
href: messaging/dynamic-router.md
- name: Message Bus
href: messaging/message-bus.md
- name: Messaging Bridge
href: messaging/messaging-bridge.md
- name: Channel Adapter
href: messaging/channel-adapter.md
- name: Messaging Gateway
Expand Down
Loading
Loading