This document captures the main architecture risks implied by the current baseline.
It is a critique and preservation aid, not a governing document. It does not replace or override:
Use this document to understand where the baseline is strongest, where it is most expensive to maintain, and which architectural concerns deserve the most active attention.
The risks below are not claims that the architecture is unsound. In most cases, they are the costs of deliberate strengths in the baseline.
In practice, the most important risks in this repository are not shortcut risks. They are:
- concentration risks around baseline-owned platform seams
- consistency risks across advanced runtime behavior
- adoption risks where the baseline can be misunderstood or over-trusted
- maintenance risks caused by the cost of keeping the governed system aligned
| Priority | Risk | Why it matters |
|---|---|---|
| P1 | Baseline-owned core seam concentration | Too much repo-wide behavior depends on custom platform code |
| P1 | Module-state execution convergence drift | One of the baseline's signature promises depends on the shared execution gate staying aligned across execution paths |
| Resolved by the role-based production-grade Identity refactor; see entry 3 below | ||
| P2 | Reference-module interdependence | Teaching realism increases adoption and cleanup complexity |
| P2 | Persistence posture misread risk | Adopters can mistake specialized PostgreSQL patterns for the default feature-module path |
| P2 | Governance freshness burden | The architecture only remains credible while docs, gates, scaffold, and code stay aligned |
| P3 | Full validation environment cost | The strongest quality wall depends on non-trivial local and CI setup |
| P3 | Frontend generation-discipline dependence | The frontend posture is only strong while generated-contract discipline remains intact |
| P3 | PublicContracts expansion pressure | Successful modular systems always face pressure to widen shared contracts beyond their intended role; now mitigated by a per-module cross-module reference cap (BP-033) |
The baseline owns a large amount of critical shared behavior directly, including:
- the dispatcher and pipeline ordering
- the outbox and inbox posture
- runtime module activation and state coordination
- scaffold and governance validation tooling
Each of those choices is defensible on its own. Together, they create a concentration risk: subtle drift or bugs in one of these seams can affect the entire repository.
These are not leaf concerns. They are central execution and composition concerns. If they degrade, the baseline degrades system-wide.
- dispatcher behavior growth without corresponding simplification elsewhere
- new baseline-owned abstractions being added faster than shared complexity is retired
- weak ownership around shared execution seams
Treat these seams as explicit platform surfaces with named ownership, regression expectations, and active maintenance rather than as incidental implementation details. The chaos / failure-mode integration tests under tests/Integration.Tests/ChaosScenarios/ exercise the real outbox dispatcher, real Postgres driver, and real module-execution gate against injected faults so that "the platform survives a transient database fault and a poison message dead-letters with an observability signal" stops being a claim and becomes a test that fails when the mechanism is broken.
Runtime module activation is one of the repository's most distinctive capabilities. The earlier request-vs-worker split has been converged behind the shared module-execution gate, and that closure is recorded in BASELINE_DECLARATION.md.
The remaining risk is preservation drift: future work could reintroduce divergence across HTTP requests, dispatcher execution, integration-event handling, outbox dispatch, and worker execution even though the intended architecture is now one converged control model.
The module-state system is only as strong as its most weakly aligned execution path. If request execution, integration-event execution, and worker execution evolve differently, the baseline can begin to promise one runtime model while implementing several similar but not identical ones.
- bug fixes landing in one execution path but not another
- disable and re-enable semantics diverging across HTTP, handlers, and workers
- increasing reliance on special-case worker coordination logic
Keep new execution paths on the shared module-execution gate, and treat any future divergence as an explicit architecture change rather than allowing it to creep back in as implementation drift. OutboxResilienceChaosTests.DisablingModuleAfterEnqueueLeavesOutboxRowsParkedAndReEnableDeliversExactlyOnce proves the disable / re-enable cycle leaves no orphaned outbox rows; pair it with OutboxDispatchSkipsDisabledModulesUntilTheyAreReEnabled in IntegrationEventOutboxIntegrationTests for the established baseline.
Status: Resolved. The Identity posture ambiguity was closed by the Phases 1-6 Identity refactor. The Identity module is now explicitly a production-grade implementation built on standard ASP.NET Core Identity, cookie authentication, EF Core, and role-based authorization, behind the existing module boundary. There is no longer a "baseline vs. production" ambiguity for adopters to misread.
Key elements of the resolution:
- ASP.NET Core Identity with cookie authentication is the production answer, not a stub
- role-based authorization (
Admin,User,Machine) replaced the custom permission catalog - session invalidation rides ASP.NET Core Identity's standard security stamp
- the obsolete
IDENTITY_HARDENING_PLAN.mdandIdentityReplaceabilityGuardrailTestsare deleted - seeded admin and seeded machine credentials are Development/Testing only with no override flag; any other environment fails fast at startup
- the
AllowUnsafeSeededCredentialsInNonDevelopmentescape hatch is removed
See adr/ADR-IDENTITY-POSTURE.md for the governing decision.
Teams that still require a different identity provider can replace Identity.Infrastructure with their own implementation, but the baseline no longer asks adopters to do so.
The teaching modules are intentionally realistic, which means they are not perfectly isolated from one another. Cross-module examples help demonstrate the supported seams, but they also make the teaching set interdependent.
This is a good tradeoff for proving the architecture, but it makes adoption and cleanup more complex. It is especially relevant when adopters try to remove only some of the reference modules.
- forks that remove one teaching module and leave broken shared-query or event-consumer examples behind
- confusion about whether the teaching modules are intended to be understood as a set or independently
- rising adoption friction caused by partial cleanup complexity
Continue documenting the teaching modules as a coordinated reference set and keep adoption guidance explicit about removal order, cleanup scope, and reference-module dependencies.
The repository has a coherent persistence posture:
- EF Core is the default relational path
- specialized direct PostgreSQL access is allowed when justified
- both styles remain behind the same migration and durability expectations
The risk is that new adopters may read the live code and conclude that EF-backed and specialized store-backed patterns are equally default.
If that misunderstanding spreads, teams may adopt specialized store patterns too early and bypass the simpler governed path for normal relational features.
- teams reaching for specialized PostgreSQL stores before trying the EF-first path
- module examples being cited without the teaching context around them
- erosion of the baseline's intended EF-first teaching path
Keep Blog and the EF persistence guidance prominent as the default reference path, and continue describing KnowledgeBase and similar store-backed modules as specialized references rather than the default module model.
One of the baseline's core strengths is that documentation, scaffold, tests, rule catalog, ADRs, and CI shape are treated as one governed system.
That is also a maintenance risk. The architecture remains credible only while contributors keep all those assets aligned.
That burden has grown as the baseline added more operational and governance surface: format enforcement, dependency-vulnerability scanning, CodeQL, Dependabot, deployment guidance, migration-rollback guidance, and telemetry-collector wiring all improve confidence, but they widen the set of assets that can drift out of sync.
If the cost of staying aligned becomes too high, contributors will eventually begin to bypass the governed system instead of improving it.
- growing friction around updating the governed document set
- implementation changes landing without the corresponding catalog, ADR, or scaffold updates
- contributors treating governance updates as optional cleanup rather than part of the architectural change
Keep the governed documents concise, continue pruning redundant wording, and treat governance usability as part of architecture quality rather than as secondary documentation work.
The strongest quality wall depends on a non-trivial environment, including PostgreSQL, Docker or Testcontainers, and the frontend test toolchain.
That cost is no longer just about getting the app and tests to run. The supported posture now also includes stronger CI and security checks plus a more realistic deployment and telemetry story, which improves trustworthiness but raises the bar for anyone trying to reproduce the full baseline experience locally.
This is the right tradeoff for parity and realism, but it raises the cost of full local validation. Over time, that can lead teams to rely more on partial validation than the baseline intends.
- frequent local validation that skips the strongest integration paths
- contributors treating CI as the only place where the real architecture is exercised
- new tests increasing setup cost faster than they increase architectural confidence
- operational or telemetry changes being merged with only partial local reproduction because the full posture is too expensive to exercise routinely
Keep CI authoritative, keep local smoke paths fast, and be selective about adding heavyweight validation that does not materially improve architectural confidence.
The frontend architecture is strong, but its strength depends on discipline around:
- generated contracts
- RTK Query wrappers
- manifest-based lazy loading
- shared realtime and telemetry adapters
The remaining auth-related frontend risk is convergence drift, not posture ambiguity. If generated artifacts drift or feature code begins bypassing the shared path, the frontend can gradually move away from the governed role-based and public-route posture even while the repository still describes the stronger shape.
- stale generated contract artifacts
- hand-maintained transport mirrors creeping into feature code
- feature-local HTTP or realtime exceptions accumulating
Keep contract generation friction low, keep frontend rules explicit, and continue protecting the shared path with tests and architecture guardrails.
The current baseline governs .PublicContracts well, but successful modular systems always face pressure to widen their shared surface over time.
If .PublicContracts becomes a convenience bucket for shared DTOs, read models, or cross-module shortcuts, one of the baseline's most important boundaries will erode gradually rather than obviously.
- new shared contracts without a clearly identified consumer need
- browser-facing transport shapes drifting into
.PublicContracts - convenience-sharing arguments replacing explicit public-contract intent
CrossModuleReferenceCapGuardrailTests now enforces a maximum of 3 distinct cross-module PublicContracts references per module. SharedReadFallbackGuardrailTests ensures all shared read adapters declare explicit timeout, freshness, and fallback policies. EfQueryProjectionGuardrailTests ensures query and reader paths prefer .Select() projections over .Include() eager loading.
Keep .PublicContracts narrow, require explicit justification for new shared contracts, and prefer projections or explicit query services over cheap type sharing.
The following architectural choices are currently strong and should be preserved unless there is a deliberate governed change:
ApiHostas composition-only- one public
IApiModuleper module - scaffold-first structural change workflow
- cookie auth plus antiforgery with same-origin default posture
- generated browser contracts
- PostgreSQL-only supported runtime posture
- executable architecture and governance tests
- explicit runtime module activation semantics
- treatment of docs, tests, scaffold, and CI as one governed system
If the repository's maintainers want to reduce risk without weakening the architecture, the most valuable order of attention is:
- keep hardening module-state execution consistency
- treat dispatcher, module-state, outbox or inbox, and scaffold as explicitly owned platform surfaces
- keep governance concise enough that contributors continue using it instead of working around it
- protect the EF-first teaching path and the narrow meaning of
.PublicContracts
The most important point is this:
The baseline's main architectural risks are mostly the costs of deliberate strength, not signs of architectural incoherence.
This repository is strongest where many similar codebases are weakest:
- boundary definition
- governance
- drift resistance
- runtime operational posture
- extension-path clarity
That strength should be preserved by managing its maintenance costs explicitly rather than by relaxing the architecture silently.