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
29 changes: 29 additions & 0 deletions docs/examples/generated-dead-letter-channel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Generated Dead Letter Channel

The generated dead-letter channel example models a checkout fulfillment boundary where failed warehouse or carrier messages are captured with reason, attempt count, original headers, and replay metadata.

Production applications can import the example through `IServiceCollection`:

```csharp
var services = new ServiceCollection();
services.AddFulfillmentDeadLetterChannelExample();

using var provider = services.BuildServiceProvider();
var workflow = provider.GetRequiredService<FulfillmentDeadLetterWorkflow>();

var summary = workflow.Capture(
FulfillmentDeadLetterChannelExample.CreateCommand("order-100"),
"carrier timeout");
```

The example includes:

- a fluent `DeadLetterChannel<FulfillmentCommand>` path
- a source-generated `[GenerateDeadLetterChannel]` path
- an importable `FulfillmentDeadLetterWorkflow`
- TinyBDD tests for fluent, generated, and DI usage

Files:

- `src/PatternKit.Examples/Messaging/FulfillmentDeadLetterChannelExample.cs`
- `test/PatternKit.Examples.Tests/Messaging/FulfillmentDeadLetterChannelExampleTests.cs`
3 changes: 3 additions & 0 deletions docs/examples/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@
- name: Generated Claim Check
href: generated-claim-check.md

- name: Generated Dead Letter Channel
href: generated-dead-letter-channel.md

- name: Generated Recipient List
href: generated-recipient-list.md

Expand Down
41 changes: 41 additions & 0 deletions docs/generators/dead-letter-channel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Dead Letter Channel Generator

`[GenerateDeadLetterChannel]` emits a factory for `DeadLetterChannel<TPayload>` from a partial host and a store factory method.

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

