Skip to content

feat(messaging): add dynamic router pattern#368

Merged
JerrettDavis merged 1 commit into
mainfrom
feat/dynamic-router-pattern
May 26, 2026
Merged

feat(messaging): add dynamic router pattern#368
JerrettDavis merged 1 commit into
mainfrom
feat/dynamic-router-pattern

Conversation

@JerrettDavis

Copy link
Copy Markdown
Owner

Summary

  • add DynamicRouter fluent runtime API with runtime route registration/removal
  • add [GenerateDynamicRouter] source generator, diagnostics, and TinyBDD generator coverage
  • add DI-importable order fulfillment example, catalogs, docs, pattern table, and benchmark matrix updates
  • add BenchmarkDotNet dynamic router scenario with measured fluent vs generated rows

Validation

  • dotnet build PatternKit.slnx --configuration Release --no-restore -m:1
  • dotnet test test/PatternKit.Tests/PatternKit.Tests.csproj --configuration Release --no-build --logger "console;verbosity=minimal" -p:TestTfmsInParallel=false
  • dotnet test test/PatternKit.Generators.Tests/PatternKit.Generators.Tests.csproj --configuration Release --no-build --logger "console;verbosity=minimal" -p:TestTfmsInParallel=false
  • dotnet test test/PatternKit.Examples.Tests/PatternKit.Examples.Tests.csproj --configuration Release --no-build --logger "console;verbosity=minimal" -p:TestTfmsInParallel=false
  • dotnet run --project benchmarks/PatternKit.Benchmarks/PatternKit.Benchmarks.csproj -c Release --framework net10.0 -- --filter DynamicRouterBenchmarks --artifacts ./BenchmarkDotNet.Artifacts/DynamicRouter --exporters json

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Deprecation Warning: The deny-licenses option is deprecated for possible removal in the next major release. For more information, see issue 997.

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@github-actions

Copy link
Copy Markdown
Contributor

Test Results

    1 files      1 suites   2m 8s ⏱️
1 017 tests 1 017 ✅ 0 💤 0 ❌
1 022 runs  1 022 ✅ 0 💤 0 ❌

Results for commit 5093753.

@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR Validation Results

Version: ``

✅ Validation Steps

  • Build solution
  • Run tests
  • Build documentation
  • Dry-run NuGet packaging

📊 Artifacts

Dry-run artifacts have been uploaded and will be available for 7 days.


This comment was automatically generated by the PR validation workflow.

@codecov

codecov Bot commented May 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.50327% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.61%. Comparing base (0659206) to head (5093753).

Files with missing lines Patch % Lines
....Abstractions/Messaging/DynamicRouterAttributes.cs 21.05% 15 Missing ⚠️
...Kit.Generators/Messaging/DynamicRouterGenerator.cs 93.03% 11 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #368      +/-   ##
==========================================
+ Coverage   89.65%   95.61%   +5.96%     
==========================================
  Files         496      500       +4     
  Lines       40657    40963     +306     
  Branches     5859     5911      +52     
