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
50 changes: 14 additions & 36 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,25 +54,14 @@ jobs:

- name: Test with coverage
timeout-minutes: 30
shell: bash
run: |
set -euo pipefail
for project in \
test/PatternKit.Tests/PatternKit.Tests.csproj \
test/PatternKit.Generators.Tests/PatternKit.Generators.Tests.csproj \
test/PatternKit.Examples.Tests/PatternKit.Examples.Tests.csproj \
test/PatternKit.Hosting.Extensions.Tests/PatternKit.Hosting.Extensions.Tests.csproj
do
dotnet test "$project" \
--configuration Release \
--no-build \
-p:TestTfmsInParallel=false \
--collect:"XPlat Code Coverage" \
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura \
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Include="[PatternKit*]*" \
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Exclude="[*Tests]*" \
-- RunConfiguration.TestSessionTimeout=1800000
done
dotnet test PatternKit.slnx \
--configuration Release \
--no-build \
-p:TestTfmsInParallel=false \
--collect:"XPlat Code Coverage" \
--results-directory ./TestResults \
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura

- name: Install ReportGenerator
run: dotnet tool update -g dotnet-reportgenerator-globaltool
Expand Down Expand Up @@ -181,25 +170,14 @@ jobs:

- name: Test with coverage (Release)
timeout-minutes: 30
shell: bash
run: |
set -euo pipefail
for project in \
test/PatternKit.Tests/PatternKit.Tests.csproj \
test/PatternKit.Generators.Tests/PatternKit.Generators.Tests.csproj \
test/PatternKit.Examples.Tests/PatternKit.Examples.Tests.csproj \
test/PatternKit.Hosting.Extensions.Tests/PatternKit.Hosting.Extensions.Tests.csproj
do
dotnet test "$project" \
--configuration Release \
--no-build \
-p:TestTfmsInParallel=false \
--collect:"XPlat Code Coverage" \
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura \
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Include="[PatternKit*]*" \
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Exclude="[*Tests]*" \
-- RunConfiguration.TestSessionTimeout=1800000
done
dotnet test PatternKit.slnx \
--configuration Release \
--no-build \
-p:TestTfmsInParallel=false \
--collect:"XPlat Code Coverage" \
--results-directory ./TestResults \
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura


