Skip to content

feat: add Sidecar pattern#323

Merged
JerrettDavis merged 1 commit into
mainfrom
feature/sidecar-316
May 22, 2026
Merged

feat: add Sidecar pattern#323
JerrettDavis merged 1 commit into
mainfrom
feature/sidecar-316

Conversation

@JerrettDavis
Copy link
Copy Markdown
Owner

Summary

  • add Sidecar runtime with companion before/after steps around a primary handler
  • add source generator attributes/generator with diagnostics PKSC001-PKSC004
  • add order telemetry sidecar example with IServiceCollection and ASP.NET Core endpoint integration
  • document pattern, generator, example, and production readiness catalogs

Closes #316

Validation

  • dotnet build src\PatternKit.Core\PatternKit.Core.csproj -f netstandard2.0 /p:UseSharedCompilation=false
  • dotnet build src\PatternKit.Core\PatternKit.Core.csproj -f net8.0 /p:UseSharedCompilation=false
  • dotnet build src\PatternKit.Core\PatternKit.Core.csproj -f net9.0 /p:UseSharedCompilation=false
  • dotnet build src\PatternKit.Core\PatternKit.Core.csproj -f net10.0 /p:UseSharedCompilation=false
  • dotnet build src\PatternKit.Generators.Abstractions\PatternKit.Generators.Abstractions.csproj /p:UseSharedCompilation=false
  • dotnet build src\PatternKit.Generators\PatternKit.Generators.csproj /p:UseSharedCompilation=false
  • dotnet test test\PatternKit.Tests\PatternKit.Tests.csproj -f net8.0 --no-restore --filter "FullyQualifiedName~SidecarTests" /p:BuildProjectReferences=false /p:UseSharedCompilation=false
  • dotnet test test\PatternKit.Tests\PatternKit.Tests.csproj -f net9.0 --no-restore --filter "FullyQualifiedName~SidecarTests" /p:BuildProjectReferences=false /p:UseSharedCompilation=false
  • dotnet test test\PatternKit.Tests\PatternKit.Tests.csproj -f net10.0 --no-restore --filter "FullyQualifiedName~SidecarTests" /p:BuildProjectReferences=false /p:UseSharedCompilation=false
  • dotnet test test\PatternKit.Generators.Tests\PatternKit.Generators.Tests.csproj -f net8.0 --no-restore --filter "FullyQualifiedNameSidecarGeneratorTests|FullyQualifiedNameAbstractionsAttributeCoverageTests" /p:BuildProjectReferences=false /p:UseSharedCompilation=false
  • dotnet test test\PatternKit.Generators.Tests\PatternKit.Generators.Tests.csproj -f net9.0 --no-restore --filter "FullyQualifiedNameSidecarGeneratorTests|FullyQualifiedNameAbstractionsAttributeCoverageTests" /p:BuildProjectReferences=false /p:UseSharedCompilation=false
  • dotnet test test\PatternKit.Generators.Tests\PatternKit.Generators.Tests.csproj -f net10.0 --no-restore --filter "FullyQualifiedNameSidecarGeneratorTests|FullyQualifiedNameAbstractionsAttributeCoverageTests" /p:BuildProjectReferences=false /p:UseSharedCompilation=false
  • git diff --check

Note: local full examples build remains blocked by the known CS9057 analyzer/compiler mismatch; hosted CI is authoritative for the full examples/docs pass.

Copilot AI review requested due to automatic review settings May 22, 2026 17:20
@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 438 tests   1 438 ✅  16s ⏱️
    1 suites      0 💤
    1 files        0 ❌

Results for commit 4edfdb3.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new “Sidecar” cloud-architecture pattern to PatternKit, including a fluent runtime API, a Roslyn incremental source generator (with diagnostics), a production-readiness catalog entry, and a DI + ASP.NET Core minimal API example.

Changes:

  • Introduces Sidecar<TRequest,TResponse> runtime with before/after companion steps and a primary handler result model.
  • Adds [GenerateSidecar] / [SidecarBefore] / [SidecarAfter] / [SidecarHandler] attributes plus SidecarGenerator with PKSC001–PKSC004 diagnostics.
  • Adds an “Order Telemetry Sidecar” example (DI registration + endpoint mapping) and documents/catalogs the new pattern, generator, and example.

Reviewed changes

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

