Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion assets/data/search-index.json

Large diffs are not rendered by default.

1,011 changes: 606 additions & 405 deletions docs-src/onboarding/00-dependency-manifest.md

Large diffs are not rendered by default.

356 changes: 203 additions & 153 deletions docs-src/onboarding/00-group-taxonomy.md

Large diffs are not rendered by default.

24 changes: 12 additions & 12 deletions docs-src/onboarding/00-index.md

Large diffs are not rendered by default.

1,143 changes: 676 additions & 467 deletions docs-src/onboarding/00-inventory.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions docs-src/onboarding/00-primer.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,10 @@ the full set, for orientation:
| 125 | Parameterized SQL only: hand-written SQL has exactly one door, `IRawSqlQueryExecutor`, whose two entry points accept `FormattableString` and nothing else (a concatenated statement does not compile), `EFRawSqlQueryExecutor` names the relational-only limit, and `RawSqlConventionTestsBase` bans the four `*Raw` EF members in module code as a fitness gate | [g07](group-07-persistence-ef-core.md)/[g28](group-28-testing-infrastructure.md) |
| 126 | Event sourcing is not adopted: aggregates stay current-state EF rows that raise domain events for notification, carry audit fields and soft-delete, so no repository gains an event store, per-aggregate stream, projection layer or rehydration path | [g02](group-02-domain-building-blocks.md)/[g04](group-04-events-outbox.md) |
| 127 | The actor model is not adopted: per-entity state stays in the owning module's database behind optimistic concurrency, read pressure stays on the caching tiers, and live fan-out stays on the notification hub, with no actor runtime as a hosting layer | [g07](group-07-persistence-ef-core.md)/[g09](group-09-caching.md)/[g10](group-10-notifications.md) |
| 128 | Time as an input: Domain and Application code never reads the ambient clock (`DateTime.UtcNow`, `Now`, `Today`, `DateTimeOffset.UtcNow`, `Now`); handlers inject `TimeProvider` and pass the instant into the domain, `BaseDomainEvent.DateOccurred` is the single exemption, and an IL-scanning fitness base (`ClockReadTestsBase`) enforces it | [g28](group-28-testing-infrastructure.md) |
| 129 | The tactical aggregate contract: entities are sealed classes over `BaseEntity` / `AuditableBaseEntity` / `AuditableAggregateRootEntity`, built only through a `Create` returning `Result<T>`, with private setters, child access through the root and invariants in static `*Invariants` classes; `EntityConventionTestsBase` gates construction, encapsulation and placement | [g28](group-28-testing-infrastructure.md) |
| 130 | Per-engine data source strategy: one internal `IDataSourceEngine` per engine (SQL Server, PostgreSQL, SQLite, Cosmos DB) behind the static `DataSourceEngines.For(DataSource)` registry, so call sites read engine facts instead of branching on the `DataSource` enum, with `DataSourceBranchingFitnessTests` as the gate | [g07](group-07-persistence-ef-core.md)/[g28](group-28-testing-infrastructure.md) |
| 131 | Same-origin API proxy (backend-for-frontend): an opt-in `AddCommonSameOriginApiProxy` plus `MapCommonSameOriginApiProxy` serves `/api/**` on the UI host's own origin and forwards to the gateway through YARP, attaching the bearer from the HttpOnly session cookie so no browser script ever holds an API token | [g15](group-15-common-ui-framework.md)/[g08](group-08-auth.md)/[g12](group-12-api-hosting-mapping.md) |

The canonical index for the full set can be found at <https://ivanball.github.io/docs/adr/>.

Expand Down
52 changes: 51 additions & 1 deletion docs-src/onboarding/99-coverage-audit.md

Large diffs are not rendered by default.

