From 590543fb65df3257b6e6dae52b381ccba95c8eaa Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Tue, 5 May 2026 09:32:25 +0200 Subject: [PATCH] docs(architecture): rewrite dependency-flow for vertical features + boundary rules --- docs/architecture/dependency-flow.md | 92 +++++++++++++++++----------- 1 file changed, 56 insertions(+), 36 deletions(-) diff --git a/docs/architecture/dependency-flow.md b/docs/architecture/dependency-flow.md index 3343269..db8f887 100644 --- a/docs/architecture/dependency-flow.md +++ b/docs/architecture/dependency-flow.md @@ -1,50 +1,70 @@ # Dependency Flow -## Package Dependencies (one direction only) - ``` -apps/web-next → @repo/api-client, @repo/ui - Startup: @repo/cms-core (config) + @repo/cms-client (init local) -apps/web-tanstack → @repo/api-client, @repo/ui - Startup: @repo/cms-core (config) + @repo/cms-client (init local) -apps/cms → @repo/cms-core, payload, next -apps/storybook → @repo/ui + +-------------+ +-----------------+ +-----------+ + | apps/web- | | apps/web- | | apps/cms | + | next | | tanstack | | | + +------+------+ +--------+--------+ +-----+-----+ + | | | + +------------------+--------------+ | | + | | | | | + +----v-----+ +-----v------+ +-----v----v---+ +-------v------+ + | core-api | | core-trpc | | feature | | core-cms | + | | | | | packages | | | + +-----+----+ +-----+------+ +------+-------+ +-------+------+ + | | | | + | | | | + +--+-------+------+---------------+----+ +-------------+ + | | | | + +----v---+ +-v---------+ +-------v---v---+ + | core- | | core-ui | | core-shared | + | shared | | | | | + +--------+ +-----------+ +----------------+ -@repo/api-client → @repo/api (router types only) -@repo/api → @repo/core/interface-adapters (controllers) -@repo/cms-core → @repo/core/application (use cases for hooks), payload (types) -@repo/cms-client → (standalone — receives Payload instance, doesn't import it) -@repo/ui → (standalone — tailwind, shadcn) + Boundary rules (enforced by eslint-plugin-boundaries): + app → app, core, feature, core-composition (any) + feature → core (any), but NOT other features, NOT app + core → core, but NOT feature, NOT app + core-composition → core, feature subpath exports only (`/cms`, `/api`) + core-api → @repo//api + core-cms → @repo//cms ``` -## Core Internal Dependencies +## Concrete examples -``` -core/entities → (standalone — zero deps) -core/application → core/entities only -core/interface-adapters → core/application, core/entities -core/infrastructure → core/application, core/entities, @repo/cms-client, external libs -core/di → all internal layers +Allowed: +```ts +// in apps/web-next +import { appRouter } from "@repo/core-api"; +import { NextTrpcProvider } from "@repo/core-trpc/next"; +import { bindProductionBlog } from "@repo/blog/di/bind-production"; + +// in packages/blog +import { slugifyIfMissing } from "@repo/core-shared/payload"; + +// in packages/core-api +import { blogRouter } from "@repo/blog/api"; // composition exception +import { router } from "@repo/core-shared/trpc/init"; // core → core fine + +// in packages/core-cms +import { articles } from "@repo/blog/cms"; // composition exception ``` -## Circular Dependency Prevention — HARD RULES +Disallowed: +```ts +// in packages/blog (cross-feature) +import { Article } from "@repo/marketing-pages"; // ❌ feature → feature -- **NEVER:** packages/core → apps/* -- **NEVER:** apps/cms → packages/core/infrastructure -- **NEVER:** packages/cms-client → apps/cms or packages/core or packages/cms-core -- **NEVER:** packages/cms-core → packages/cms-client -- **NEVER:** core/entities → anything -- **NEVER:** core/application → core/infrastructure +// in packages/blog (deep import past public exports) +import { articles } from "@repo/blog/src/integrations/cms/collections/articles"; // ❌ no-private -## Why These Rules Exist +// in packages/core-shared +import { blogRouter } from "@repo/blog/api"; // ❌ core → feature -The Payload CMS integration creates a potential circular dependency: -``` -apps/cms hooks → @repo/core/application (use cases) -@repo/core/infrastructure → @repo/cms-client → Payload API +// in packages/core-trpc +import { someBlogThing } from "@repo/blog"; // ❌ core → feature (only core-api/core-cms have exception) ``` -This is resolved by: -1. `cms-client` is standalone — receives Payload instance via injection, never imports it -2. `cms-core` hooks only import from `core/application`, never `core/infrastructure` -3. App startup code (not a shared package) wires Payload instance into cms-client +## Three-layer enforcement + +ESLint catches accidental cross-package imports at lint time. The `package.json` `exports` map blocks deep imports at module-resolution time. Workspace `dependencies` declarations make the package graph itself the source of truth — if you didn't declare it, you can't import it.