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 @@ -473,11 +473,11 @@ var cachedRemoteProxy = Proxy<int, string>.Create(id => remoteProxy.Execute(id))
---

## Patterns Table
PatternKit currently tracks 102 production-readiness patterns. Each catalog pattern is represented in tests, documentation, real-world examples, IoC integration, and the BenchmarkDotNet coverage matrix.
PatternKit currently tracks 103 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 | 17 | 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, Value Object |
| Application Architecture | 18 | Activity Tracker, Aggregate Root, 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, Value Object |
| 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 |
Expand All @@ -497,6 +497,8 @@ BenchmarkDotNet guidance is documented in [docs/guides/benchmarks.md](docs/guide
| Adapter | Execution | 59.084 ns | 416 B | 20.479 ns | 80 B | Generated adapter execution was faster and allocated less for shipment adaptation. |
| Activity Tracker | Construction | 13.09 ns | 152 B | 12.98 ns | 152 B | Same allocation; generated was slightly faster in this microbenchmark. |
| Activity Tracker | Execution | 446.88 ns | 1,656 B | 452.36 ns | 1,656 B | Same allocation; fluent was slightly faster for dashboard loading gates. |
| Aggregate Root | Construction | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Aggregate Root | Execution | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Aggregator | Construction | 14.562 ns | 168 B | 15.235 ns | 168 B | Same allocation; fluent was slightly faster in this microbenchmark. |
| Aggregator | Execution | 188.000 ns | 1,088 B | 200.564 ns | 1,088 B | Same allocation; fluent was faster for order line aggregation. |
| Ambassador | Construction | 55.42 ns | 448 B | 48.03 ns | 360 B | Generated reduced construction time and allocation in this microbenchmark. |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using BenchmarkDotNet.Attributes;
using PatternKit.Application.Aggregates;
using PatternKit.Examples.AggregateRootDemo;

namespace PatternKit.Benchmarks.Application;

[BenchmarkCategory("ApplicationArchitecture", "AggregateRoot")]
public class AggregateRootBenchmarks
{
private readonly AggregateCommandHandler<OrderAggregateRootDemo.OrderAggregate, OrderAggregateRootDemo.OrderCommand, OrderAggregateRootDemo.IOrderEvent> _fluent =
OrderAggregateRootDemo.CreateFluentHandler();

private readonly AggregateCommandHandler<OrderAggregateRootDemo.OrderAggregate, OrderAggregateRootDemo.OrderCommand, OrderAggregateRootDemo.IOrderEvent> _generated =
OrderAggregateRootDemo.CreateGeneratedHandler();

[Benchmark(Baseline = true, Description = "Fluent: create aggregate command handler")]
[BenchmarkCategory("Fluent", "Construction")]
public AggregateCommandHandler<OrderAggregateRootDemo.OrderAggregate, OrderAggregateRootDemo.OrderCommand, OrderAggregateRootDemo.IOrderEvent> Fluent_CreateHandler()
=> OrderAggregateRootDemo.CreateFluentHandler();

[Benchmark(Description = "Generated: create aggregate command handler")]
[BenchmarkCategory("Generated", "Construction")]
public AggregateCommandHandler<OrderAggregateRootDemo.OrderAggregate, OrderAggregateRootDemo.OrderCommand, OrderAggregateRootDemo.IOrderEvent> Generated_CreateHandler()
=> OrderAggregateRootDemo.CreateGeneratedHandler();

[Benchmark(Description = "Fluent: execute aggregate workflow")]
[BenchmarkCategory("Fluent", "Execution")]
public OrderAggregateRootDemo.OrderSummary Fluent_ExecuteWorkflow()
=> OrderAggregateRootDemo.ExecuteOrder(_fluent);

[Benchmark(Description = "Generated: execute aggregate workflow")]
[BenchmarkCategory("Generated", "Execution")]
public OrderAggregateRootDemo.OrderSummary Generated_ExecuteWorkflow()
=> OrderAggregateRootDemo.ExecuteOrder(_generated);
}
30 changes: 30 additions & 0 deletions docs/examples/order-aggregate-root-pattern.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Order Aggregate Root Pattern

This example demonstrates a production-style Aggregate Root for order placement and payment. It includes a fluent command handler, a source-generated command handler, TinyBDD tests, and an `IServiceCollection` extension.

## Import

```csharp
using Microsoft.Extensions.DependencyInjection;
using PatternKit.Examples.AggregateRootDemo;

var services = new ServiceCollection();
services.AddOrderAggregateRootDemo();

using var provider = services.BuildServiceProvider(validateScopes: true);
var service = provider.GetRequiredService<OrderAggregateRootService>();
```

## Run

```csharp
var summary = service.Run();
```

The service uses the generated `AggregateCommandHandler<OrderAggregate, OrderCommand, IOrderEvent>`. The fluent route uses the same command decision and event application functions, so teams can compare runtime composition with generated factories without changing domain behavior.

## Production Notes

- Keep command decision pure: inspect aggregate state and return events.
- Keep event application deterministic: event data should be enough to mutate aggregate state.
- Persist and publish `UncommittedEvents` after the unit of work commits, then call `MarkCommitted()`.
3 changes: 3 additions & 0 deletions docs/examples/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@
- name: Loan Approval Specifications
href: loan-approval-specifications.md

- name: Order Aggregate Root Pattern
href: order-aggregate-root-pattern.md

- name: Order Value Object Pattern
href: order-value-object-pattern.md

Expand Down
56 changes: 56 additions & 0 deletions docs/generators/aggregate-root.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Aggregate Root Generator

The Aggregate Root generator turns a decision method and event applier into a typed aggregate command handler factory.

## Usage

```csharp
using PatternKit.Generators.Aggregates;

[GenerateAggregateCommandHandler(
typeof(OrderAggregate),
typeof(OrderCommand),
typeof(IOrderEvent),
HandlerName = "orders")]
public static partial class OrderHandlers
{
[AggregateDecision]
private static IEnumerable<IOrderEvent> Decide(OrderAggregate aggregate, OrderCommand command)
=> OrderDecisions.Decide(aggregate, command);

[AggregateEventApplier]
private static void Apply(OrderAggregate aggregate, IOrderEvent domainEvent)
=> aggregate.Record(domainEvent);
}
```

Generated output:

```csharp
var handler = OrderHandlers.Create();
var result = handler.Execute(order, command);
```

## Method Shape

Decision methods must be static and return `IEnumerable<TEvent>`:

```csharp
static IEnumerable<TEvent> Decide(TAggregate aggregate, TCommand command)
```

Event appliers must be static void methods:

```csharp
static void Apply(TAggregate aggregate, TEvent domainEvent)
```

## Diagnostics

| ID | Meaning |
|---|---|
| `PKAGG001` | Host type must be `partial`. |
| `PKAGG002` | Host type must declare exactly one `[AggregateDecision]` method. |
| `PKAGG003` | Host type must declare exactly one `[AggregateEventApplier]` method. |
| `PKAGG004` | Decision method signature is invalid. |
| `PKAGG005` | Event applier method signature is invalid. |
3 changes: 3 additions & 0 deletions docs/generators/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
- name: Activity Tracker
href: activity-tracker.md

- name: Aggregate Root
href: aggregate-root.md

- name: Audit Log
href: audit-log.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 @@ -17,6 +17,8 @@ The latest measured timings below were captured on Windows 11, Intel Core i9-149
| Adapter | Execution | 59.084 ns | 416 B | 20.479 ns | 80 B | Generated adapter execution was faster and allocated less for shipment adaptation. |
| Activity Tracker | Construction | 13.09 ns | 152 B | 12.98 ns | 152 B | Same allocation; generated was slightly faster in this microbenchmark. |
| Activity Tracker | Execution | 446.88 ns | 1,656 B | 452.36 ns | 1,656 B | Same allocation; fluent was slightly faster for dashboard loading gates. |
| Aggregate Root | Construction | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Aggregate Root | Execution | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Aggregator | Construction | 14.562 ns | 168 B | 15.235 ns | 168 B | Same allocation; fluent was slightly faster in this microbenchmark. |
| Aggregator | Execution | 188.000 ns | 1,088 B | 200.564 ns | 1,088 B | Same allocation; fluent was faster for order line aggregation. |
| Ambassador | Construction | 55.42 ns | 448 B | 48.03 ns | 360 B | Generated reduced construction time and allocation in this microbenchmark. |
Expand Down Expand Up @@ -222,19 +224,19 @@ The latest measured timings below were captured on Windows 11, Intel Core i9-149

## Coverage Matrix Summary

The coverage matrix currently publishes 102 catalog patterns and 408 pattern route results. Each pattern has four BenchmarkDotNet routes: fluent construction, fluent execution, source-generated construction, and source-generated execution. The reusable hosting integration matrix publishes 9 reusable hosting integration route results for package-level `IServiceCollection` registrations.
The coverage matrix currently publishes 103 catalog patterns and 412 pattern route results. Each pattern has four BenchmarkDotNet routes: fluent construction, fluent execution, source-generated construction, and source-generated execution. The reusable hosting integration matrix publishes 9 reusable hosting integration route results for package-level `IServiceCollection` registrations.

| Category | Patterns | Published route results |
| --- | ---: | ---: |
| Application Architecture | 17 | 68 |
| Application Architecture | 18 | 72 |
| Behavioral | 11 | 44 |
| Cloud Architecture | 17 | 68 |
| Creational | 5 | 20 |
| Enterprise Integration | 41 | 164 |
| Messaging Reliability | 3 | 12 |
| Structural | 7 | 28 |

The generator matrix currently publishes 97 generator source route results.
The generator matrix currently publishes 98 generator source route results.

## Hosting Integration Matrix Results

Expand All @@ -255,6 +257,7 @@ The generator matrix currently publishes 97 generator source route results.
| Category | Pattern | Fluent construction | Fluent execution | Generated construction | Generated execution |
| --- | --- | --- | --- | --- | --- |
| Application Architecture | Activity Tracker | Covered | Covered | Covered | Covered |
| Application Architecture | Aggregate Root | Covered | Covered | Covered | Covered |
| Application Architecture | Anti-Corruption Layer | Covered | Covered | Covered | Covered |
| Application Architecture | Audit Log | Covered | Covered | Covered | Covered |
| Application Architecture | CQRS | Covered | Covered | Covered | Covered |
Expand Down Expand Up @@ -361,6 +364,7 @@ The generator matrix currently publishes 97 generator source route results.
| Generator | Source | Matrix result |
| --- | --- | --- |
| ActivityTrackerGenerator | `src/PatternKit.Generators/ActivityTracking/ActivityTrackerGenerator.cs` | Covered |
| AggregateCommandHandlerGenerator | `src/PatternKit.Generators/Aggregates/AggregateCommandHandlerGenerator.cs` | Covered |
| AdapterGenerator | `src/PatternKit.Generators/Adapter/AdapterGenerator.cs` | Covered |
| AmbassadorGenerator | `src/PatternKit.Generators/Ambassador/AmbassadorGenerator.cs` | Covered |
| AntiCorruptionLayerGenerator | `src/PatternKit.Generators/AntiCorruption/AntiCorruptionLayerGenerator.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 @@ -34,6 +34,8 @@ The following numbers were captured on Windows 11, Intel Core i9-14900K, .NET SD
| Adapter | Execution | 59.084 ns | 416 B | 20.479 ns | 80 B | Generated adapter execution was faster and allocated less for shipment adaptation. |
| Activity Tracker | Construction | 13.09 ns | 152 B | 12.98 ns | 152 B | Same allocation; generated was slightly faster in this microbenchmark. |
| Activity Tracker | Execution | 446.88 ns | 1,656 B | 452.36 ns | 1,656 B | Same allocation; fluent was slightly faster for dashboard loading gates. |
| Aggregate Root | Construction | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Aggregate Root | Execution | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Aggregator | Construction | 14.562 ns | 168 B | 15.235 ns | 168 B | Same allocation; fluent was slightly faster in this microbenchmark. |
| Aggregator | Execution | 188.000 ns | 1,088 B | 200.564 ns | 1,088 B | Same allocation; fluent was faster for order line aggregation. |
| Ambassador | Construction | 55.42 ns | 448 B | 48.03 ns | 360 B | 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 @@ -105,6 +105,7 @@ The source of truth is `PatternKitPatternCatalog` in `src/PatternKit.Examples/Pr
| Cloud Architecture | Leader Election | `LeaderElection<TContext>` | Leader Election generator |
| Cloud Architecture | Scheduler Agent Supervisor | `SchedulerAgentSupervisor<TWork,TResult>` | Scheduler Agent Supervisor generator |
| Application Architecture | CQRS | Mediator/dispatcher command-query split | Dispatcher generator |
| Application Architecture | Aggregate Root | `AggregateRoot<TId,TEvent>` and command handlers | Aggregate Root generator |
| Application Architecture | Specification | `Specification<T>` and named registries | Specification generator |
| Application Architecture | Value Object | `ValueObject<TSelf>` and `ValueObjectFactory<T>` | Value Object generator |
| Application Architecture | Repository | `IRepository<TEntity,TKey>` and `InMemoryRepository<TEntity,TKey>` | Repository generator |
Expand Down
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ if (parser.Execute("123", out var value))

## 📚 Available Patterns

PatternKit covers 102 production-readiness patterns with fluent APIs, source-generated routes where applicable, IoC integration examples, TinyBDD coverage, and BenchmarkDotNet coverage-matrix validation:
PatternKit covers 103 production-readiness patterns with fluent APIs, source-generated routes where applicable, IoC integration examples, TinyBDD coverage, and BenchmarkDotNet coverage-matrix validation:

| Category | Count | Patterns |
| --- | ---: | --- |
| Application Architecture | 17 | 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, Value Object |
| Application Architecture | 18 | Activity Tracker, Aggregate Root, 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, Value Object |
| 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 |
Expand Down
49 changes: 49 additions & 0 deletions docs/patterns/application/aggregate-root.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Aggregate Root

Aggregate Root protects a consistency boundary by deciding which domain events a command may produce and applying those events to the aggregate state.

Use it when updates must enforce invariants across a cluster of entities, such as order placement and payment, account lifecycle changes, or inventory reservations.

## Fluent Path

```csharp
using PatternKit.Application.Aggregates;

var handler = AggregateCommandHandler<OrderAggregate, OrderCommand, IOrderEvent>.Create(
"order-aggregate",
Decide,
(aggregate, domainEvent) => aggregate.Record(domainEvent));

var result = handler.Execute(order, new PayOrder(order.Id));
```

`AggregateRoot<TId,TEvent>` tracks identity, version, and uncommitted events. `AggregateCommandHandler<TAggregate,TCommand,TEvent>` keeps command decision and event application explicit and testable.

## Generated Path

```csharp
using PatternKit.Generators.Aggregates;

[GenerateAggregateCommandHandler(typeof(OrderAggregate), typeof(OrderCommand), typeof(IOrderEvent))]
public static partial class OrderHandlers
{
[AggregateDecision]
private static IEnumerable<IOrderEvent> Decide(OrderAggregate aggregate, OrderCommand command)
=> OrderDecisions.Decide(aggregate, command);

[AggregateEventApplier]
private static void Apply(OrderAggregate aggregate, IOrderEvent domainEvent)
=> aggregate.Record(domainEvent);
}
```

The generator emits a factory returning `AggregateCommandHandler<TAggregate,TCommand,TEvent>` so application services can inject the generated command path.

## IoC Usage

```csharp
services.AddOrderAggregateRootDemo();
services.AddSingleton<OrderWorkflowService>();
```

The example in `docs/examples/order-aggregate-root-pattern.md` shows fluent and generated aggregate command handling through standard `IServiceCollection`.
2 changes: 2 additions & 0 deletions docs/patterns/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,8 @@
href: cloud/scheduler-agent-supervisor.md
- name: Application Architecture
items:
- name: Aggregate Root
href: application/aggregate-root.md
- name: Anti-Corruption Layer
href: application/anti-corruption-layer.md
- name: Activity Tracker
Expand Down
Loading
Loading