-
Notifications
You must be signed in to change notification settings - Fork 0
Add source-generated Mediator pattern with zero runtime dependency + comprehensive production demo #81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add source-generated Mediator pattern with zero runtime dependency + comprehensive production demo #81
Conversation
Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
|
@copilot ensure we've completed the implementation to the feature request spec. We need to ensure the feature is accurately labeled and documented as an implementation of the mediator pattern. |
…ation Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
Updated documentation and code to properly identify this as a Mediator pattern implementation (source-generated variant). Changes in commit 410d321:
All tests passing (9/9 dispatcher tests, 1,729/1,729 total). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements a source-generated Mediator pattern that produces standalone message dispatcher code with zero PatternKit runtime dependencies. The implementation generates compile-time code for handling commands (request/response), notifications (fan-out), and streams (async enumerable), with support for pre/post execution pipelines.
Changes:
- Source generator infrastructure for Mediator pattern with
DispatcherGeneratorandGenerateDispatcherAttribute - Fluent builder API for registering command, notification, and stream handlers
- Generated contracts including handler interfaces and pipeline delegates
- Comprehensive test coverage with 9 tests validating core functionality
- Documentation explaining the Mediator pattern and when to use source-generated vs runtime variants
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/PatternKit.Generators/Messaging/DispatcherGenerator.cs | Core generator implementing IIncrementalGenerator, produces three files per mediator |
| src/PatternKit.Generators.Abstractions/Messaging/GenerateDispatcherAttribute.cs | Assembly-level attribute for marking assemblies for generation |
| test/PatternKit.Generators.Tests/DispatcherGeneratorTests.cs | Test suite with 9 tests covering commands, notifications, streams, pipelines, and error handling |
| src/PatternKit.Examples/Messaging/DispatcherExample.cs | Working examples demonstrating commands, notifications, streams, and pipelines |
| docs/patterns/messaging/dispatcher.md | Comprehensive documentation including API reference, best practices, and pattern explanation |
| test/PatternKit.*.Tests/packages.lock.json | Updated package lock files with new dependencies for all three test projects |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@copilot apply changes based on the comments in this thread |
Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
Applied the code review suggestions in commit cf243c0:
All tests passing (9/9). |
|
@copilot Ensure our code is SOLID, DRY, fluent, declarative, functional, and as flat as possible. We want to limit as much nesting as possible. |
Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
Applied SOLID, DRY, and functional programming principles in commit 51d5c59: SOLID:
DRY:
Functional & Declarative:
Flat:
All 9 tests passing. |
|
@copilot ensure the user docs contain adequate documentation on our sample. |
Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
Added comprehensive documentation in commit 954c77e: Documentation Added:
All documentation is cross-linked and includes runnable code examples from the 780+ line comprehensive demo. |
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
Test Results110 tests 110 ✅ 20s ⏱️ Results for commit f98c724. ♻️ This comment has been updated with latest results. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private static int _nextId = 1; | ||
| private readonly ILogger _logger; | ||
|
|
||
| public CreateCustomerHandler(ILogger logger) | ||
| { | ||
| _logger = logger; | ||
| } | ||
|
|
||
| public ValueTask<Customer> Handle(CreateCustomerCommand request, CancellationToken ct) | ||
| { | ||
| _logger.Log($"Creating customer: {request.Name}"); | ||
| var customer = new Customer(_nextId++, request.Name, request.Email, request.CreditLimit); | ||
| return new ValueTask<Customer>(customer); | ||
| } | ||
| } | ||
|
|
||
| public class PlaceOrderHandler : global::PatternKit.Examples.Messaging.SourceGenerated.ICommandHandler<PlaceOrderCommand, Order> | ||
| { | ||
| private static int _nextOrderId = 1000; | ||
| private readonly ILogger _logger; | ||
|
|
||
| public PlaceOrderHandler(ILogger logger) | ||
| { | ||
| _logger = logger; | ||
| } | ||
|
|
||
| public ValueTask<Order> Handle(PlaceOrderCommand request, CancellationToken ct) | ||
| { | ||
| _logger.Log($"Placing order for customer {request.CustomerId}"); | ||
| var total = request.Items.Sum(i => i.Price * i.Quantity); | ||
| var order = new Order(_nextOrderId++, request.CustomerId, request.Items, total, OrderStatus.Pending); |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Write to static field from instance method, property, or constructor.
| private static int _nextId = 1; | |
| private readonly ILogger _logger; | |
| public CreateCustomerHandler(ILogger logger) | |
| { | |
| _logger = logger; | |
| } | |
| public ValueTask<Customer> Handle(CreateCustomerCommand request, CancellationToken ct) | |
| { | |
| _logger.Log($"Creating customer: {request.Name}"); | |
| var customer = new Customer(_nextId++, request.Name, request.Email, request.CreditLimit); | |
| return new ValueTask<Customer>(customer); | |
| } | |
| } | |
| public class PlaceOrderHandler : global::PatternKit.Examples.Messaging.SourceGenerated.ICommandHandler<PlaceOrderCommand, Order> | |
| { | |
| private static int _nextOrderId = 1000; | |
| private readonly ILogger _logger; | |
| public PlaceOrderHandler(ILogger logger) | |
| { | |
| _logger = logger; | |
| } | |
| public ValueTask<Order> Handle(PlaceOrderCommand request, CancellationToken ct) | |
| { | |
| _logger.Log($"Placing order for customer {request.CustomerId}"); | |
| var total = request.Items.Sum(i => i.Price * i.Quantity); | |
| var order = new Order(_nextOrderId++, request.CustomerId, request.Items, total, OrderStatus.Pending); | |
| private static int _nextId = 0; | |
| private readonly ILogger _logger; | |
| public CreateCustomerHandler(ILogger logger) | |
| { | |
| _logger = logger; | |
| } | |
| private static int GetNextId() | |
| { | |
| return System.Threading.Interlocked.Increment(ref _nextId); | |
| } | |
| public ValueTask<Customer> Handle(CreateCustomerCommand request, CancellationToken ct) | |
| { | |
| _logger.Log($"Creating customer: {request.Name}"); | |
| var customer = new Customer(GetNextId(), request.Name, request.Email, request.CreditLimit); | |
| return new ValueTask<Customer>(customer); | |
| } | |
| } | |
| public class PlaceOrderHandler : global::PatternKit.Examples.Messaging.SourceGenerated.ICommandHandler<PlaceOrderCommand, Order> | |
| { | |
| private static int _nextOrderId = 999; | |
| private readonly ILogger _logger; | |
| public PlaceOrderHandler(ILogger logger) | |
| { | |
| _logger = logger; | |
| } | |
| private static int GetNextOrderId() | |
| { | |
| return System.Threading.Interlocked.Increment(ref _nextOrderId); | |
| } | |
| public ValueTask<Order> Handle(PlaceOrderCommand request, CancellationToken ct) | |
| { | |
| _logger.Log($"Placing order for customer {request.CustomerId}"); | |
| var total = request.Items.Sum(i => i.Price * i.Quantity); | |
| var order = new Order(GetNextOrderId(), request.CustomerId, request.Items, total, OrderStatus.Pending); |
| { | ||
| _logger.Log($"Placing order for customer {request.CustomerId}"); | ||
| var total = request.Items.Sum(i => i.Price * i.Quantity); | ||
| var order = new Order(_nextOrderId++, request.CustomerId, request.Items, total, OrderStatus.Pending); |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Write to static field from instance method, property, or constructor.
🔍 PR Validation ResultsVersion: ✅ Validation Steps
📊 ArtifactsDry-run artifacts have been uploaded and will be available for 7 days. This comment was automatically generated by the PR validation workflow. |
|
@copilot apply changes based on the comments in this thread |
…D generation Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
Applied all code review suggestions in commit f98c724:
All 9 dispatcher tests passing across all target frameworks (net8.0, net9.0, net10.0). |
Code Coverage |
Message Dispatcher Source Generator - Implementation Complete ✅
Successfully implemented a source-generated Mediator pattern with zero PatternKit runtime dependency as specified in the requirements.
✅ Pattern Identification
This feature implements the Mediator behavioral design pattern:
Documentation Updates
Code Quality Improvements
.Where()instead of implicit filtering in foreach.SelectMany().Select()for more functional styleComprehensive Production Demo ✅
MediatorComprehensiveDemo/ComprehensiveDemo.cs)IServiceCollectionextensions (AddSourceGeneratedMediator(),AddHandlersFromAssembly(),AddBehavior<>())Interlocked.Incrementfor ID generation✅ All Acceptance Criteria Met
Runtime Independence ✅
using PatternKit.*statementsAPI Completeness ✅
Send<TRequest, TResponse>Publish<TNotification>(0..N handlers, sequential)Stream<TRequest, TItem>usingIAsyncEnumerable<T>Performance & Determinism ✅
Test Coverage ✅
Production-Ready Examples & Documentation ✅
AddMediator(),AddHandlersFromAssembly(),AddBehavior<>()docs/patterns/messaging/dispatcher.mdwith production examplessrc/PatternKit.Examples/MediatorComprehensiveDemo/README.md)📦 What Was Delivered
Core Generator Files
PatternKit.Generators.Abstractions/Messaging/GenerateDispatcherAttribute.csPatternKit.Generators/Messaging/DispatcherGenerator.cs{Name}.g.cs- Main mediator class{Name}.Builder.g.cs- Fluent builder{Name}.Contracts.g.cs- Handler interfacesDocumentation & Examples
docs/patterns/messaging/dispatcher.md- Full Mediator pattern documentation with production examplesdocs/patterns/messaging/README.md- Quick start with link to comprehensive demosrc/PatternKit.Examples/Messaging/DispatcherExample.cs- Simple usage examplessrc/PatternKit.Examples/MediatorComprehensiveDemo/ComprehensiveDemo.cs- Comprehensive production demo (783 lines)Interlocked.Incrementsrc/PatternKit.Examples/MediatorComprehensiveDemo/README.md- Dedicated comprehensive demo documentation (370+ lines)✅ Quality Metrics
Original prompt
This section details on the original issue you should resolve
<issue_title>[Feature] Source-generated message dispatcher (commands, notifications, streams) with zero PatternKit runtime dependency + dual-mode API (class-based + fluent)</issue_title>
<issue_description>## Summary
We want a source generator that produces a standalone message dispatcher for an application:
Critical requirement
The generated output must be 100% independent of PatternKit at runtime:
.g.csfiles may notThe goal is to let teams write “transpilable” / standardized message flows with minimal manual wiring, while keeping the runtime surface small, fast, and distinct.
Goals
G1 — Generated runtime has no dependency on PatternKit
using PatternKit.*in emitted codeG2 — First-class async and streaming
ValueTask)ValueTask)IAsyncEnumerable<T>and streaming pipelines are supportedG3 — Dual-mode consumption
The generated dispatcher supports both:
Mode A: class-based, structured (framework-like)
Mode B: lightweight, fluent (minimal ceremony)
Program.csG4 — AOT-friendly, low-overhead runtime
Non-goals
Desired Public API (generated)
Core concepts
We want three categories of messages:
And a unified dispatcher surface:
Overloads and “ergonomics”
We want overloads that cover common needs and reduce ceremony, while staying distinct:
Commands
Notifications
Streams
Notes
Pipeline behavior model (commands + streams)
We want a pipeline model that can be used in both class-based and fluent modes.
Command pipeline
Proposed delegates: