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
47 changes: 47 additions & 0 deletions docs/examples/generated-message-translator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Generated Message Translator

This example normalizes partner order events into an application-owned commerce event. It shows the fluent runtime path, the source-generated path, and the `IServiceCollection` integration an importing app can use.

Source:

- `src/PatternKit.Examples/Messaging/PartnerEventTranslatorExample.cs`
- `test/PatternKit.Examples.Tests/Messaging/PartnerEventTranslatorExampleTests.cs`

## Runtime Path

```csharp
var translator = PartnerOrderTranslatorPolicies.CreateFluentTranslator();
var result = translator.Translate(PartnerEventTranslatorExample.CreatePartnerMessage(
"partner-a",
"EXT-100",
125m));
```

The translator preserves correlation headers, drops the raw partner signature header, and writes the normalized content type.

## Generated Path

```csharp
[GenerateMessageTranslator(typeof(PartnerOrderAccepted), typeof(CommerceOrderAccepted), TranslatorName = "partner-order-translator")]
[MessageTranslatorDropHeader("raw-signature")]
[MessageTranslatorHeader(MessageHeaderNames.ContentType, "application/vnd.patternkit.commerce-order-accepted+json")]
public static partial class GeneratedPartnerOrderTranslator;
```

The generated factory returns the same runtime translator type:

```csharp
var translator = GeneratedPartnerOrderTranslator.Create();
var result = translator.Translate(partnerMessage);
```

## DI Integration

```csharp
services.AddPartnerEventTranslatorExample();

var importer = provider.GetRequiredService<PartnerOrderImportService>();
var summary = importer.Import(partnerMessage);
```

`AddPatternKitExamples()` also registers this example through `GeneratedMessageTranslatorExample` so catalog consumers can resolve it with the rest of the production-ready examples.
3 changes: 3 additions & 0 deletions docs/examples/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@
- name: Generated Message Envelope
href: generated-message-envelope.md

- name: Generated Message Translator
href: generated-message-translator.md

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

