# Component Dependencies Dependency matrix, communication patterns and data flow. --- ## Dependency Diagram ```mermaid graph TD subgraph core["SlpModularCms.Core"] sechdr["C-01 SecurityHeadersMiddleware"] secopt["C-02 SecurityHeadersOptions"] csp["C-03 CspPolicyBuilder"] health["C-04 Health checks"] dp["C-05 CmsDataProtection"] appdb["C-06 ApplicationDbContext
plus keys table"] migrate["C-07 Migration runner"] logging["C-08 CmsLogging"] sentry["C-09 CmsSentry"] end subgraph apihost["SlpModularCms.Api"] statics["C-10 Static mounts"] prog["C-16 Host composition"] end subgraph slavehost["SlpModularCms.Api.Slave"] progslave["C-16 Host composition"] end subgraph modules["Modules"] avail["C-13 AvailabilityMiddleware"] end subgraph fe["frontend"] cfg["C-14 Config"] feobs["C-15 Sentry plus Umami"] end subgraph wf[".gitea/workflows"] ci["C-12 CI workflow"] scp["C-11 deploy-scp"] end sechdr --> secopt sechdr --> csp csp --> secopt dp --> appdb migrate --> appdb sentry --> logging prog --> sechdr prog --> health prog --> dp prog --> migrate prog --> logging prog --> sentry prog --> statics progslave --> sechdr progslave --> health progslave --> dp progslave --> migrate progslave --> logging progslave --> sentry avail --> dp cfg --> feobs ci --> scp ci --> fe ci --> apihost classDef corelayer fill:#fbd38d,stroke:#c05621,stroke-width:1px,color:#000; classDef host fill:#90cdf4,stroke:#2b6cb0,stroke-width:1px,color:#000; classDef module fill:#9ae6b4,stroke:#2f855a,stroke-width:1px,color:#000; classDef frontend fill:#d6bcfa,stroke:#6b46c1,stroke-width:1px,color:#000; classDef workflow fill:#fbb6ce,stroke:#b83280,stroke-width:1px,color:#000; class sechdr,secopt,csp,health,dp,appdb,migrate,logging,sentry corelayer; class statics,prog,progslave host; class avail module; class cfg,feobs frontend; class ci,scp workflow; ``` Text alternative: all new cross-cutting components live in Core and are consumed by both host projects; only the static mounts are exclusive to the Api host; the Availability middleware depends on Core's Data Protection; and the CI workflow orchestrates the frontend, the Api host and the deploy workflow. --- ## Dependency Matrix | Component | Depends on | Depended on by | Coupling | |---|---|---|---| | C-01 `SecurityHeadersMiddleware` | C-02, C-03 | C-16 (both hosts) | Compile | | C-02 `SecurityHeadersOptions` | Configuration | C-01, C-03 | Configuration binding | | C-03 `CspPolicyBuilder` | C-02 | C-01 | Compile | | C-04 Health checks | — | C-16 (both hosts) | Compile | | C-05 `CmsDataProtection` | C-06 | C-16, and indirectly C-13 | Compile | | C-06 `ApplicationDbContext` keys table | EF Core, SQL Server | C-05, C-07 | Compile + schema | | C-07 Migration runner | C-06 | C-16 (both hosts) | Compile | | C-08 `CmsLogging` | — | C-09, C-16 | Compile | | C-09 `CmsSentry` | C-08, configuration | C-16 | Compile | | C-10 Static mounts | Filesystem layout | C-16 (`Api` only) | Runtime (filesystem) | | C-11 `deploy-scp.yaml` | Host over SSH | C-12 | Workflow call | | C-12 CI workflow | C-11, both build outputs | — | Workflow | | C-13 `AvailabilityMiddleware` | C-05 (key ring), validated principal | Both hosts, via module discovery | Compile + runtime | | C-14 Frontend config | Vite build variables | C-15, `ApiClient` | Build-time | | C-15 Frontend observability | C-14, Vite build variables | — | Build-time | | C-16 Host composition | C-01, C-04, C-05, C-07, C-08, C-09, C-10 | — | Compile | --- ## Communication Patterns ### In-process (the majority) Everything in `Core` is consumed by the hosts through **DI registration and middleware composition**. There are no new service-to-service calls, no new queues and no new network hops inside the application. This is deliberate: the feature adds cross-cutting behaviour, not new interactions. ### Configuration-driven `C-02` binds the `SecurityHeaders` section; `C-09` reads a Sentry DSN; `C-05` reads Data Protection settings. All follow the existing Options pattern. **Configuration errors must surface at startup, not per request** — an unknown CSP policy name fails the process rather than silently degrading, which given fail-fast startup (Q8 = A) means a misconfigured deployment goes visibly red instead of quietly serving without protection. ### Filesystem-coupled (the fragile one) `C-10` depends on a directory layout that no code creates: | Path | Owner | Created by | |---|---|---| | `wwwroot/admin/` | This repository | `dotnet publish` (`BuildAndCopyAdminFrontend` target) | | `wwwroot/web/` | **A separate website workspace** | That workspace's own deployment; persists across releases (ASM-01) | This is the feature's most fragile coupling, because it is enforced by convention rather than by the type system. Two consequences: 1. `C-10` must **tolerate a missing `wwwroot/web/` at startup** — a fresh deployment has none until a website workspace deploys into it, and the CMS must still start and serve `/admin` and `/api/v1`. 2. The deployment (S-05) must link the persistent `wwwroot/web/` into each new release directory. Getting this wrong destroys the customer's website — the highest-severity risk in the feature (NFR-02). ### Build-time coupling (frontend) `C-14` and `C-15` read Vite variables baked in at build time, which is precisely why two artifacts are produced (D-15). The backend and frontend are additionally coupled in the *reverse* direction by the publish target, which runs `pnpm install` and `pnpm build` — making Node and pnpm prerequisites of `dotnet publish` and a required step in the CI workflow's toolchain setup. ### Cross-instance (unchanged, but newly protected) Master↔slave communication is untouched functionally. What changes is its durability: with `C-05` and `C-06`, the encrypted API keys underpinning that trust survive a redeploy. Previously an atomic release switch would have discarded the file-based key ring and broken the protocol silently. --- ## Data Flow ### Request flow with the new components ```mermaid sequenceDiagram box rgba(246,224,94,0.4) Client participant V as Visitor or admin end box rgba(144,205,244,0.4) Pipeline participant E as Exception handler participant S as Security headers participant F as Static files participant A as Availability gate participant H as Health endpoint end V->>E: HTTP request E->>S: continue S->>S: resolve CSP policy for path S->>F: continue with OnStarting callback alt file exists in web or admin mount F-->>V: file, headers applied at response start else no matching file F->>A: continue alt path is /health or another bypass prefix A->>H: continue H-->>V: 200 Healthy or 503 Unhealthy else instance disabled A-->>V: 503 ProblemDetails else instance available A-->>V: routed to controllers or SPA fallback end end ``` Text alternative: security headers register a response-start callback before static files short-circuit, so both static and dynamic responses carry them; `/health` passes the availability gate via the bypass list, while other paths are blocked with a 503 when the instance is disabled. ### Startup flow ```mermaid graph TD start["Process starts"] log["Configure logging and Sentry"] disc["Discover modules"] svc["Register services
including Data Protection"] build["Build application"] mig["Migrate ApplicationDbContext"] modmig["Module contexts migrate
during UseModules"] serve["Accept traffic;
/health answers"] dead["Process does not start;
/health silent, UptimeRobot red"] start --> log log --> disc disc --> svc svc --> build build --> mig mig -->|success| modmig mig -->|failure| dead modmig --> serve classDef normal fill:#9ae6b4,stroke:#2f855a,stroke-width:1px,color:#000; classDef decision fill:#90cdf4,stroke:#2b6cb0,stroke-width:1px,color:#000; classDef bad fill:#fbb6ce,stroke:#b83280,stroke-width:1px,color:#000; class start,log,disc,svc,build,modmig,serve normal; class mig decision; class dead bad; ``` Text alternative: logging and Sentry are configured first so any later startup failure is captured; Core migrations run before module migrations, and a migration failure stops the process entirely rather than serving a broken application. ### Deployment data flow ```mermaid graph LR art["Build artifact"] backup[("Database backup
production only")] newrel["New release directory"] persist[("Persistent wwwroot/web
customer website")] active["Active release symlink"] prev["Retained previous release"] art --> newrel backup -.->|before any change| newrel persist -->|linked into| newrel newrel --> active active -.->|previous becomes| prev prev -.->|rollback| active classDef artifact fill:#9ae6b4,stroke:#2f855a,stroke-width:1px,color:#000; classDef store fill:#d6bcfa,stroke:#6b46c1,stroke-width:1px,color:#000; classDef link fill:#90cdf4,stroke:#2b6cb0,stroke-width:1px,color:#000; class art,newrel artifact; class backup,persist store; class active,prev link; ``` Text alternative: the build artifact populates a new release directory into which the persistent customer website is linked; the active pointer then switches atomically, and the previous release is retained so a rollback is a pointer switch rather than a rebuild. --- ## Coupling Concerns | Concern | Assessment | |---|---| | **`Core` is inherited by both hosts** | Intended (Q1 = A, Q2 = A). Every `Core` change reaches `SlpModularCms.Api.Slave`, which has no test project — so it is verified at Build and Test by actually starting it. The Slave is a reference instance, not a throwaway. | | **Duplicate `AddDataProtection()` in two modules** | **A real conflict**, documented in `services.md` S-01. Module registration runs *after* host registration, so the modules' calls would override the persistent key store and make FR-12 a no-op that looks implemented. Both must be removed. | | **`AvailabilityMiddleware` runs before `UseAuthentication()`** | Constrains how FR-24 can be implemented — either validate the token in the middleware, or move authentication earlier. Resolved in Functional Design for Unit 2. | | **Filesystem convention for `wwwroot/web/`** | The weakest link: not enforceable in code, and getting it wrong is destructive. Mitigated by design (persistent path outside the release directory) and by documentation (FR-09), but it stays a convention. | | **Static files short-circuit the pipeline** | Dictates that security headers precede them. Any future middleware that must see all responses faces the same constraint — worth remembering rather than rediscovering. | | **Build-time frontend configuration** | Forces two build artifacts. Accepted (D-15), with runtime configuration recorded as a possible later improvement. |