- name: Pack (all packable projects)
Expand Down
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 103 production-readiness patterns. Each catalog pattern is represented in tests, documentation, real-world examples, IoC integration, and the BenchmarkDotNet coverage matrix.
PatternKit currently tracks 104 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 | 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 |
| Application Architecture | 19 | Activity Tracker, Aggregate Root, Anti-Corruption Layer, Audit Log, CQRS, Data Mapper, Domain Event, Domain Service, 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 Expand Up @@ -561,6 +561,8 @@ BenchmarkDotNet guidance is documented in [docs/guides/benchmarks.md](docs/guide
| 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. |
| Domain Event | Execution | 367.2 ns | 1.77 KB | 346.4 ns | 1.55 KB | Generated reduced execution time and allocation for the order-placed dispatch workflow. |
| Domain Service | Construction | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Domain Service | Execution | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Event-Carried State Transfer | Construction | 7.552 ns | 48 B | 6.751 ns | 48 B | Same allocation; generated was slightly faster in this microbenchmark. |
| Event-Carried State Transfer | Execution | 58.508 ns | 448 B | 59.071 ns | 448 B | Effectively equivalent for the inventory projection workflow. |
| Event Notification | Construction | 30.920 ns | 232 B | 31.926 ns | 232 B | Effectively equivalent for this microbenchmark. |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using BenchmarkDotNet.Attributes;
using PatternKit.Application.DomainServices;
using PatternKit.Examples.DomainServiceDemo;

namespace PatternKit.Benchmarks.Application;

[BenchmarkCategory("ApplicationArchitecture", "DomainService")]
public class DomainServiceBenchmarks
{
private static readonly ShippingDomainServiceDemo.ShippingRequest Request =
ShippingDomainServiceDemo.CreateHighValueRequest();

private readonly DomainServiceRegistry<ShippingDomainServiceDemo.ShippingRequest, ShippingDomainServiceDemo.ShippingDecision> _fluent =
ShippingDomainServiceDemo.CreateFluentRegistry();

private readonly DomainServiceRegistry<ShippingDomainServiceDemo.ShippingRequest, ShippingDomainServiceDemo.ShippingDecision> _generated =
ShippingDomainServiceDemo.CreateGeneratedRegistry();

[Benchmark(Baseline = true, Description = "Fluent: create domain service registry")]
[BenchmarkCategory("Fluent", "Construction")]
public DomainServiceRegistry<ShippingDomainServiceDemo.ShippingRequest, ShippingDomainServiceDemo.ShippingDecision> Fluent_CreateRegistry()
=> ShippingDomainServiceDemo.CreateFluentRegistry();

[Benchmark(Description = "Generated: create domain service registry")]
[BenchmarkCategory("Generated", "Construction")]
public DomainServiceRegistry<ShippingDomainServiceDemo.ShippingRequest, ShippingDomainServiceDemo.ShippingDecision> Generated_CreateRegistry()
=> ShippingDomainServiceDemo.CreateGeneratedRegistry();

[Benchmark(Description = "Fluent: select shipping decision")]
[BenchmarkCategory("Fluent", "Execution")]
public ShippingDomainServiceDemo.ShippingDecision Fluent_SelectBest()
=> ShippingDomainServiceDemo.SelectBest(Request, _fluent);

[Benchmark(Description = "Generated: select shipping decision")]
[BenchmarkCategory("Generated", "Execution")]
public ShippingDomainServiceDemo.ShippingDecision Generated_SelectBest()
=> ShippingDomainServiceDemo.SelectBest(Request, _generated);
}
30 changes: 30 additions & 0 deletions docs/examples/shipping-domain-service-pattern.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Shipping Domain Service Pattern

This example demonstrates a production-style Domain Service for shipping decisions. It includes a fluent operation registry, a source-generated operation registry, TinyBDD tests, and an `IServiceCollection` extension.

## Import

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

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

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

## Use

```csharp
var decision = service.SelectBest(ShippingDomainServiceDemo.CreateHighValueRequest());
```

The service uses a generated `DomainServiceRegistry<ShippingRequest, ShippingDecision>`. The fluent and generated routes share the same carrier and insurance rules so teams can compare runtime and generated composition without changing domain behavior.

## Production Notes

- Keep domain services stateless.
- Keep operation names stable because callers and tests use them as domain vocabulary.
- Register the example with `AddShippingDomainServiceDemo()` or import all examples with `AddPatternKitExamples()`.
3 changes: 3 additions & 0 deletions docs/examples/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@
- name: Order Aggregate Root Pattern
href: order-aggregate-root-pattern.md

- name: Shipping Domain Service Pattern
href: shipping-domain-service-pattern.md

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

Expand Down
43 changes: 43 additions & 0 deletions docs/generators/domain-service.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Domain Service Generator

The Domain Service generator turns annotated static methods into a named `DomainServiceRegistry<TRequest,TResponse>` factory.

## Usage

```csharp
using PatternKit.Generators.DomainServices;

[GenerateDomainServiceRegistry(typeof(ShippingRequest), typeof(ShippingDecision), FactoryMethodName = "Build")]
public static partial class ShippingServices
{
[DomainServiceOperation("insured-air")]
private static ShippingDecision InsuredAir(ShippingRequest request)
=> new(request.OrderId, "air", request.Weight * 3m);
}
```

Generated output:

```csharp
var registry = ShippingServices.Build();
var decision = registry.Execute("insured-air", request);
```

## Operation Shape

Domain service operations must be static methods with this shape:

```csharp
static TResponse Operation(TRequest request)
```

Operation names must be unique within the generated registry.

## Diagnostics

| ID | Meaning |
|---|---|
| `PKDOM001` | Host type must be `partial`. |
| `PKDOM002` | Host type has no `[DomainServiceOperation]` methods. |
| `PKDOM003` | Operation method signature is invalid. |
| `PKDOM004` | Operation name is duplicated. |
3 changes: 3 additions & 0 deletions docs/generators/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@
- name: Domain Event
href: domain-event.md

- name: Domain Service
href: domain-service.md

- name: Event Sourcing
href: event-sourcing.md

Expand Down
18 changes: 11 additions & 7 deletions docs/guides/benchmark-results.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ The latest measured timings below were captured on Windows 11, Intel Core i9-149
| 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. |
| Domain Event | Execution | 367.2 ns | 1.77 KB | 346.4 ns | 1.55 KB | Generated reduced execution time and allocation for the order-placed dispatch workflow. |
| Domain Service | Construction | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Domain Service | Execution | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Event-Carried State Transfer | Construction | 7.552 ns | 48 B | 6.751 ns | 48 B | Same allocation; generated was slightly faster in this microbenchmark. |
| Event-Carried State Transfer | Execution | 58.508 ns | 448 B | 59.071 ns | 448 B | Effectively equivalent for the inventory projection workflow. |
| Event Notification | Construction | 30.920 ns | 232 B | 31.926 ns | 232 B | Effectively equivalent for this microbenchmark. |
Expand Down Expand Up @@ -224,19 +226,19 @@ The latest measured timings below were captured on Windows 11, Intel Core i9-149

## Coverage Matrix Summary

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.
The coverage matrix currently publishes 104 catalog patterns and 416 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 | 18 | 72 |
| Application Architecture | 19 | 76 |
| 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 98 generator source route results.
The generator matrix currently publishes 99 generator source route results.

## Hosting Integration Matrix Results

Expand All @@ -262,8 +264,9 @@ The generator matrix currently publishes 98 generator source route results.
| Application Architecture | Audit Log | Covered | Covered | Covered | Covered |
| Application Architecture | CQRS | Covered | Covered | Covered | Covered |
| Application Architecture | Data Mapper | Covered | Covered | Covered | Covered |
| Application Architecture | Domain Event | Covered | Covered | Covered | Covered |
| Application Architecture | Event Sourcing | Covered | Covered | Covered | Covered |
| Application Architecture | Domain Event | Covered | Covered | Covered | Covered |
| Application Architecture | Domain Service | Covered | Covered | Covered | Covered |
| Application Architecture | Event Sourcing | Covered | Covered | Covered | Covered |
| Application Architecture | Feature Toggle | Covered | Covered | Covered | Covered |
| Application Architecture | Identity Map | Covered | Covered | Covered | Covered |
| Application Architecture | Materialized View | Covered | Covered | Covered | Covered |
Expand Down Expand Up @@ -383,8 +386,9 @@ The generator matrix currently publishes 98 generator source route results.
| CompositeGenerator | `src/PatternKit.Generators/Composite/CompositeGenerator.cs` | Covered |
| DataMapperGenerator | `src/PatternKit.Generators/DataMapping/DataMapperGenerator.cs` | Covered |
| DecoratorGenerator | `src/PatternKit.Generators/DecoratorGenerator.cs` | Covered |
| DomainEventDispatcherGenerator | `src/PatternKit.Generators/DomainEvents/DomainEventDispatcherGenerator.cs` | Covered |
| EventCarriedStateTransferGenerator | `src/PatternKit.Generators/EventCarriedStateTransfer/EventCarriedStateTransferGenerator.cs` | Covered |
| DomainEventDispatcherGenerator | `src/PatternKit.Generators/DomainEvents/DomainEventDispatcherGenerator.cs` | Covered |
| DomainServiceRegistryGenerator | `src/PatternKit.Generators/DomainServices/DomainServiceRegistryGenerator.cs` | Covered |
| EventCarriedStateTransferGenerator | `src/PatternKit.Generators/EventCarriedStateTransfer/EventCarriedStateTransferGenerator.cs` | Covered |
| EventNotificationGenerator | `src/PatternKit.Generators/EventNotification/EventNotificationGenerator.cs` | Covered |
| EventStoreGenerator | `src/PatternKit.Generators/EventSourcing/EventStoreGenerator.cs` | Covered |
| FacadeGenerator | `src/PatternKit.Generators/FacadeGenerator.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 @@ -96,6 +96,8 @@ The following numbers were captured on Windows 11, Intel Core i9-14900K, .NET SD
| 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. |
| Domain Event | Execution | 367.2 ns | 1.77 KB | 346.4 ns | 1.55 KB | Generated reduced execution time and allocation for the order-placed dispatch workflow. |
| Domain Service | Construction | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Domain Service | Execution | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Event-Carried State Transfer | Construction | 7.552 ns | 48 B | 6.751 ns | 48 B | Same allocation; generated was slightly faster in this microbenchmark. |
| Event-Carried State Transfer | Execution | 58.508 ns | 448 B | 59.071 ns | 448 B | Effectively equivalent for the inventory projection workflow. |
| Event Notification | Construction | 30.920 ns | 232 B | 31.926 ns | 232 B | Effectively equivalent for 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 @@ -115,6 +115,7 @@ The source of truth is `PatternKitPatternCatalog` in `src/PatternKit.Examples/Pr
| Application Architecture | Transaction Script | `TransactionScript<TRequest,TResponse>` | Transaction Script generator |
| Application Architecture | Service Layer | `IServiceOperation<TRequest,TResponse>` and `ServiceLayerOperation<TRequest,TResponse>` | Service Layer generator |
| Application Architecture | Domain Event | `IDomainEvent` and `DomainEventDispatcher<TEventBase>` | Domain Event generator |
| Application Architecture | Domain Service | `DomainServiceOperation<TRequest,TResponse>` and named registries | Domain Service generator |
| Application Architecture | Table Data Gateway | `ITableDataGateway<TRow,TKey>` and `InMemoryTableDataGateway<TRow,TKey>` | Table Data Gateway generator |
| Application Architecture | Event Sourcing | `IEventStore<TEvent,TStreamId>` and `InMemoryEventStore<TEvent,TStreamId>` | Event Sourcing generator |
| Application Architecture | Feature Toggle | `IFeatureToggleSet<TContext>` and `FeatureToggleSet<TContext>` | Feature Toggle generator |
Expand Down
Loading
Loading