Show a summary per file
File Description
test/PatternKit.Tests/Cloud/Sidecar/SidecarTests.cs Adds runtime coverage for sidecar execution order, failure behavior, and validation.
test/PatternKit.Generators.Tests/SidecarGeneratorTests.cs Validates generator output and PKSC diagnostics for invalid declarations.
test/PatternKit.Generators.Tests/AbstractionsAttributeCoverageTests.cs Extends attribute coverage tests to include Sidecar generator attributes.
test/PatternKit.Examples.Tests/SidecarDemo/OrderTelemetrySidecarDemoTests.cs Adds example-level tests for fluent + DI-imported generated sidecar usage and catalog checks.
test/PatternKit.Examples.Tests/ProductionReadiness/PatternKitPatternCatalogTests.cs Updates expected pattern lists/counts to include Sidecar in CloudArchitecture.
src/PatternKit.Generators/Sidecar/SidecarGenerator.cs Implements the Sidecar source generator and diagnostics PKSC001–PKSC004.
src/PatternKit.Generators/AnalyzerReleases.Unshipped.md Registers new analyzer IDs/descriptions for Sidecar diagnostics.
src/PatternKit.Generators.Abstractions/Cloud/SidecarAttributes.cs Adds public generator attributes for Sidecar declarations.
src/PatternKit.Examples/SidecarDemo/OrderTelemetrySidecarDemo.cs Adds runnable Sidecar example with DI and minimal API endpoint integration.
src/PatternKit.Examples/ProductionReadiness/PatternKitPatternCatalog.cs Catalogs Sidecar pattern + generator + example assets for production readiness tracking.
src/PatternKit.Examples/ProductionReadiness/PatternKitExampleCatalog.cs Adds “Order Telemetry Sidecar” example descriptor to the example catalog.
src/PatternKit.Examples/DependencyInjection/PatternKitExampleServiceCollectionExtensions.cs Adds DI import helper for the new sidecar example and wires it into AddPatternKitExamples().
src/PatternKit.Core/Cloud/Sidecar/Sidecar.cs Introduces the Sidecar runtime types (SidecarContext, SidecarResult, Sidecar + builder).
docs/patterns/toc.yml Adds Sidecar to the patterns documentation TOC.
docs/patterns/cloud/sidecar.md Adds Sidecar pattern documentation and basic usage snippet.
docs/guides/pattern-coverage.md Updates pattern coverage matrix to include Sidecar + generator.
docs/generators/toc.yml Adds Sidecar generator doc to generators TOC.
docs/generators/sidecar.md Adds Sidecar generator usage and diagnostic reference documentation.
docs/generators/index.md Adds Sidecar to generator index and quick reference snippet.
docs/examples/toc.yml Adds Order Telemetry Sidecar example to the examples TOC.
docs/examples/order-telemetry-sidecar.md Adds short documentation page for the sidecar example.
docs/examples/index.md Lists the new example in the examples index page.

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

Comment on lines +115 to +123
public Builder Before(string name, Action<SidecarContext<TRequest>> step)
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("Sidecar step name is required.", nameof(name));
if (step is null)
throw new ArgumentNullException(nameof(step));
if (_before.Any(item => string.Equals(item.Name, name, StringComparison.OrdinalIgnoreCase)))
throw new InvalidOperationException($"Sidecar before step '{name}' is already registered.");

Comment on lines +128 to +136
public Builder After(string name, Action<SidecarContext<TRequest>, TResponse> step)
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("Sidecar step name is required.", nameof(name));
if (step is null)
throw new ArgumentNullException(nameof(step));
if (_after.Any(item => string.Equals(item.Name, name, StringComparison.OrdinalIgnoreCase)))
throw new InvalidOperationException($"Sidecar after step '{name}' is already registered.");

Comment on lines +71 to +85
var before = NamedMembers(type, BeforeAttributeName);
var after = NamedMembers(type, AfterAttributeName);
var handlers = MembersWith(type, HandlerAttributeName);
if (before.Length + after.Length == 0 || handlers.Length != 1)
{
context.ReportDiagnostic(Diagnostic.Create(MissingMembers, node.Identifier.GetLocation(), type.Name));
return;
}

var duplicate = before.Concat(after).GroupBy(static item => item.Name, StringComparer.OrdinalIgnoreCase).FirstOrDefault(static group => group.Count() > 1);
if (duplicate is not null)
{
context.ReportDiagnostic(Diagnostic.Create(DuplicateStep, node.Identifier.GetLocation(), duplicate.Key));
return;
}
@github-actions
Copy link
Copy Markdown
Contributor

Code Coverage

Summary
  Generated on: 05/22/2026 - 17:25:44
  Coverage date: 05/22/2026 - 17:24:13 - 05/22/2026 - 17:25:33
  Parser: MultiReport (9x Cobertura)
  Assemblies: 4
  Classes: 1369
  Files: 562
  Line coverage: 94.7%
  Covered lines: 37467
  Uncovered lines: 2080
  Coverable lines: 39547
  Total lines: 86913
  Branch coverage: 75.6% (10910 of 14423)
  Covered branches: 10910
  Total branches: 14423
  Method coverage: 96.2% (7342 of 7625)
  Full method coverage: 88.3% (6738 of 7625)
  Covered methods: 7342
  Fully covered methods: 6738
  Total methods: 7625

PatternKit.Core                                                                                                     95.8%
  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%

@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
Copy link
Copy Markdown

codecov Bot commented May 22, 2026

Codecov Report

❌ Patch coverage is 90.47619% with 28 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.82%. Comparing base (ec6c95c) to head (4edfdb3).

Files with missing lines Patch % Lines
.../PatternKit.Generators/Sidecar/SidecarGenerator.cs 91.03% 13 Missing ⚠️
src/PatternKit.Core/Cloud/Sidecar/Sidecar.cs 88.88% 9 Missing ⚠️
....Examples/SidecarDemo/OrderTelemetrySidecarDemo.cs 79.31% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #323      +/-   ##
==========================================
+ Coverage   89.96%   95.82%   +5.85%     
==========================================
  Files         456      460       +4     
  Lines       37785    38077     +292     
  Branches     5377     5424      +47     
==========================================
+ Hits        33994    36488    +2494     
+ Misses       1685     1589      -96     
+ Partials     2106        0    -2106     
Flag Coverage Δ
unittests 95.82% <90.47%> (+5.85%) ⬆️

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.

@JerrettDavis JerrettDavis merged commit c08bcc4 into main May 22, 2026
13 checks passed
@JerrettDavis JerrettDavis deleted the feature/sidecar-316 branch May 22, 2026 17:32
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.

Add Sidecar pattern

2 participants