==========================================
+ Hits        36449    39165    +2716     
+ Misses       1912     1798     -114     
+ Partials     2296        0    -2296     
Flag Coverage Δ
unittests 95.61% <91.50%> (+5.96%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

Copy link
Copy Markdown
Contributor

Code Coverage

Summary
  Generated on: 05/26/2026 - 13:50:39
  Coverage date: 05/26/2026 - 13:49:10 - 05/26/2026 - 13:50:28
  Parser: MultiReport (9x Cobertura)
  Assemblies: 4
  Classes: 1507
  Files: 610
  Line coverage: 94.5%
  Covered lines: 40170
  Uncovered lines: 2307
  Coverable lines: 42477
  Total lines: 93185
  Branch coverage: 75.6% (11841 of 15659)
  Covered branches: 11841
  Total branches: 15659
  Method coverage: 95.9% (8012 of 8348)
  Full method coverage: 88.1% (7356 of 8348)
  Covered methods: 8012
  Fully covered methods: 7356
  Total methods: 8348

PatternKit.Core                                                                                                     95.4%
  PatternKit.Application.AntiCorruption.AntiCorruptionLayer<T1, T2>                                                 90.4%
  PatternKit.Application.AntiCorruption.AntiCorruptionResult<T>                                                      100%
  PatternKit.Application.AuditLog.AuditLogAppendResult<T>                                                           85.7%
  PatternKit.Application.AuditLog.InMemoryAuditLog<T1, T2>                                                          95.4%
  PatternKit.Application.DataMapping.DataMapper<T1, T2>                                                             94.6%
  PatternKit.Application.DataMapping.DataMapperError                                                                  90%
  PatternKit.Application.DataMapping.DataMapperResult<T>                                                            84.6%
  PatternKit.Application.DomainEvents.DomainEventDispatcher<T>                                                      95.4%
  PatternKit.Application.DomainEvents.DomainEventDispatchResult                                                      100%
  PatternKit.Application.EventSourcing.EventStoreAppendResult                                                        100%
  PatternKit.Application.EventSourcing.InMemoryEventStore<T1, T2>                                                   97.9%
  PatternKit.Application.EventSourcing.StoredEvent<T1, T2>                                                            80%
  PatternKit.Application.FeatureToggles.FeatureToggleDecision                                                       87.5%
  PatternKit.Application.FeatureToggles.FeatureToggleRule<T>                                                         100%
  PatternKit.Application.FeatureToggles.FeatureToggleSet<T>                                                         96.9%
  PatternKit.Application.IdentityMap.IdentityMap<T1, T2>                                                             100%
  PatternKit.Application.IdentityMap.IdentityMapResult<T>                                                           92.8%
  PatternKit.Application.MaterializedViews.MaterializedView<T1, T2>                                                 98.4%
  PatternKit.Application.Repository.InMemoryRepository<T1, T2>                                                      92.8%
  PatternKit.Application.Repository.RepositoryResult<T>                                                             93.3%
  PatternKit.Application.ServiceLayer.ServiceLayerOperation<T1, T2>                                                 96.7%
  PatternKit.Application.ServiceLayer.ServiceLayerResult<T>                                                         94.7%
  PatternKit.Application.ServiceLayer.ServiceLayerRule<T>                                                            100%
  PatternKit.Application.Specification.Specification<T>                                                              100%
  PatternKit.Application.Specification.SpecificationRegistry<T>                                                     93.3%
  PatternKit.Application.TableDataGateway.InMemoryTableDataGateway<T1, T2>                                            86%
  PatternKit.Application.TableDataGateway.TableGatewayResult<T>                                                     82.3%
  PatternKit.Application.TransactionScript.TransactionScript<T1, T2>                                                  97%
  PatternKit.Application.TransactionScript.TransactionScriptError                                                     90%
  PatternKit.Application.TransactionScript.TransactionScriptResult<T>                                                100%
  PatternKit.Application.UnitOfWork.UnitOfWork                                                                      90.9%
  PatternKit.Application.UnitOfWork.UnitOfWorkResult                                                                94.7%
  PatternKit.Application.UnitOfWork.UnitOfWorkRollbackResult                                                         100%
  PatternKit.Application.UnitOfWork.UnitOfWorkStep                                                                   100%
  PatternKit.Behavioral.Chain.ActionChain<T>                                                                         100%
  PatternKit.Behavioral.Chain.AsyncActionChain<T>                                                                    100%
  PatternKit.Behavioral.Chain.AsyncResultChain<T1, T2>                                                              97.7%
  PatternKit.Behavioral.Chain.ResultChain<T1, T2>                                                                    100%

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 introduces a new Dynamic Router messaging pattern to PatternKit, including a runtime-mutable DynamicRouter<TPayload, TResult> API and a companion [GenerateDynamicRouter] source generator, plus end-to-end examples, docs, and benchmark coverage to keep the production-readiness catalog consistent.

Changes:

  • Added DynamicRouter<TPayload, TResult> (fluent builder + runtime register/unregister) and TinyBDD coverage.
  • Added DynamicRouterGenerator + new generator attributes and Roslyn-based generator tests/diagnostics coverage.
  • Added a DI-importable order fulfillment example, documentation entries, pattern catalog updates, and BenchmarkDotNet scenario/results matrix updates.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
test/PatternKit.Tests/Messaging/Routing/DynamicRouterTests.cs New unit tests for fluent + runtime mutation behavior and argument validation.
test/PatternKit.Generators.Tests/DynamicRouterGeneratorTests.cs New Roslyn generator tests covering generation and diagnostics.
test/PatternKit.Examples.Tests/ProductionReadiness/PatternKitPatternCatalogTests.cs Updates catalog assertions to include Dynamic Router.
test/PatternKit.Examples.Tests/ProductionReadiness/PatternKitBenchmarkCoverageTests.cs Updates benchmark matrix expectations for new pattern count/results.
test/PatternKit.Examples.Tests/Messaging/OrderDynamicRouterExampleTests.cs Adds example validation for fluent vs generated parity and DI wiring.
src/PatternKit.Generators/Messaging/DynamicRouterGenerator.cs New incremental generator implementation for dynamic router factories + diagnostics.
src/PatternKit.Generators.Abstractions/Messaging/DynamicRouterAttributes.cs New public attributes for dynamic router generation.
src/PatternKit.Examples/ProductionReadiness/PatternKitPatternCatalog.cs Registers Dynamic Router in the production-readiness pattern catalog.
src/PatternKit.Examples/ProductionReadiness/PatternKitExampleCatalog.cs Adds “Order Dynamic Router” example descriptor.
src/PatternKit.Examples/Messaging/OrderDynamicRouterExample.cs Adds runtime + generated example, runner, and DI extension.
src/PatternKit.Examples/DependencyInjection/PatternKitExampleServiceCollectionExtensions.cs Wires the new example into the aggregate examples registration surface.
src/PatternKit.Core/Messaging/Routing/DynamicRouter.cs New runtime-mutable router implementation.
README.md Updates pattern counts and benchmark matrix entries.
docs/patterns/toc.yml Adds Dynamic Router to pattern docs TOC.
docs/patterns/messaging/dynamic-router.md New Dynamic Router pattern documentation page.
docs/guides/pattern-coverage.md Adds Dynamic Router to the coverage guide table.
docs/guides/benchmarks.md Adds Dynamic Router benchmark rows to published guidance.
docs/guides/benchmark-results.md Updates published benchmark results + coverage matrix for new pattern/generator.
docs/generators/toc.yml Adds Dynamic Router generator to generator docs TOC.
docs/generators/dynamic-router.md New Dynamic Router generator documentation page.
docs/examples/toc.yml Adds Order Dynamic Router to examples TOC.
docs/examples/order-dynamic-router.md New Order Dynamic Router example documentation page.
benchmarks/PatternKit.Benchmarks/Messaging/DynamicRouterBenchmarks.cs Adds a Dynamic Router BenchmarkDotNet scenario (fluent vs generated).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +38 to +47
var entry = new RouteEntry(name, order, predicate, handler);
lock (_gate)
{
_routes = _routes
.Where(route => !string.Equals(route.Name, name, StringComparison.Ordinal))
.Append(entry)
.OrderBy(static route => route.Order)
.ThenBy(static route => route.Name, StringComparer.Ordinal)
.ToArray();
}
Comment on lines +69 to +79
/// <summary>Routes <paramref name="message"/> through the current route snapshot.</summary>
public TResult Route(Message<TPayload> message, MessageContext? context = null)
{
if (message is null)
throw new ArgumentNullException(nameof(message));

var effectiveContext = context ?? MessageContext.From(message);
var snapshot = _routes;
foreach (var route in snapshot)
if (route.Predicate(message, effectiveContext))
return route.Handler(message, effectiveContext);
Comment on lines +236 to +242

foreach (var route in routes)
sb.Append(" .When(@\"").Append(route.Name).Append("\", ").Append(route.Order).Append(", ").Append(route.PredicateMethodName).Append(").Then(").Append(route.HandlerMethodName).AppendLine(")");

if (defaultHandler is not null)
sb.Append(" .Default(").Append(defaultHandler).AppendLine(")");

@JerrettDavis JerrettDavis merged commit c232ec0 into main May 26, 2026
13 checks passed
@JerrettDavis JerrettDavis deleted the feat/dynamic-router-pattern branch May 26, 2026 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants