From f6e86cf55eb9dd14555ed70d46835c532e49b20a Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Tue, 5 May 2026 09:32:10 +0200 Subject: [PATCH] docs(architecture): rewrite overview for vertical feature architecture --- docs/architecture/overview.md | 85 ++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md index be8646a..1ec2301 100644 --- a/docs/architecture/overview.md +++ b/docs/architecture/overview.md @@ -1,55 +1,58 @@ # Architecture Overview -## Clean Architecture Monorepo +A vertical-feature monorepo. Business capabilities are top-level packages; non-business foundations are `core-*`. -This template implements Uncle Bob's Clean Architecture in a Turborepo + pnpm monorepo. The core principle: **dependencies point inward only**. +## Package map ``` -┌─────────────────────────────────────────────────┐ -│ Frameworks & Drivers (outermost) │ -│ Next.js, TanStack Start, Payload CMS, │ -│ Storybook, PostgreSQL, Docker │ -│ ┌─────────────────────────────────────────┐ │ -│ │ Interface Adapters │ │ -│ │ tRPC routers, Controllers, Presenters │ │ -│ │ ┌─────────────────────────────────┐ │ │ -│ │ │ Application (Use Cases) │ │ │ -│ │ │ Business logic, interfaces │ │ │ -│ │ │ ┌─────────────────────────┐ │ │ │ -│ │ │ │ Entities (innermost) │ │ │ │ -│ │ │ │ Models, Errors, Zod │ │ │ │ -│ │ │ └─────────────────────────┘ │ │ │ -│ │ └─────────────────────────────────┘ │ │ -│ └─────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────┘ +packages/ + # Foundation (no business logic) + core-shared/ Generic primitives — Payload field/block helpers, tRPC init/context, lib utilities + core-cms/ Composition only: assembles feature CMS exports into one Payload config + core-api/ Composition only: aggregates feature tRPC routers into one appRouter + core-trpc/ Frontend tRPC client + per-framework providers (Next.js, TanStack) + core-ui/ Design-system primitives (atoms, molecules, generic organisms, templates) + + # Business capabilities + auth/ Users + sign-in/sign-up/sign-out + session/cookie domain + blog/ Articles collection + publishing flow + media/ Media upload collection (skeleton; expand with optimization, CDN, etc.) + marketing-pages/ Pages collection + SiteSettings global + navigation/ Header global + menu items + + # Tooling + eslint-config/ Shared ESLint flat config + boundary rules + typescript-config/ Shared tsconfig + vitest base ``` -## Package Map +## Data flow ``` -packages/core → Clean architecture (entities, use cases, infra, DI) -packages/api → tRPC routers (calls core controllers) -packages/api-client → Shared React Query hooks + provider -packages/cms-core → Payload CMS config, collections, hooks -packages/cms-client → Dual-mode Payload client (local + HTTP) -packages/ui → Atomic Design + shadcn/ui + Tailwind v4 - -apps/web-next → Next.js 15 reference app -apps/web-tanstack → TanStack Start reference app -apps/cms → Thin Next.js shell for Payload admin -apps/storybook → Centralized Storybook instance +React component + ↓ useQuery(trpc.blog.articleBySlug.queryOptions(...)) ← ui/query.ts (typed tRPC client) +HTTP /api/trpc + ↓ +tRPC procedure ← integrations/api/router.ts + ↓ .input(zod).query(...) +Controller (Zod safeParse) ← interface-adapters/controllers/ + ↓ +Use case ← application/use-cases/ + ↓ container.get(SYMBOL) +Repository implementation ← infrastructure/repositories/ (@injectable) + ↓ getPayload({ config }) +Payload Local API → Postgres ``` -## Data Flow +## Three enforcement layers -``` -UI → useQuery(trpc.content.list) → tRPC Router → Controller → Use Case → Repository → Payload Local API → PostgreSQL -``` +1. **`package.json` deps** — only declare allowed deps +2. **`exports` map** — each package exposes a small public surface (`.`, `./cms`, `./api`, `./di/bind-production`) +3. **ESLint `eslint-plugin-boundaries`** — three tags (`app`, `feature`, `core`); two composition exceptions (`core-api` may import `@repo//api`; `core-cms` may import `@repo//cms`) -## Key Design Decisions +## Per-feature DI containers -- **InversifyJS DI** — symbol-based resolution, documented in di/AGENTS.md -- **Dual-mode CMS client** — Local API for server-side (no HTTP overhead), HTTP for external -- **Atomic Design** — atoms/molecules/organisms/templates, co-located stories -- **tRPC single data path** — all data (including CMS content) flows through tRPC -- **Agent-optimized docs** — AGENTS.md at every level with rules, recipes, tables +Each feature owns its own InversifyJS `Container` + symbol table. No shared symbols, no cross-feature DI coupling. Tests rebind per feature without touching others. Apps call `bindProduction*(config)` per feature at boot to swap the default mock implementations for Payload-backed ones. + +## Spec reference + +`docs/architecture/vertical-feature-spec.md` is the canonical design.