[GenerateDeadLetterChannel(
typeof(FulfillmentCommand),
FactoryName = "CreateChannel",
ChannelName = "fulfillment-dead-letter",
Source = "checkout.fulfillment",
IdPrefix = "fulfillment-dead")]
public static partial class FulfillmentDeadLetters
{
[DeadLetterStoreFactory]
private static IDeadLetterStore<FulfillmentCommand> CreateStore()
=> new InMemoryDeadLetterStore<FulfillmentCommand>();
}
```

The generated factory:

- creates a named dead-letter channel
- records the configured source in captured metadata
- uses the marked store factory for application-owned persistence
- generates deterministic ids from the configured prefix and message id
- enables exception detail capture by default

## Diagnostics

| ID | Meaning |
| --- | --- |
| `PKDL001` | The host type marked with `[GenerateDeadLetterChannel]` must be partial. |
| `PKDL002` | The host must declare exactly one `[DeadLetterStoreFactory]` method. |
| `PKDL003` | The store factory must be static, parameterless, and return `IDeadLetterStore<TPayload>`. |

## Example

See [Generated Dead Letter Channel](../examples/generated-dead-letter-channel.md).
9 changes: 9 additions & 0 deletions docs/generators/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ PatternKit includes a Roslyn incremental generator package (`PatternKit.Generato
| [**Message Envelope**](messaging.md#generated-message-envelope) | Required message metadata contracts | `[GenerateMessageEnvelope]` |
| [**Message Translator**](message-translator.md) | Partner and transport event normalization | `[GenerateMessageTranslator]` |
| [**Claim Check**](claim-check.md) | External payload storage references | `[GenerateClaimCheck]` |
| [**Dead Letter Channel**](dead-letter-channel.md) | Failed-message capture and replay handoff | `[GenerateDeadLetterChannel]` |
| [**Content Router**](messaging.md#generated-content-router) | Content-based message routing factories | `[GenerateContentRouter]` |
| [**Recipient List**](messaging.md#generated-recipient-list) | Recipient fan-out factories | `[GenerateRecipientList]` |
| [**Splitter / Aggregator**](messaging.md#generated-splitter-and-aggregator) | Split/rejoin message routing factories | `[GenerateSplitter]` / `[GenerateAggregator]` |
Expand Down Expand Up @@ -165,6 +166,14 @@ public static partial class LegacyOrderAcl { }
[GenerateMessageTranslator(typeof(PartnerOrderAccepted), typeof(CommerceOrderAccepted))]
public static partial class PartnerOrderTranslator { }

// Claim check - external payload storage reference
[GenerateClaimCheck(typeof(LargeOrderDocument), StoreName = "document-archive")]
public static partial class LargeDocumentClaims { }

// Dead-letter channel - failed message capture and replay handoff
[GenerateDeadLetterChannel(typeof(FulfillmentCommand), ChannelName = "fulfillment-dead-letter")]
public static partial class FulfillmentDeadLetters { }

// Visitor - type-safe double dispatch
[GenerateVisitor]
public interface IDocumentVisitor { }
Expand Down
27 changes: 26 additions & 1 deletion docs/generators/messaging.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# Messaging Generators

PatternKit includes twelve messaging-oriented source generators:
PatternKit includes thirteen messaging-oriented source generators:

- <xref:PatternKit.Generators.Messaging.GenerateDispatcherAttribute> for source-generated mediator dispatchers.
- <xref:PatternKit.Generators.Messaging.GenerateMessageEnvelopeAttribute> for required message-envelope contracts.
- <xref:PatternKit.Generators.Messaging.GenerateMessageTranslatorAttribute> for partner and transport message normalization.
- <xref:PatternKit.Generators.Messaging.GenerateClaimCheckAttribute> for external payload storage references.
- <xref:PatternKit.Generators.Messaging.GenerateDeadLetterChannelAttribute> for failed-message capture and replay handoff.
- <xref:PatternKit.Generators.Messaging.GenerateContentRouterAttribute> for content-based message routers.
- <xref:PatternKit.Generators.Messaging.GenerateRecipientListAttribute> for recipient-list fan-out.
- <xref:PatternKit.Generators.Messaging.GenerateSplitterAttribute> and <xref:PatternKit.Generators.Messaging.GenerateAggregatorAttribute> for split/rejoin routing.
Expand Down Expand Up @@ -105,6 +106,27 @@ Example source:
- `src/PatternKit.Examples/Messaging/LargeDocumentClaimCheckExample.cs`
- `test/PatternKit.Examples.Tests/Messaging/LargeDocumentClaimCheckExampleTests.cs`

## Generated Dead Letter Channel

`[GenerateDeadLetterChannel]` creates a `DeadLetterChannel<TPayload>` factory with a pluggable dead-letter store:

```csharp
[GenerateDeadLetterChannel(typeof(FulfillmentCommand), ChannelName = "fulfillment-dead-letter")]
public static partial class FulfillmentDeadLetters
{
[DeadLetterStoreFactory]
private static IDeadLetterStore<FulfillmentCommand> CreateStore()
=> new InMemoryDeadLetterStore<FulfillmentCommand>();
}
```

See [Dead Letter Channel Generator](dead-letter-channel.md) for diagnostics and examples.

Example source:

- `src/PatternKit.Examples/Messaging/FulfillmentDeadLetterChannelExample.cs`
- `test/PatternKit.Examples.Tests/Messaging/FulfillmentDeadLetterChannelExampleTests.cs`

## Generated Content Router

`[GenerateContentRouter]` creates a `ContentRouter<TPayload, TResult>` factory from static route methods:
Expand Down Expand Up @@ -346,6 +368,8 @@ Example source:
| --- | --- | --- |
| `PKDSP001`-`PKDSP004` | Dispatcher | Invalid dispatcher configuration or handler registration. |
| `PKME001`-`PKME004` | Message Envelope | Non-partial host, missing headers, invalid header configuration, or duplicate names. |
| `PKCC001`-`PKCC003` | Claim Check | Non-partial host, missing store factory, or invalid store factory signature. |
| `PKDL001`-`PKDL003` | Dead Letter Channel | Non-partial host, missing store factory, or invalid store factory signature. |
| `PKCR001`-`PKCR005` | Content Router | Non-partial host, missing routes, invalid signatures, duplicate defaults, or duplicate route identity. |
| `PKRL001`-`PKRL004` | Recipient List | Non-partial host, missing recipients, invalid signatures, or duplicate recipient identity. |
| `PKSA001`-`PKSA006` | Splitter / Aggregator | Non-partial host, missing contract methods, invalid signatures, or invalid duplicate policy. |
Expand All @@ -358,6 +382,7 @@ Example source:
## Related Runtime Patterns

- [Message Envelope and Context](../patterns/messaging/message-envelope.md)
- [Dead Letter Channel](../patterns/messaging/dead-letter-channel.md)
- [Enterprise Message Routing](../patterns/messaging/message-routing.md)
- [Routing Slip](../patterns/messaging/routing-slip.md)
- [Saga / Process Manager](../patterns/messaging/saga.md)
Expand Down
3 changes: 3 additions & 0 deletions docs/generators/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@
- name: Claim Check
href: claim-check.md

- name: Dead Letter Channel
href: dead-letter-channel.md

- name: Prototype
href: prototype.md

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 @@ -47,6 +47,7 @@ The source of truth is `PatternKitPatternCatalog` in `src/PatternKit.Examples/Pr
| Enterprise Integration | Message Envelope | `Message<TPayload>`, headers, context | Messaging generator |
| Enterprise Integration | Message Translator | `MessageTranslator<TInput, TOutput>` | Message Translator generator |
| Enterprise Integration | Claim Check | `ClaimCheck<TPayload>` | Claim Check generator |
| Enterprise Integration | Dead Letter Channel | `DeadLetterChannel<TPayload>` | Dead Letter Channel generator |
| Enterprise Integration | Content-Based Router | `ContentRouter<TPayload, TResult>` | Messaging generator |
| Enterprise Integration | Recipient List | `RecipientList<TPayload>` | Messaging generator |
| Enterprise Integration | Splitter | `Splitter<TIn, TOut>` | Messaging generator |
Expand Down
50 changes: 50 additions & 0 deletions docs/patterns/messaging/dead-letter-channel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Dead Letter Channel

Dead Letter Channel captures messages that cannot be processed or delivered after the owning pipeline has exhausted its normal handling path. PatternKit keeps the channel application-owned: you choose the store, source name, failure reason, attempt count, and replay handoff.

Use `DeadLetterChannel<TPayload>` when a message should be preserved for operations instead of being dropped or retried forever.

```csharp
var store = new InMemoryDeadLetterStore<FulfillmentCommand>();

var channel = DeadLetterChannel<FulfillmentCommand>.Create("fulfillment-dead-letter")
.FromSource("checkout.fulfillment")
.UseStore(store)
.UseIds((message, reason, context) => "fulfillment-dead:" + message.Headers.MessageId)
.Build();

var deadLetter = await channel.CaptureAsync(
command,
"carrier timeout",
exception,
attempts: 4,
cancellationToken: ct);
```

Captured messages preserve the original payload and headers, then add operational headers such as `dead-letter-id`, `dead-letter-channel`, `dead-letter-reason`, `dead-letter-attempts`, and `dead-letter-source`.

## Replay Handoff

Replay is explicit. `PrepareReplayAsync` loads the captured message and adds replay metadata without deleting the dead-letter record:

```csharp
var replay = await channel.PrepareReplayAsync(deadLetter.Id, ct);

if (replay.ReadyForReplay)
{
await fulfillmentInbox.ProcessAsync(replay.Message!, cancellationToken: ct);
}
```

Use a durable `IDeadLetterStore<TPayload>` implementation for production transport boundaries. `InMemoryDeadLetterStore<TPayload>` is intended for tests, samples, and embedded in-process usage.

## Source Generator

Use `[GenerateDeadLetterChannel]` when the channel name, source, and store factory are stable at compile time. See [Dead Letter Channel Generator](../../generators/dead-letter-channel.md).

## Example

The production-shaped fulfillment example demonstrates the fluent channel, generated channel, and `IServiceCollection` integration:

- `src/PatternKit.Examples/Messaging/FulfillmentDeadLetterChannelExample.cs`
- `test/PatternKit.Examples.Tests/Messaging/FulfillmentDeadLetterChannelExampleTests.cs`
2 changes: 2 additions & 0 deletions docs/patterns/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@
href: messaging/message-translator.md
- name: Claim Check
href: messaging/claim-check.md
- name: Dead Letter Channel
href: messaging/dead-letter-channel.md
- name: Enterprise Message Routing
href: messaging/message-routing.md
- name: Routing Slip
Expand Down
Loading
Loading