Expand Down
5 changes: 5 additions & 0 deletions docs/generators/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ PatternKit includes a Roslyn incremental generator package (`PatternKit.Generato
|---|---|---|
| [**Dispatcher**](dispatcher.md) | Mediator pattern with commands, notifications, and streams | `[GenerateDispatcher]` |
| [**Message Envelope**](messaging.md#generated-message-envelope) | Required message metadata contracts | `[GenerateMessageEnvelope]` |
| [**Message Translator**](message-translator.md) | Partner and transport event normalization | `[GenerateMessageTranslator]` |
| [**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 @@ -159,6 +160,10 @@ public abstract partial class DataProcessor { }
[GenerateAntiCorruptionLayer(typeof(LegacyOrderDto), typeof(CommerceOrder))]
public static partial class LegacyOrderAcl { }

// Message translator - partner event normalization
[GenerateMessageTranslator(typeof(PartnerOrderAccepted), typeof(CommerceOrderAccepted))]
public static partial class PartnerOrderTranslator { }

// Visitor - type-safe double dispatch
[GenerateVisitor]
public interface IDocumentVisitor { }
Expand Down
38 changes: 38 additions & 0 deletions docs/generators/message-translator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Message Translator Generator

`[GenerateMessageTranslator]` creates a factory for `MessageTranslator<TInput,TOutput>`.

Use it when partner or transport event normalization is part of the application contract and should be validated by the compiler.

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

[GenerateMessageTranslator(typeof(PartnerOrderAccepted), typeof(CommerceOrderAccepted), FactoryName = "Build")]
[MessageTranslatorDropHeader("raw-signature")]
[MessageTranslatorHeader(MessageHeaderNames.ContentType, "application/vnd.myapp.order+json")]
public static partial class PartnerOrderTranslator
{
[MessageTranslatorHandler]
private static CommerceOrderAccepted Translate(Message<PartnerOrderAccepted> message, MessageContext context)
=> new($"commerce-{message.Payload.ExternalOrderId}", message.Payload.Amount, message.Payload.PartnerId);
}
```

Generated output:

```csharp
var translator = PartnerOrderTranslator.Build();
var result = translator.Translate(partnerMessage);
```

## Diagnostics

- `PKMT001`: the translator host must be partial.
- `PKMT002`: exactly one `[MessageTranslatorHandler]` method is required.
- `PKMT003`: the handler must be static, return the output payload type, and accept `Message<TInput>` plus `MessageContext`.

## Example

- `src/PatternKit.Examples/Messaging/PartnerEventTranslatorExample.cs`
- `test/PatternKit.Examples.Tests/Messaging/PartnerEventTranslatorExampleTests.cs`
29 changes: 28 additions & 1 deletion docs/generators/messaging.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Messaging Generators

PatternKit includes ten messaging-oriented source generators:
PatternKit includes eleven 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.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 @@ -56,6 +57,32 @@ Example source:
- `src/PatternKit.Examples/Messaging/MessageEnvelopeExample.cs`
- `test/PatternKit.Examples.Tests/Messaging/MessageEnvelopeExampleTests.cs`

## Generated Message Translator

`[GenerateMessageTranslator]` creates a `MessageTranslator<TInput,TOutput>` factory from one static handler method:

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

[GenerateMessageTranslator(typeof(PartnerOrderAccepted), typeof(CommerceOrderAccepted))]
[MessageTranslatorDropHeader("raw-signature")]
[MessageTranslatorHeader(MessageHeaderNames.ContentType, "application/vnd.myapp.order+json")]
public static partial class PartnerOrderTranslator
{
[MessageTranslatorHandler]
private static CommerceOrderAccepted Translate(Message<PartnerOrderAccepted> message, MessageContext context)
=> new($"commerce-{message.Payload.ExternalOrderId}", message.Payload.Amount, message.Payload.PartnerId);
}
```

The generated factory preserves headers by default, then applies declared drop/set policies. See [Message Translator Generator](message-translator.md) for diagnostics and examples.

Example source:

- `src/PatternKit.Examples/Messaging/PartnerEventTranslatorExample.cs`
- `test/PatternKit.Examples.Tests/Messaging/PartnerEventTranslatorExampleTests.cs`

## Generated Content Router

`[GenerateContentRouter]` creates a `ContentRouter<TPayload, TResult>` factory from static route methods:
Expand Down
3 changes: 3 additions & 0 deletions docs/generators/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@
- name: Messaging Generators
href: messaging.md

- name: Message Translator
href: message-translator.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 @@ -45,6 +45,7 @@ The source of truth is `PatternKitPatternCatalog` in `src/PatternKit.Examples/Pr
| Family | Pattern | Fluent/runtime path | Source-generated path |
| --- | --- | --- | --- |
| Enterprise Integration | Message Envelope | `Message<TPayload>`, headers, context | Messaging generator |
| Enterprise Integration | Message Translator | `MessageTranslator<TInput, TOutput>` | Message Translator 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
74 changes: 74 additions & 0 deletions docs/patterns/messaging/message-translator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Message Translator

`MessageTranslator<TInput,TOutput>` translates one message contract into another while applying an explicit header policy. Use it at integration boundaries where partner, vendor, or transport-specific events need to become application-owned contracts before they enter routers, sagas, mailboxes, or reliability pipelines.

## Runtime Path

```csharp
using PatternKit.Messaging;
using PatternKit.Messaging.Transformation;

var translator = MessageTranslator<PartnerOrderAccepted, CommerceOrderAccepted>
.Create("partner-order-translator")
.TranslateWith(static (message, context) => new CommerceOrderAccepted(
$"commerce-{message.Payload.ExternalOrderId}",
message.Payload.Amount,
message.Payload.PartnerId))
.DropHeader("raw-signature")
.SetHeader(MessageHeaderNames.ContentType, "application/vnd.myapp.order+json")
.Build();

var result = translator.Translate(partnerMessage);
```

The translator returns `MessageTranslationResult<TOutput>` instead of leaking transformation exceptions into routing code. Invalid translator output or header policy failures produce a failed result with the captured exception.

## Header Policies

Headers are preserved by default so correlation, causation, message identifiers, and tenant metadata survive the contract change. Fluent policies can remove sensitive transport headers, keep only an allow-list, or set normalized headers:

```csharp
var translator = MessageTranslator<RawEvent, NormalizedEvent>
.Create("normalizer")
.TranslateWith(static (message, _) => new NormalizedEvent(message.Payload.Id))
.KeepHeaders(MessageHeaderNames.CorrelationId, "tenant-id")
.SetHeader(MessageHeaderNames.ContentType, "application/vnd.myapp.normalized+json")
.Build();
```

## Source-Generated Path

Use `[GenerateMessageTranslator]` when a translator contract should be compile-time visible and reusable from dependency injection setup:

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

[GenerateMessageTranslator(typeof(PartnerOrderAccepted), typeof(CommerceOrderAccepted), TranslatorName = "partner-order-translator")]
[MessageTranslatorDropHeader("raw-signature")]
[MessageTranslatorHeader(MessageHeaderNames.ContentType, "application/vnd.myapp.order+json")]
public static partial class PartnerOrderTranslator
{
[MessageTranslatorHandler]
private static CommerceOrderAccepted Translate(Message<PartnerOrderAccepted> message, MessageContext context)
=> new($"commerce-{message.Payload.ExternalOrderId}", message.Payload.Amount, message.Payload.PartnerId);
}
```

The generated factory returns `MessageTranslator<PartnerOrderAccepted, CommerceOrderAccepted>` and emits the same runtime builder calls.

## DI Integration

Examples can be imported through `Microsoft.Extensions.DependencyInjection`:

```csharp
services.AddPartnerEventTranslatorExample();

var service = provider.GetRequiredService<PartnerOrderImportService>();
var summary = service.Import(partnerMessage);
```

Production example:

- `src/PatternKit.Examples/Messaging/PartnerEventTranslatorExample.cs`
- `test/PatternKit.Examples.Tests/Messaging/PartnerEventTranslatorExampleTests.cs`
2 changes: 2 additions & 0 deletions docs/patterns/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,8 @@
items:
- name: Message Envelope and Context
href: messaging/message-envelope.md
- name: Message Translator
href: messaging/message-translator.md
- name: Enterprise Message Routing
href: messaging/message-routing.md
- name: Routing Slip
Expand Down
Loading
Loading