30 changes: 23 additions & 7 deletions docs-src/onboarding/CONCEPT-MAPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,12 @@ One sealed context class per engine (`SQLServerDbContext`, `PostgreSQLDbContext`
`CosmosDbContext`) over the abstract `ApplicationDbContext`, **one instance per database**. Each
entity is engine-agnostic; a single `[UseDataSource(engine)]` attribute on its config class picks
SQL Server, PostgreSQL ([ADR-113](https://ivanball.github.io/docs/adr/113-postgresql-as-a-first-class-engine.html)), Cosmos, or SQLite. Cross-source relationships auto-degrade; the outbox
is the cross-source consistency mechanism. Each service self-applies its EF migrations at boot
is the cross-source consistency mechanism. Engine facts (relational or not, migrations, row-version
strategy, explicit-key inserts) come from one internal `IDataSourceEngine` per engine, looked up
through the static `DataSourceEngines.For(DataSource)` registry, so call sites read capabilities
instead of branching on the engine
([ADR-130](https://ivanball.github.io/docs/adr/130-per-engine-data-source-strategy.html)). Each
service self-applies its EF migrations at boot
([ADR-030](https://ivanball.github.io/docs/adr/030-startup-sole-migrator.html)).

```mermaid
Expand All @@ -487,10 +492,13 @@ flowchart TD

subgraph SHIMS["Engine shim base classes (one token = one engine)"]
SQL["…SQLServer&lt;T,Id&gt;<br/>(all prod configs today)"]
PG["...PostgreSQL&lt;T,Id&gt;<br/>(first-class engine, ADR-113, staged)"]
COS["…Cosmos&lt;T,Id&gt;<br/>(shipped + tested, staged)"]
LITE["…Sqlite&lt;T,Id&gt;<br/>(fast integration tests, staged)"]
end

ENG["DataSourceEngines.For: one internal IDataSourceEngine<br/>per engine, capabilities not branches (ADR-130)"]

subgraph DBS["Database-per-service (ADR-006)"]
CTX1["SQLServerDbContext instance: Conference DB + outbox"]
CTX2["SQLServerDbContext instance: Engagement DB + outbox"]
Expand All @@ -502,13 +510,15 @@ flowchart TD
MIG["Startup sole-migrator: DatabaseInitStrategy=Migrate (ADR-030)"]

ENTITY --> CFG --> ATTR --> REG
REG --> SQL & COS & LITE
REG --> SQL & PG & COS & LITE
SQL --> DBS
DBS -->|"FK dropped across sources → batch loaders"| XSPEC
MIG -.-> DBS
ENG -.->|"engine facts"| REG
ENG -.-> MIG

classDef p fill:#fef7e0,stroke:#f9ab00,color:#111
class ENTITY,CFG,ATTR,REG,XSPEC,MIG p
class ENTITY,CFG,ATTR,REG,XSPEC,MIG,ENG p
```

---
Expand All @@ -524,11 +534,11 @@ flowchart TD
subgraph AUTHN["Authentication"]
JWT["JWT bearer validation"]
JWKS["JWKS discovery + fallback fetch (ADR-004)"]
COOKIE["HttpOnly session cookie + non-validating SSR scheme (ADR-022)"]
COOKIE["HttpOnly session cookie + non-validating SSR scheme (ADR-022);<br/>ISessionCookieStore writes them server-side for the proxy (ADR-131)"]
HASH["PBKDF2-HMAC-SHA512, 32-byte salt, 600k iters:<br/>one algorithm, no legacy branch (ADR-102)"]
LOGIN["ILoginProtectionService: lockout + per-IP cap (ADR-029)"]
EXT["External OAuth: Google / GitHub behind a short-lived<br/>ExternalLogin cookie + single-use code (ADR-036/043)"]
ROT["AuthenticationServiceBase: 15-min access token +<br/>one refresh session per device, SHA-256 at rest,<br/>rotated with reuse detection (ADR-097);<br/>ITokenRefresher per head (ADR-051)"]
ROT["AuthenticationServiceBase decides who is signed in;<br/>IAuthSessionIssuer: 15-min access token +<br/>one refresh session per device, SHA-256 at rest,<br/>rotated with reuse detection (ADR-097);<br/>ITokenRefresher per head (ADR-051)"]
RESET["Forgot/reset password: cache-backed single-use<br/>hashed token + email leg (ADR-091)"]
end

Expand Down Expand Up @@ -612,7 +622,11 @@ time ([ADR-042](https://ivanball.github.io/docs/adr/042-device-capability-abstra
G26). Culture cookie + `IStringLocalizer` drive i18n ([ADR-027](https://ivanball.github.io/docs/adr/027-multi-locale-i18n.html)); `ThemeService`
drives day/dark ([ADR-028](https://ivanball.github.io/docs/adr/028-dark-theme-mode.html)). Each
module's UI plugs into one shared shell by contributing navigation and routes through `IUIModule`
([ADR-067](https://ivanball.github.io/docs/adr/067-ui-module-shell-composition.html)).
([ADR-067](https://ivanball.github.io/docs/adr/067-ui-module-shell-composition.html)). A Blazor
Web host can opt in to the same-origin API proxy in `MMCA.Common.UI.Web`, which serves `/api/**` on
the UI host's own origin and forwards to the gateway through YARP with the bearer read from the
HttpOnly session cookie
([ADR-131](https://ivanball.github.io/docs/adr/131-same-origin-api-proxy.html)).

```mermaid
flowchart TD
Expand All @@ -638,6 +652,7 @@ flowchart TD
CSP["Security headers + pluggable CSP (ADR-023)"]
RM["Render mode: InteractiveAuto on the root router,<br/>prerender on (ADR-056)"]
CAP["Device capability contracts in MMCA.Common.UI:<br/>browser-JS, inert fallback and MAUI-native adapters<br/>chosen per host at DI time (ADR-042, G26)"]
BFF["Opt-in same-origin API proxy, MMCA.Common.UI.Web:<br/>/api/** forwarded to the gateway via YARP,<br/>bearer from the HttpOnly cookie (ADR-131, G15)"]

PAGE --> COMMONUI
COMMONUI --> WEB
Expand All @@ -652,9 +667,10 @@ flowchart TD
COMMONUI --> CAP
CAP -.->|"browser + fallback adapters"| WEB
CAP -.->|"MMCA.Common.UI.Maui adapters"| MAUI
BFF -.->|"browser API calls"| WEB

classDef u fill:#f3e8fd,stroke:#a142f4,color:#111
class PAGE,COMMONUI,SHELL,WEB,MAUI,CAP u
class PAGE,COMMONUI,SHELL,WEB,MAUI,CAP,BFF u
```

---
Expand Down
Loading
Loading