From 71999bd04dc1df8acda676b8978eea3e01b27012 Mon Sep 17 00:00:00 2001 From: JerrettDavis Date: Tue, 19 May 2026 21:25:18 -0500 Subject: [PATCH] docs: expand pattern coverage catalog --- .../examples/production-ready-integrations.md | 2 +- docs/guides/pattern-coverage.md | 36 +++- .../PatternKitPatternCatalog.cs | 196 +++++++++++++++++- .../PatternKitPatternCatalogTests.cs | 72 ++++++- 4 files changed, 289 insertions(+), 17 deletions(-) diff --git a/docs/examples/production-ready-integrations.md b/docs/examples/production-ready-integrations.md index d281144..40d6bd2 100644 --- a/docs/examples/production-ready-integrations.md +++ b/docs/examples/production-ready-integrations.md @@ -63,7 +63,7 @@ Each `PatternKitExampleDescriptor` includes: | `Patterns` | PatternKit primitives demonstrated by the example. | | `ProductionChecks` | The behaviors that make the example production-shaped and regression-testable. | -The companion `IPatternKitPatternCatalog` records the canonical GoF pattern matrix. It links each pattern to its fluent runtime path, TinyBDD tests, real-world example, and source-generated path. Any missing generated path must point to a tracked issue; currently that is limited to the dedicated Interpreter generator and the dedicated Abstract Factory family generator. +The companion `IPatternKitPatternCatalog` records the broader pattern matrix: GoF patterns, enterprise integration patterns, messaging reliability patterns, and application architecture patterns. It links each pattern to its fluent/runtime path, TinyBDD tests, real-world example, and source-generated path. Any missing generated path must point to a tracked issue so planned work stays visible. ## Validate in a generic host diff --git a/docs/guides/pattern-coverage.md b/docs/guides/pattern-coverage.md index 06ffe03..3080aeb 100644 --- a/docs/guides/pattern-coverage.md +++ b/docs/guides/pattern-coverage.md @@ -1,6 +1,8 @@ # Pattern Coverage -PatternKit tracks the canonical Gang of Four patterns as production surfaces, not just API names. Each pattern should have: +PatternKit tracks design, integration, messaging, reliability, and architecture patterns as production surfaces, not just API names. The canonical Gang of Four patterns are one baseline, but the catalog is intentionally broader: Enterprise Integration Patterns, cloud architecture patterns, DDD-adjacent application patterns, and messaging reliability patterns can all be included when PatternKit has a credible runtime or generated integration story. + +Each pattern should have: - a fluent runtime path in `PatternKit.Core` - TinyBDD coverage for the runtime path @@ -10,7 +12,7 @@ PatternKit tracks the canonical Gang of Four patterns as production surfaces, no The source of truth is `PatternKitPatternCatalog` in `src/PatternKit.Examples/ProductionReadiness`. The TinyBDD tests in `PatternKitPatternCatalogTests` validate the catalog against the repository so missing files, missing examples, or undocumented generator gaps fail in CI. -## Current GoF Coverage +## Current GoF Baseline | Family | Pattern | Fluent path | Source-generated path | | --- | --- | --- | --- | @@ -38,6 +40,36 @@ The source of truth is `PatternKitPatternCatalog` in `src/PatternKit.Examples/Pr | Behavioral | Template Method | `TemplateMethod` and fluent templates | Template Method generator | | Behavioral | Visitor | `Visitor` and variants | Visitor generator | +## Enterprise And Architecture Coverage + +| Family | Pattern | Fluent/runtime path | Source-generated path | +| --- | --- | --- | --- | +| Enterprise Integration | Message Envelope | `Message`, headers, context | Tracked in [#215](https://github.com/JerrettDavis/PatternKit/issues/215) | +| Enterprise Integration | Content-Based Router | `ContentRouter` | Messaging generator | +| Enterprise Integration | Recipient List | `RecipientList` | Tracked in [#210](https://github.com/JerrettDavis/PatternKit/issues/210) | +| Enterprise Integration | Splitter | `Splitter` | Tracked in [#211](https://github.com/JerrettDavis/PatternKit/issues/211) | +| Enterprise Integration | Aggregator | `Aggregator` | Tracked in [#211](https://github.com/JerrettDavis/PatternKit/issues/211) | +| Enterprise Integration | Routing Slip | `RoutingSlip` | Messaging generator | +| Enterprise Integration | Saga / Process Manager | `Saga` | Messaging generator | +| Enterprise Integration | Mailbox | `Mailbox` | Tracked in [#209](https://github.com/JerrettDavis/PatternKit/issues/209) | +| Messaging Reliability | Idempotent Receiver | `IdempotentReceiver` | Tracked in [#213](https://github.com/JerrettDavis/PatternKit/issues/213) | +| Messaging Reliability | Inbox | `InboxProcessor` | Tracked in [#213](https://github.com/JerrettDavis/PatternKit/issues/213) | +| Messaging Reliability | Outbox | `InMemoryOutbox` and dispatcher contracts | Tracked in [#213](https://github.com/JerrettDavis/PatternKit/issues/213) | +| Enterprise Integration | Request-Reply | Messaging backplane facade example | Tracked in [#214](https://github.com/JerrettDavis/PatternKit/issues/214) | +| Enterprise Integration | Publish-Subscribe | Messaging backplane facade example | Tracked in [#214](https://github.com/JerrettDavis/PatternKit/issues/214) | +| Application Architecture | CQRS | Mediator/dispatcher command-query split | First-class example tracked in [#212](https://github.com/JerrettDavis/PatternKit/issues/212) | + +## Research Baselines + +The catalog is allowed to grow beyond GoF when an external catalog describes a recurring enterprise pattern that PatternKit can make concrete: + +- Enterprise Integration Patterns: message routing, splitter, aggregator, routing slip, idempotent receiver, and related messaging patterns. +- Patterns of Enterprise Application Architecture: application and data-access patterns such as Unit of Work, Identity Map, Repository, and Data Mapper. +- Cloud architecture patterns: retry, circuit breaker, CQRS, external configuration, competing consumers, and related distributed-system patterns. +- Domain-driven design tactical patterns: aggregate, domain event, specification, repository, bounded context, and process manager. + +Entries should still be selective. A pattern belongs in the catalog only when PatternKit can demonstrate a runtime path, a source-generated path or tracked generator issue, documentation, TinyBDD coverage, and an importable example. + ## Adding Or Extending A Pattern 1. Add or update the fluent runtime implementation and TinyBDD tests. diff --git a/src/PatternKit.Examples/ProductionReadiness/PatternKitPatternCatalog.cs b/src/PatternKit.Examples/ProductionReadiness/PatternKitPatternCatalog.cs index 11ba104..f1efb45 100644 --- a/src/PatternKit.Examples/ProductionReadiness/PatternKitPatternCatalog.cs +++ b/src/PatternKit.Examples/ProductionReadiness/PatternKitPatternCatalog.cs @@ -3,13 +3,17 @@ namespace PatternKit.Examples.ProductionReadiness; /// -/// GoF pattern family used by the PatternKit pattern coverage catalog. +/// Pattern family used by the PatternKit pattern coverage catalog. /// public enum PatternFamily { Creational, Structural, - Behavioral + Behavioral, + EnterpriseIntegration, + MessagingReliability, + CloudArchitecture, + ApplicationArchitecture } /// @@ -37,7 +41,7 @@ public sealed record PatternImplementationPath( } /// -/// Describes one canonical GoF pattern and the PatternKit surfaces that support it. +/// Describes one design, integration, or architecture pattern and the PatternKit surfaces that support it. /// public sealed record PatternCoverageDescriptor( string Name, @@ -46,7 +50,7 @@ public sealed record PatternCoverageDescriptor( IReadOnlyList IntegrationNotes); /// -/// Read-only catalog of PatternKit's coverage for the canonical GoF design patterns. +/// Read-only catalog of PatternKit's supported and tracked design, integration, and architecture patterns. /// public interface IPatternKitPatternCatalog { @@ -357,7 +361,189 @@ public sealed class PatternKitPatternCatalog : IPatternKitPatternCatalog "docs/examples/document-processing-visitor.md", "src/PatternKit.Examples/Generators/Visitors/DocumentProcessingDemo.cs", "test/PatternKit.Examples.Tests/Generators/VisitorGeneratorExamplesTests.cs", - ["fluent visitor", "generated visitor", "document processing example"]) + ["fluent visitor", "generated visitor", "document processing example"]), + + Pattern("Message Envelope", PatternFamily.EnterpriseIntegration, + "docs/patterns/messaging/message-envelope.md", + "src/PatternKit.Core/Messaging/Message.cs", + "test/PatternKit.Tests/Messaging/MessageTests.cs", + null, + null, + null, + "https://github.com/JerrettDavis/PatternKit/issues/215", + "docs/examples/enterprise-messaging-workflows.md", + "src/PatternKit.Examples/Messaging/MessageEnvelopeExample.cs", + "test/PatternKit.Examples.Tests/Messaging/MessageEnvelopeExampleTests.cs", + ["runtime envelope and headers", "generated contract path tracked", "enterprise workflow example"]), + + Pattern("Content-Based Router", PatternFamily.EnterpriseIntegration, + "docs/patterns/messaging/message-routing.md", + "src/PatternKit.Core/Messaging/Routing/ContentRouter.cs", + "test/PatternKit.Tests/Messaging/Routing/ContentRouterTests.cs", + "docs/generators/messaging.md", + "src/PatternKit.Generators/Messaging/ContentRouterGenerator.cs", + "test/PatternKit.Generators.Tests/ContentRouterGeneratorTests.cs", + null, + "docs/examples/enterprise-messaging-workflows.md", + "src/PatternKit.Examples/Messaging/ContentRouterGeneratorExample.cs", + "test/PatternKit.Examples.Tests/Messaging/ContentRouterGeneratorExampleTests.cs", + ["fluent content router", "generated content router", "message routing example"]), + + Pattern("Recipient List", PatternFamily.EnterpriseIntegration, + "docs/patterns/messaging/message-routing.md", + "src/PatternKit.Core/Messaging/Routing/RecipientList.cs", + "test/PatternKit.Tests/Messaging/Routing/RecipientListTests.cs", + null, + null, + null, + "https://github.com/JerrettDavis/PatternKit/issues/210", + "docs/examples/enterprise-messaging-workflows.md", + "src/PatternKit.Examples/Messaging/MessageRoutingExample.cs", + "test/PatternKit.Examples.Tests/Messaging/MessageRoutingExampleTests.cs", + ["fluent recipient list", "generated recipient list tracked", "fan-out routing example"]), + + Pattern("Splitter", PatternFamily.EnterpriseIntegration, + "docs/patterns/messaging/message-routing.md", + "src/PatternKit.Core/Messaging/Routing/Splitter.cs", + "test/PatternKit.Tests/Messaging/Routing/SplitterTests.cs", + null, + null, + null, + "https://github.com/JerrettDavis/PatternKit/issues/211", + "docs/examples/enterprise-messaging-workflows.md", + "src/PatternKit.Examples/Messaging/MessageRoutingExample.cs", + "test/PatternKit.Examples.Tests/Messaging/MessageRoutingExampleTests.cs", + ["fluent splitter", "generated splitter tracked", "line-item message example"]), + + Pattern("Aggregator", PatternFamily.EnterpriseIntegration, + "docs/patterns/messaging/message-routing.md", + "src/PatternKit.Core/Messaging/Routing/Aggregator.cs", + "test/PatternKit.Tests/Messaging/Routing/AggregatorTests.cs", + null, + null, + null, + "https://github.com/JerrettDavis/PatternKit/issues/211", + "docs/examples/enterprise-messaging-workflows.md", + "src/PatternKit.Examples/Messaging/MessageRoutingExample.cs", + "test/PatternKit.Examples.Tests/Messaging/MessageRoutingExampleTests.cs", + ["fluent aggregator", "generated aggregator tracked", "correlated total example"]), + + Pattern("Routing Slip", PatternFamily.EnterpriseIntegration, + "docs/patterns/messaging/routing-slip.md", + "src/PatternKit.Core/Messaging/Routing/RoutingSlip.cs", + "test/PatternKit.Tests/Messaging/Routing/RoutingSlipTests.cs", + "docs/generators/messaging.md", + "src/PatternKit.Generators/Messaging/RoutingSlipGenerator.cs", + "test/PatternKit.Generators.Tests/RoutingSlipGeneratorTests.cs", + null, + "docs/examples/enterprise-messaging-workflows.md", + "src/PatternKit.Examples/Messaging/RoutingSlipExample.cs", + "test/PatternKit.Examples.Tests/Messaging/RoutingSlipExampleTests.cs", + ["fluent routing slip", "generated routing slip", "fulfillment itinerary example"]), + + Pattern("Saga / Process Manager", PatternFamily.EnterpriseIntegration, + "docs/patterns/messaging/saga.md", + "src/PatternKit.Core/Messaging/Sagas/Saga.cs", + "test/PatternKit.Tests/Messaging/Sagas/SagaTests.cs", + "docs/generators/messaging.md", + "src/PatternKit.Generators/Messaging/SagaGenerator.cs", + "test/PatternKit.Generators.Tests/SagaGeneratorTests.cs", + null, + "docs/examples/enterprise-messaging-workflows.md", + "src/PatternKit.Examples/Messaging/SagaExample.cs", + "test/PatternKit.Examples.Tests/Messaging/SagaExampleTests.cs", + ["fluent saga", "generated process manager", "order lifecycle example"]), + + Pattern("Mailbox", PatternFamily.EnterpriseIntegration, + "docs/patterns/messaging/mailbox.md", + "src/PatternKit.Core/Messaging/Mailboxes/Mailbox.cs", + "test/PatternKit.Tests/Messaging/Mailboxes/MailboxTests.cs", + null, + null, + null, + "https://github.com/JerrettDavis/PatternKit/issues/209", + "docs/examples/enterprise-messaging-workflows.md", + "src/PatternKit.Examples/Messaging/MailboxExample.cs", + "test/PatternKit.Examples.Tests/Messaging/MailboxExampleTests.cs", + ["fluent serialized inbox", "generated mailbox tracked", "bounded worker example"]), + + Pattern("Idempotent Receiver", PatternFamily.MessagingReliability, + "docs/patterns/messaging/reliability.md", + "src/PatternKit.Core/Messaging/Reliability/IdempotentReceiver.cs", + "test/PatternKit.Tests/Messaging/Reliability/IdempotentReceiverTests.cs", + null, + null, + null, + "https://github.com/JerrettDavis/PatternKit/issues/213", + "docs/examples/enterprise-messaging-workflows.md", + "src/PatternKit.Examples/Messaging/ReliabilityExample.cs", + "test/PatternKit.Examples.Tests/Messaging/ReliabilityExampleTests.cs", + ["runtime duplicate suppression", "generated reliability path tracked", "at-least-once example"]), + + Pattern("Inbox", PatternFamily.MessagingReliability, + "docs/patterns/messaging/reliability.md", + "src/PatternKit.Core/Messaging/Reliability/InboxProcessor.cs", + "test/PatternKit.Tests/Messaging/Reliability/IdempotentReceiverTests.cs", + null, + null, + null, + "https://github.com/JerrettDavis/PatternKit/issues/213", + "docs/examples/enterprise-messaging-workflows.md", + "src/PatternKit.Examples/Messaging/ReliabilityExample.cs", + "test/PatternKit.Examples.Tests/Messaging/ReliabilityExampleTests.cs", + ["runtime inbox boundary", "generated inbox path tracked", "reliable handoff example"]), + + Pattern("Outbox", PatternFamily.MessagingReliability, + "docs/patterns/messaging/reliability.md", + "src/PatternKit.Core/Messaging/Reliability/InMemoryOutbox.cs", + "test/PatternKit.Tests/Messaging/Reliability/OutboxTests.cs", + null, + null, + null, + "https://github.com/JerrettDavis/PatternKit/issues/213", + "docs/examples/enterprise-messaging-workflows.md", + "src/PatternKit.Examples/Messaging/ReliabilityExample.cs", + "test/PatternKit.Examples.Tests/Messaging/ReliabilityExampleTests.cs", + ["runtime outbox", "generated outbox path tracked", "dispatch handoff example"]), + + Pattern("Request-Reply", PatternFamily.EnterpriseIntegration, + "docs/examples/messaging-backplane-facade.md", + "src/PatternKit.Examples/Messaging/BackplaneFacadeDemo.cs", + "test/PatternKit.Examples.Tests/Messaging/BackplaneFacadeDemoTests.cs", + null, + null, + null, + "https://github.com/JerrettDavis/PatternKit/issues/214", + "docs/examples/messaging-backplane-facade.md", + "src/PatternKit.Examples/Messaging/BackplaneFacadeDemo.cs", + "test/PatternKit.Examples.Tests/Messaging/BackplaneFacadeDemoTests.cs", + ["typed request/reply client", "generated backplane topology tracked", "hosted backplane example"]), + + Pattern("Publish-Subscribe", PatternFamily.EnterpriseIntegration, + "docs/examples/messaging-backplane-facade.md", + "src/PatternKit.Examples/Messaging/BackplaneFacadeDemo.cs", + "test/PatternKit.Examples.Tests/Messaging/BackplaneFacadeDemoTests.cs", + null, + null, + null, + "https://github.com/JerrettDavis/PatternKit/issues/214", + "docs/examples/messaging-backplane-facade.md", + "src/PatternKit.Examples/Messaging/BackplaneFacadeDemo.cs", + "test/PatternKit.Examples.Tests/Messaging/BackplaneFacadeDemoTests.cs", + ["typed publish/subscribe", "generated backplane topology tracked", "transport boundary example"]), + + Pattern("CQRS", PatternFamily.ApplicationArchitecture, + "docs/generators/dispatcher.md", + "src/PatternKit.Core/Behavioral/Mediator/Mediator.cs", + "test/PatternKit.Tests/Behavioral/Mediator/MediatorTests.cs", + "docs/generators/dispatcher.md", + "src/PatternKit.Generators/Messaging/DispatcherGenerator.cs", + "test/PatternKit.Generators.Tests/DispatcherGeneratorTests.cs", + "https://github.com/JerrettDavis/PatternKit/issues/212", + "docs/generators/dispatcher.md", + "src/PatternKit.Examples/MediatorComprehensiveDemo/ComprehensiveDemo.cs", + "test/PatternKit.Examples.Tests/MediatorDemo/MediatorDemoTests.cs", + ["dispatcher command/query separation", "generated dispatcher", "first-class CQRS example tracked"]) ]; public IReadOnlyList Patterns => Items; diff --git a/test/PatternKit.Examples.Tests/ProductionReadiness/PatternKitPatternCatalogTests.cs b/test/PatternKit.Examples.Tests/ProductionReadiness/PatternKitPatternCatalogTests.cs index e2fb01c..fee1ced 100644 --- a/test/PatternKit.Examples.Tests/ProductionReadiness/PatternKitPatternCatalogTests.cs +++ b/test/PatternKit.Examples.Tests/ProductionReadiness/PatternKitPatternCatalogTests.cs @@ -6,7 +6,7 @@ namespace PatternKit.Examples.Tests.ProductionReadiness; -[Feature("GoF pattern coverage catalog")] +[Feature("Pattern coverage catalog")] public sealed class PatternKitPatternCatalogTests(ITestOutputHelper output) : TinyBddXunitBase(output) { private static readonly string[] CanonicalGofPatterns = @@ -36,6 +36,24 @@ public sealed class PatternKitPatternCatalogTests(ITestOutputHelper output) : Ti "Visitor" ]; + private static readonly string[] EnterprisePatternAdditions = + [ + "Message Envelope", + "Content-Based Router", + "Recipient List", + "Splitter", + "Aggregator", + "Routing Slip", + "Saga / Process Manager", + "Mailbox", + "Idempotent Receiver", + "Inbox", + "Outbox", + "Request-Reply", + "Publish-Subscribe", + "CQRS" + ]; + [Scenario("Catalog covers every canonical GoF pattern")] [Fact] public Task Catalog_Covers_Every_Canonical_Gof_Pattern() @@ -43,14 +61,40 @@ public Task Catalog_Covers_Every_Canonical_Gof_Pattern() .When("reading the catalog entries", catalog => catalog.Patterns) .Then("all canonical GoF patterns are represented exactly once", patterns => { - ScenarioExpect.Equal(CanonicalGofPatterns.OrderBy(static x => x), patterns.Select(static p => p.Name).OrderBy(static x => x)); - ScenarioExpect.Equal(CanonicalGofPatterns.Length, patterns.Select(static p => p.Name).Distinct(StringComparer.Ordinal).Count()); + var canonical = patterns + .Where(pattern => CanonicalGofPatterns.Contains(pattern.Name, StringComparer.Ordinal)) + .Select(static p => p.Name) + .OrderBy(static x => x); + + ScenarioExpect.Equal(CanonicalGofPatterns.OrderBy(static x => x), canonical); + ScenarioExpect.Equal(patterns.Count, patterns.Select(static p => p.Name).Distinct(StringComparer.Ordinal).Count()); }) .And("the catalog keeps the GoF family counts honest", patterns => { - ScenarioExpect.Equal(5, patterns.Count(static p => p.Family == PatternFamily.Creational)); - ScenarioExpect.Equal(7, patterns.Count(static p => p.Family == PatternFamily.Structural)); - ScenarioExpect.Equal(11, patterns.Count(static p => p.Family == PatternFamily.Behavioral)); + var canonical = patterns + .Where(pattern => CanonicalGofPatterns.Contains(pattern.Name, StringComparer.Ordinal)) + .ToArray(); + + ScenarioExpect.Equal(5, canonical.Count(static p => p.Family == PatternFamily.Creational)); + ScenarioExpect.Equal(7, canonical.Count(static p => p.Family == PatternFamily.Structural)); + ScenarioExpect.Equal(11, canonical.Count(static p => p.Family == PatternFamily.Behavioral)); + }) + .AssertPassed(); + + [Scenario("Catalog includes enterprise integration and architecture patterns")] + [Fact] + public Task Catalog_Includes_Enterprise_Integration_And_Architecture_Patterns() + => Given("the PatternKit pattern catalog", () => new PatternKitPatternCatalog()) + .When("reading the non-GoF pattern entries", catalog => catalog.Patterns + .Where(pattern => !CanonicalGofPatterns.Contains(pattern.Name, StringComparer.Ordinal)) + .ToArray()) + .Then("enterprise pattern additions are represented", patterns => + ScenarioExpect.Equal(EnterprisePatternAdditions.OrderBy(static x => x), patterns.Select(static p => p.Name).OrderBy(static x => x))) + .And("enterprise entries are grouped by integration reliability and architecture families", patterns => + { + ScenarioExpect.Equal(10, patterns.Count(static p => p.Family == PatternFamily.EnterpriseIntegration)); + ScenarioExpect.Equal(3, patterns.Count(static p => p.Family == PatternFamily.MessagingReliability)); + ScenarioExpect.Equal(1, patterns.Count(static p => p.Family == PatternFamily.ApplicationArchitecture)); }) .AssertPassed(); @@ -77,7 +121,17 @@ public Task Each_Pattern_Has_Fluent_Generated_Documented_And_Example_Paths() ScenarioExpect.Equal( [ "Abstract Factory has a tracked source-generated gap: https://github.com/JerrettDavis/PatternKit/issues/207", - "Interpreter has a tracked source-generated gap: https://github.com/JerrettDavis/PatternKit/issues/206" + "Aggregator has a tracked source-generated gap: https://github.com/JerrettDavis/PatternKit/issues/211", + "Idempotent Receiver has a tracked source-generated gap: https://github.com/JerrettDavis/PatternKit/issues/213", + "Inbox has a tracked source-generated gap: https://github.com/JerrettDavis/PatternKit/issues/213", + "Interpreter has a tracked source-generated gap: https://github.com/JerrettDavis/PatternKit/issues/206", + "Mailbox has a tracked source-generated gap: https://github.com/JerrettDavis/PatternKit/issues/209", + "Message Envelope has a tracked source-generated gap: https://github.com/JerrettDavis/PatternKit/issues/215", + "Outbox has a tracked source-generated gap: https://github.com/JerrettDavis/PatternKit/issues/213", + "Publish-Subscribe has a tracked source-generated gap: https://github.com/JerrettDavis/PatternKit/issues/214", + "Recipient List has a tracked source-generated gap: https://github.com/JerrettDavis/PatternKit/issues/210", + "Request-Reply has a tracked source-generated gap: https://github.com/JerrettDavis/PatternKit/issues/214", + "Splitter has a tracked source-generated gap: https://github.com/JerrettDavis/PatternKit/issues/211" ], tracked); }) .AssertPassed(); @@ -96,8 +150,8 @@ public Task Pattern_Catalog_Is_Available_Through_IServiceCollection() using (provider) return provider.GetRequiredService(); }) - .Then("the catalog resolves all GoF patterns", catalog => - ScenarioExpect.Equal(CanonicalGofPatterns.Length, catalog.Patterns.Count)) + .Then("the catalog resolves GoF and enterprise patterns", catalog => + ScenarioExpect.Equal(CanonicalGofPatterns.Length + EnterprisePatternAdditions.Length, catalog.Patterns.Count)) .And("all patterns include user-facing integration notes", catalog => ScenarioExpect.True(catalog.Patterns.All(static pattern => pattern.IntegrationNotes.Count > 0))) .AssertPassed();