docs(architecture): correct package lists and feature-to-feature boundary

Align the architecture docs with the current repo:

- Boundary matrix: feature may depend on core, feature, tooling — a
  feature may import another feature's public exports. overview.md,
  dependency-flow.md, and vertical-feature-spec.md all said the stale
  `feature -> core, tooling`.
- Optional-core lists completed with core-analytics, core-consent,
  core-dsr; tooling list completed with core-testing.
- Package count corrected to the accurate 19-package breakdown.
- BindContext table gained analytics, consentFactory, rateLimit.

Deeper drift (the HTML explainers, vertical-feature-spec §5/§9.5/§11)
is tracked in the local .tmp/ working note, not yet addressed.
This commit is contained in:
2026-05-22 09:53:36 +02:00
parent 80835e4fea
commit b455ae8018
4 changed files with 30 additions and 10 deletions

View File

@@ -23,11 +23,17 @@
Boundary rules (enforced by ESLint + Turborepo boundaries):
app → app, core, core-composition, feature, tooling
feature → core, tooling
feature → core, feature, tooling
core → core, core-composition, tooling
core-composition → core, core-composition, feature, tooling
tooling → tooling
feature → feature: a feature may import another feature's PUBLIC exports
(its @repo/<feature> contract barrel — types, errors, schemas, event
contracts). It must NOT reach another feature's internals, and
cross-feature behaviour still flows through IEventBus — never a direct
use-case call.
Composition exceptions:
core-api → @repo/<feature>/api (subpath only)
core-cms → @repo/<feature>/cms (subpath only)
@@ -51,6 +57,7 @@ import { articleBySlugQuery } from "@repo/blog/ui"; // queries
// in packages/blog
import { slugifyIfMissing } from "@repo/core-shared/payload";
import { userSignedUpEvent } from "@repo/auth"; // ✓ another feature's public contract
// in packages/core-api
import { blogRouter } from "@repo/blog/api"; // composition exception
@@ -63,8 +70,8 @@ import { articles } from "@repo/blog/cms"; // composition exception
Disallowed:
```ts
// in packages/blog (cross-feature)
import { Article } from "@repo/marketing-pages"; // ❌ feature → feature
// in packages/blog (reaching another feature's internals)
import { signInUseCase } from "@repo/auth/src/application/use-cases/sign-in.use-case"; // ❌ not a public export
// in packages/blog (deep import past public exports)
import { articles } from "@repo/blog/src/integrations/cms/collections/articles"; // ❌ no-private
@@ -158,6 +165,9 @@ apps/web-next/src/server/bind-production.ts (bindAll)
| `realtime` | `RealtimeBroadcasterProtocol?` | optional | `IRealtimeBroadcaster` at the aggregator |
| `realtimeRegistry` | `RealtimeRegistryProtocol?` | optional | `IRealtimeHandlerRegistry` at the aggregator |
| `auditLog` | `AuditLogProtocol?` | optional | `IAuditLog` at the aggregator; pre-wrapped in `TraceIdEnrichingAuditLog` so callers don't supply `correlationId` (ADR-018) |
| `analytics` | `AnalyticsProtocol?` | optional | `IAnalytics` product-analytics channel at the aggregator (ADR-024) |
| `consentFactory` | `ConsentFactoryProtocol?` | optional | builds a per-subject consent checker; present when `core-consent` is wired (ADR-025) |
| `rateLimit` | `IRateLimit?` | optional | per-use-case rate-limit budgets; present when rate limiting is wired (ADR-025) |
Feature binders destructure `ctx` and use optional fields with `?.` or cast to the full interface when the feature unconditionally requires them (e.g. `bus as IEventBus` when a use case always needs the event bus).