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

## Patterns Table
PatternKit currently tracks 114 production-readiness patterns. Each catalog pattern is represented in tests, documentation, real-world examples, IoC integration, and the BenchmarkDotNet coverage matrix.
PatternKit currently tracks 115 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 | 26 | Activity Tracker, Aggregate Root, Anti-Corruption Layer, Audit Log, Bounded Context, Context Map, CQRS, Data Mapper, Domain Event, Domain Service, Event Sourcing, Eventual Consistency Monitor, Feature Toggle, Identity Map, Manual Task Gate, Materialized View, Repository, Service Layer, Snapshot / Checkpoint Management, Specification, Table Data Gateway, Timeout Manager, Transaction Script, Unit of Work, Value Object, Workflow Orchestration |
| Behavioral | 11 | Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor |
| Cloud Architecture | 20 | Ambassador, Backends for Frontends, Bulkhead, Cache-Aside, Cache Stampede Protection, Circuit Breaker, External Configuration Store, Gateway Aggregation, Gateway Routing, Health Endpoint Monitoring, Leader Election, Priority Queue, Queue-Based Load Leveling, Rate Limiting, Read-Through Cache, Retry, Scheduler Agent Supervisor, Sidecar, Strangler Fig, Write-Through Cache |
| Creational | 5 | Abstract Factory, Builder, Factory Method, Prototype, Singleton |
| Creational | 6 | Abstract Factory, Builder, Factory Method, Object Pool, Prototype, Singleton |
| Enterprise Integration | 41 | Aggregator, Canonical Data Model, Channel Adapter, Channel Purger, Claim Check, Competing Consumers, Content Enricher, Content-Based Router, Control Bus, Correlation Identifier, Dead Letter Channel, Durable Subscriber, Dynamic Router, Event Notification, Event-Carried State Transfer, Event-Driven Consumer, Guaranteed Delivery, Invalid Message Channel, Mailbox, Message Bus, Message Channel, Message Envelope, Message Expiration, Message Filter, Message History, 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 @@ -521,6 +521,8 @@ BenchmarkDotNet guidance is documented in [docs/guides/benchmarks.md](docs/guide
| Backends For Frontends | Execution | 25.40 ns | 216 B | 29.77 ns | 216 B | Same allocation; fluent was faster for the web summary shaping workflow. |
| Builder | Construction | 48.24 ns | 320 B | 160.455 us | 129,236 B | Fluent builder construction is much lighter; generated measures full HostApplicationBuilder composition. |
| Builder | Execution | 65.14 ns | 352 B | 500.676 us | 279,923 B | Fluent option building is much lighter; generated exercises the full host-backed application build. |
| Object Pool | Construction | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Object Pool | Execution | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Bridge | Construction | 53.115 ns | 504 B | 3.821 ns | 24 B | Generated bridge construction was materially faster and allocated less. |
| Bridge | Execution | 91.848 ns | 664 B | 30.004 ns | 160 B | Generated bridge forwarding was faster and allocated less for notice rendering. |
| Bulkhead | Construction | 20.56 ns | 216 B | 20.48 ns | 216 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.Creational.ObjectPool;
using PatternKit.Examples.ObjectPoolDemo;

namespace PatternKit.Benchmarks.Creational;

[BenchmarkCategory("Creational", "ObjectPool")]
public class ObjectPoolBenchmarks
{
private static readonly FormulaEvaluationRequest Request = new(
"D4",
"subtotal + tax + 5",
new Dictionary<string, decimal>
{
["subtotal"] = 100m,
["tax"] = 8.25m
});

[Benchmark(Baseline = true, Description = "Fluent: create object pool")]
[BenchmarkCategory("Fluent", "Construction")]
public ObjectPool<FormulaEvaluationBuffer> Fluent_CreateObjectPool()
=> SpreadsheetFormulaBufferPools.CreateFluent();

[Benchmark(Description = "Generated: create object pool")]
[BenchmarkCategory("Generated", "Construction")]
public ObjectPool<FormulaEvaluationBuffer> Generated_CreateObjectPool()
=> SpreadsheetFormulaBufferPools.CreateGenerated();

[Benchmark(Description = "Fluent: evaluate spreadsheet formula")]
[BenchmarkCategory("Fluent", "Execution")]
public FormulaEvaluationResult Fluent_EvaluateSpreadsheetFormula()
=> SpreadsheetFormulaObjectPoolDemoRunner.RunFluent(Request);

[Benchmark(Description = "Generated: evaluate spreadsheet formula")]
[BenchmarkCategory("Generated", "Execution")]
public FormulaEvaluationResult Generated_EvaluateSpreadsheetFormula()
=> SpreadsheetFormulaObjectPoolDemoRunner.RunGenerated(Request);
}
19 changes: 19 additions & 0 deletions docs/examples/spreadsheet-formula-object-pool.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Spreadsheet Formula Object Pool

This example pools resettable formula evaluation buffers for a spreadsheet service. It demonstrates both the fluent and source-generated Object Pool routes, then registers the generated pool through `IServiceCollection`.

```csharp
services.AddSpreadsheetFormulaObjectPoolDemo();

var runner = provider.GetRequiredService<SpreadsheetFormulaObjectPoolDemoRunner>();
var result = runner.Run(new FormulaEvaluationRequest(
"D4",
"subtotal + tax + 5",
new Dictionary<string, decimal>
{
["subtotal"] = 100m,
["tax"] = 8.25m
}));
```

The pool is singleton-scoped, while every formula evaluation uses a short-lived lease. Returning the lease resets the buffer and keeps the retained pool bounded.
3 changes: 3 additions & 0 deletions docs/examples/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
- name: Order Projection Eventual Consistency Monitor
href: order-projection-eventual-consistency-monitor.md

- name: Spreadsheet Formula Object Pool
href: spreadsheet-formula-object-pool.md

- name: Auth & Logging with `ActionChain<HttpRequest>`
href: auth-logging-chain.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 @@ -30,6 +30,7 @@ PatternKit includes a Roslyn incremental generator package (`PatternKit.Generato
| [**Builder**](builder.md) | GoF-aligned builders with mutable or state-projection models, sync/async pipelines | `[GenerateBuilder]` |
| [**Factory Method**](factory-method.md) | Keyed dispatcher from a static partial class | `[FactoryMethod]` |
| [**Factory Class**](factory-class.md) | GoF-style factory mapping keys to products | `[FactoryClass]` |
| [**Object Pool**](object-pool.md) | Bounded pooled-resource factories with optional reset hooks | `[GenerateObjectPool]` |
| [**Prototype**](prototype.md) | Clone/deep-copy generation for types | `[Prototype]` |
| [**Singleton**](singleton.md) | Thread-safe singleton accessors with optional factory hooks | `[Singleton]` |

Expand Down
23 changes: 23 additions & 0 deletions docs/generators/object-pool.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Object Pool Generator

`GenerateObjectPoolAttribute` creates a static factory for `PatternKit.Creational.ObjectPool.ObjectPool<T>`.

| Setting | Type | Default | Description |
| --- | --- | --- | --- |
| `itemType` | `Type` | Required | Pooled item type. It must be a value type or expose a public parameterless constructor. |
| `FactoryMethodName` | `string` | `Create` | Generated static factory method name. |
| `MaxRetained` | `int` | `-1` | When set to `0` or greater, emits `WithMaxRetained(value)`. |
| `ResetMethodName` | `string?` | `null` | Optional instance method invoked from `OnReturn`. |

```csharp
[GenerateObjectPool(typeof(FormulaEvaluationBuffer), FactoryMethodName = "CreateGenerated", MaxRetained = 16, ResetMethodName = nameof(FormulaEvaluationBuffer.Reset))]
public static partial class SpreadsheetFormulaBufferPools;
```

Diagnostics:

| ID | Severity | Description |
| --- | --- | --- |
| `PKOP001` | Error | The host type is not partial. |
| `PKOP002` | Error | The factory method name is blank or `MaxRetained` is below `-1`. |
| `PKOP003` | Error | The pooled item type cannot be created with `new T()`. |
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: Manual Task Gate
href: manual-task-gate.md

- name: Object Pool
href: object-pool.md

- name: Workflow Orchestration
href: workflow-orchestration.md

Expand Down
20 changes: 12 additions & 8 deletions docs/guides/benchmark-results.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ The latest measured timings below were captured on Windows 11, Intel Core i9-149
| Backends For Frontends | Execution | 25.40 ns | 216 B | 29.77 ns | 216 B | Same allocation; fluent was faster for the web summary shaping workflow. |
| Builder | Construction | 48.24 ns | 320 B | 160.455 us | 129,236 B | Fluent builder construction is much lighter; generated measures full HostApplicationBuilder composition. |
| Builder | Execution | 65.14 ns | 352 B | 500.676 us | 279,923 B | Fluent option building is much lighter; generated exercises the full host-backed application build. |
| Object Pool | Construction | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Object Pool | Execution | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Bridge | Construction | 53.115 ns | 504 B | 3.821 ns | 24 B | Generated bridge construction was materially faster and allocated less. |
| Bridge | Execution | 91.848 ns | 664 B | 30.004 ns | 160 B | Generated bridge forwarding was faster and allocated less for notice rendering. |
| Bulkhead | Construction | 20.56 ns | 216 B | 20.48 ns | 216 B | Effectively equivalent for this microbenchmark. |
Expand Down Expand Up @@ -248,19 +250,19 @@ The latest measured timings below were captured on Windows 11, Intel Core i9-149

## Coverage Matrix Summary

The coverage matrix currently publishes 114 catalog patterns and 456 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 115 catalog patterns and 460 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 | 26 | 104 |
| Behavioral | 11 | 44 |
| Cloud Architecture | 20 | 80 |
| Creational | 5 | 20 |
| Creational | 6 | 24 |
| Enterprise Integration | 41 | 164 |
| Messaging Reliability | 3 | 12 |
| Structural | 7 | 28 |

The generator matrix currently publishes 108 generator source route results.
The generator matrix currently publishes 109 generator source route results.

## Hosting Integration Matrix Results

Expand Down Expand Up @@ -338,9 +340,10 @@ The generator matrix currently publishes 108 generator source route results.
| Cloud Architecture | Strangler Fig | Covered | Covered | Covered | Covered |
| Cloud Architecture | Write-Through Cache | Covered | Covered | Covered | Covered |
| Creational | Abstract Factory | Covered | Covered | Covered | Covered |
| Creational | Builder | Covered | Covered | Covered | Covered |
| Creational | Factory Method | Covered | Covered | Covered | Covered |
| Creational | Prototype | Covered | Covered | Covered | Covered |
| Creational | Builder | Covered | Covered | Covered | Covered |
| Creational | Factory Method | Covered | Covered | Covered | Covered |
| Creational | Object Pool | Covered | Covered | Covered | Covered |
| Creational | Prototype | Covered | Covered | Covered | Covered |
| Creational | Singleton | Covered | Covered | Covered | Covered |
| Enterprise Integration | Aggregator | Covered | Covered | Covered | Covered |
| Enterprise Integration | Canonical Data Model | Covered | Covered | Covered | Covered |
Expand Down Expand Up @@ -484,8 +487,9 @@ The generator matrix currently publishes 108 generator source route results.
| ServiceActivatorGenerator | `src/PatternKit.Generators/Messaging/ServiceActivatorGenerator.cs` | Covered |
| SplitterAggregatorGenerator | `src/PatternKit.Generators/Messaging/SplitterAggregatorGenerator.cs` | Covered |
| WireTapGenerator | `src/PatternKit.Generators/Messaging/WireTapGenerator.cs` | Covered |
| ObserverGenerator | `src/PatternKit.Generators/Observer/ObserverGenerator.cs` | Covered |
| PriorityQueueGenerator | `src/PatternKit.Generators/PriorityQueue/PriorityQueueGenerator.cs` | Covered |
| ObserverGenerator | `src/PatternKit.Generators/Observer/ObserverGenerator.cs` | Covered |
| ObjectPoolGenerator | `src/PatternKit.Generators/ObjectPool/ObjectPoolGenerator.cs` | Covered |
| PriorityQueueGenerator | `src/PatternKit.Generators/PriorityQueue/PriorityQueueGenerator.cs` | Covered |
| PrototypeGenerator | `src/PatternKit.Generators/PrototypeGenerator.cs` | Covered |
| ProxyGenerator | `src/PatternKit.Generators/ProxyGenerator.cs` | Covered |
| QueueLoadLevelingPolicyGenerator | `src/PatternKit.Generators/QueueLoadLeveling/QueueLoadLevelingPolicyGenerator.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 @@ -58,6 +58,8 @@ The following numbers were captured on Windows 11, Intel Core i9-14900K, .NET SD
| Backends For Frontends | Execution | 25.40 ns | 216 B | 29.77 ns | 216 B | Same allocation; fluent was faster for the web summary shaping workflow. |
| Builder | Construction | 48.24 ns | 320 B | 160.455 us | 129,236 B | Fluent builder construction is much lighter; generated measures full HostApplicationBuilder composition. |
| Builder | Execution | 65.14 ns | 352 B | 500.676 us | 279,923 B | Fluent option building is much lighter; generated exercises the full host-backed application build. |
| Object Pool | Construction | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Object Pool | Execution | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
| Bridge | Construction | 53.115 ns | 504 B | 3.821 ns | 24 B | Generated bridge construction was materially faster and allocated less. |
| Bridge | Execution | 91.848 ns | 664 B | 30.004 ns | 160 B | Generated bridge forwarding was faster and allocated less for notice rendering. |
| Bulkhead | Construction | 20.56 ns | 216 B | 20.48 ns | 216 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 @@ -44,6 +44,7 @@ The source of truth is `PatternKitPatternCatalog` in `src/PatternKit.Examples/Pr

| Family | Pattern | Fluent/runtime path | Source-generated path |
| --- | --- | --- | --- |
| Creational | Object Pool | `ObjectPool<T>` | Object Pool generator |
| Enterprise Integration | Message Channel | `MessageChannel<TPayload>` | Message Channel generator |
| Enterprise Integration | Channel Purger | `ChannelPurger<TPayload>` | Channel Purger generator |
| Enterprise Integration | Invalid Message Channel | `InvalidMessageChannel<TPayload>` | Invalid Message Channel generator |
Expand Down
42 changes: 42 additions & 0 deletions docs/patterns/creational/object-pool/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Object Pool

Object Pool keeps reusable, expensive objects behind explicit leases. PatternKit's runtime API is thread-safe, bounded, and designed for services that need temporary buffers, protocol clients, encoders, or other resettable resources without allocating on every operation.

```csharp
var pool = ObjectPool<FormulaEvaluationBuffer>
.Create()
.WithFactory(static () => new FormulaEvaluationBuffer())
.OnReturn(static buffer => buffer.Reset())
.WithMaxRetained(16)
.Build();

using var lease = pool.Rent();
lease.Value.Load(variables);
var total = lease.Value.Evaluate("subtotal + tax");
```

Use `OnReturn` to reset state, `RetainWhen` to discard unhealthy instances, and `WithMaxRetained` to prevent unbounded memory growth. Disposing the lease returns the object exactly once. Disposing the pool releases retained `IDisposable` instances.

## Source Generation

```csharp
[GenerateObjectPool(
typeof(FormulaEvaluationBuffer),
FactoryMethodName = "CreateGenerated",
MaxRetained = 16,
ResetMethodName = nameof(FormulaEvaluationBuffer.Reset))]
public static partial class SpreadsheetFormulaBufferPools;
```

The generated factory emits the same fluent API calls as handwritten code. It validates that the pool host is partial and that the pooled type can be constructed with `new T()`.

## Dependency Injection

Register the generated pool as a singleton and inject it into services that rent buffers per operation:

```csharp
services.AddSingleton(static _ => SpreadsheetFormulaBufferPools.CreateGenerated());
services.AddSingleton<SpreadsheetFormulaService>();
```

The spreadsheet formula demo in `src/PatternKit.Examples/ObjectPoolDemo` shows the production shape with `IServiceCollection`, a generated route, and TinyBDD coverage.
3 changes: 3 additions & 0 deletions docs/patterns/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ If you’re looking for end-to-end, production-shaped demos, check the **Example
- **[Creational.Factory](creational/factory/factory.md)**
Key → creator mapping with optional default; immutable and allocation-light.

- **[Creational.ObjectPool](creational/object-pool/index.md)**
Bounded, lease-based reuse for expensive resettable resources.

- **[Creational.Prototype](creational/prototype/prototype.md)**
Clone-and-tweak via fluent builders and keyed registries.

Expand Down
2 changes: 2 additions & 0 deletions docs/patterns/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@
href: creational/factory/real-world-examples.md
- name: Factory
href: creational/factory/factory.md
- name: Object Pool
href: creational/object-pool/index.md
- name: Prototype
href: creational/prototype/index.md
items:
Expand Down
Loading
Loading