From b455ae801859b4bf0e5f865aa9bbadf81b02e59a Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 22 May 2026 09:53:36 +0200 Subject: [PATCH] docs(architecture): correct package lists and feature-to-feature boundary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- docs/architecture/dependency-flow.md | 16 +++++++++++++--- docs/architecture/overview.md | 16 +++++++++++----- docs/architecture/template-tiers.md | 2 ++ docs/architecture/vertical-feature-spec.md | 6 ++++-- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/docs/architecture/dependency-flow.md b/docs/architecture/dependency-flow.md index 4f600db..3f7d70f 100644 --- a/docs/architecture/dependency-flow.md +++ b/docs/architecture/dependency-flow.md @@ -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/ 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//api (subpath only) core-cms → @repo//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). diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md index 8e23728..a64a8e7 100644 --- a/docs/architecture/overview.md +++ b/docs/architecture/overview.md @@ -19,6 +19,9 @@ packages/ core-realtime/ Socket.IO server + broadcaster + handler registry (ADR-016) core-events/ In-memory + Payload-backed event bus + job queue (ADR-015) core-audit/ DPA-compliant audit logging — sinks, hook factories, eraseSubject (ADR-018) + core-analytics/ Product analytics capture channel (ADR-024) + core-consent/ Consent records + cookie-consent banner (ADR-025) + core-dsr/ Data-subject-rights — export, delete, rectify, restrict (ADR-025) # Business capabilities auth/ Users + sign-in/sign-up/sign-out + session/cookie domain @@ -28,8 +31,9 @@ packages/ navigation/ Header global + menu items # Tooling - core-eslint/ Shared ESLint flat config + boundary rules + core-eslint/ Shared ESLint flat config + conformance rules + boundary rules core-typescript/ Shared tsconfig + vitest base + core-testing/ Factories, contract suites, recording test doubles ``` See `docs/architecture/template-tiers.md` for the must-have/optional split and the scaffold commands. @@ -91,7 +95,7 @@ each feature passes its own constructors in. ## Three enforcement layers 1. **`package.json` deps** — only declare allowed deps -2. **`exports` map** — each package exposes a small public surface (`.`, `./cms`, `./api`, `./di/bind-production`) +2. **`exports` map** — each feature exposes a small public surface (`.`, `./ui`, `./cms`, `./api`, `./di/bind-production`, `./di/bind-dev-seed`) 3. **Two parallel automated checks**: - **ESLint `eslint-plugin-boundaries`** (lint-time) — enforces boundary rules at linting - **Turborepo `boundaries`** (build-graph time) — validates entire workspace dependency graph, including transitive reaches @@ -104,9 +108,9 @@ The workspace is organized into five mutually exclusive tags: - **app** (4 packages): `apps/cms`, `apps/web-next`, `apps/web-tanstack`, `apps/storybook` - **core-composition** (2 must-have): `packages/core-api`, `packages/core-cms`. Plus `packages/core-trpc` when scaffolded via `pnpm turbo gen core-package trpc` (optional). -- **core** (1 must-have): `packages/core-shared`. Plus `packages/core-ui`, `packages/core-realtime`, `packages/core-events`, `packages/core-audit` when scaffolded via `pnpm turbo gen core-package ` (optional). +- **core** (1 must-have): `packages/core-shared`. Plus `core-ui`, `core-realtime`, `core-events`, `core-audit`, `core-analytics`, `core-consent`, `core-dsr` when scaffolded via `pnpm turbo gen core-package ` (optional). - **feature** (5 packages): `packages/auth`, `packages/blog`, `packages/media`, `packages/marketing-pages`, `packages/navigation` -- **tooling** (2 packages): `packages/core-eslint`, `packages/core-typescript` +- **tooling** (3 packages): `packages/core-eslint`, `packages/core-typescript`, `packages/core-testing` See `docs/architecture/template-tiers.md` for the must-have/optional split and the scaffold commands. @@ -117,9 +121,11 @@ See `docs/architecture/template-tiers.md` for the must-have/optional split and t | app | app, core, core-composition, feature, tooling | | core-composition | core, core-composition, feature, tooling | | core | core, core-composition, tooling | -| feature | core, tooling | +| feature | core, feature, tooling | | tooling | tooling | +A feature may import another feature's **public exports** — its `@repo/` contract barrel (types, errors, schemas, event contracts). It must not reach another feature's internals (the `exports` map seals those), and cross-feature _behaviour_ still flows through `IEventBus` — a feature never imports and invokes another feature's use cases directly. + **Composition exceptions:** - `core-api` may import from `@repo//api` subpath exports only diff --git a/docs/architecture/template-tiers.md b/docs/architecture/template-tiers.md index 103df01..7dbbdf3 100644 --- a/docs/architecture/template-tiers.md +++ b/docs/architecture/template-tiers.md @@ -23,6 +23,8 @@ Plus all 5 feature packages: auth, blog, marketing-pages, navigation, media. | core-ui | `pnpm turbo gen core-package ui` | (none) | (none) | | core-audit | `pnpm turbo gen core-package audit` | ADR-018 | docs/guides/audit-and-compliance.md | | core-analytics | `pnpm turbo gen core-package analytics` | ADR-024 | docs/guides/analytics.md | +| core-consent | `pnpm turbo gen core-package consent` | ADR-025 | docs/guides/consent.md | +| core-dsr | `pnpm turbo gen core-package dsr` | ADR-025 | docs/guides/dsr.md | ## Why optional diff --git a/docs/architecture/vertical-feature-spec.md b/docs/architecture/vertical-feature-spec.md index c9e5ce0..ccca293 100644 --- a/docs/architecture/vertical-feature-spec.md +++ b/docs/architecture/vertical-feature-spec.md @@ -145,7 +145,7 @@ repo/ pnpm-workspace.yaml tsconfig.base.json turbo.json ``` -**Package count:** 3 apps + 5 core + 5 feature + 2 tooling = **15 packages** (was 12). +**Package count:** the `packages/` workspace holds **19 packages** — 3 must-have cores (`core-shared`, `core-cms`, `core-api`), 8 optional cores scaffolded on demand (`core-ui`, `core-events`, `core-realtime`, `core-trpc`, `core-audit`, `core-analytics`, `core-consent`, `core-dsr`), 3 tooling packages (`core-eslint`, `core-typescript`, `core-testing`), and 5 feature packages — plus 4 apps. A minimal project that scaffolds none of the optional cores ships 11 packages. --- @@ -444,9 +444,11 @@ Note: `core-trpc` is `core-composition` (not plain `core`) because it transitive | app | app, core, core-composition, feature, tooling | | core-composition | core, core-composition, feature, tooling | | core | core, core-composition, tooling | -| feature | core, tooling | +| feature | core, feature, tooling | | tooling | tooling | +A feature may import another feature's **public exports** — its `@repo/` 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. + ### 9.3 Composition exceptions - `core-cms` may import `@repo//cms` subpath exports only.