Add clean architecture monorepo template design spec
Comprehensive design specification covering: Turborepo+pnpm monorepo, clean architecture core (InversifyJS DI), tRPC+TanStack Query shared API layer, Payload CMS with dual-mode client (local+HTTP), Atomic Design UI with shadcn/ui+Storybook, MCP agent infrastructure, and 4-tier documentation architecture with ~22 AGENTS.md files.
This commit is contained in:
@@ -0,0 +1,557 @@
|
||||
# Clean Architecture Monorepo Template — Design Specification
|
||||
|
||||
## Overview
|
||||
|
||||
A general-purpose monorepo application template based on Clean Architecture (Uncle Bob / Lazar Nikolov), designed to serve as the foundation for all future web applications. The template supports multiple frontend frameworks, integrates Payload CMS, and includes comprehensive agent-optimized documentation so AI coding agents can navigate, understand, and extend the codebase autonomously.
|
||||
|
||||
**References:**
|
||||
- [Clean Architecture (Uncle Bob)](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
|
||||
- [Clean Architecture with Next.js (Lazar Nikolov)](https://medium.com/@heinhtoo/clean-architecture-with-next-js-insights-from-lazar-nikolov-developer-advocate-at-sentry-abe1cb4c7ef3)
|
||||
- [Reference repo](https://github.com/heinhtoo/nextjs-clean-architecture-template)
|
||||
- [Turborepo + shadcn/ui reference](https://github.com/dan5py/turborepo-shadcn-ui)
|
||||
|
||||
---
|
||||
|
||||
## 1. Monorepo Infrastructure
|
||||
|
||||
| Concern | Choice |
|
||||
|---|---|
|
||||
| Orchestrator | Turborepo |
|
||||
| Package manager | pnpm workspaces |
|
||||
| Deployment | Docker-first (docker-compose for local dev) |
|
||||
|
||||
### Monorepo Structure
|
||||
|
||||
```
|
||||
template/
|
||||
├── apps/
|
||||
│ ├── web-next/ # Next.js reference app
|
||||
│ ├── web-tanstack/ # TanStack Start reference app
|
||||
│ ├── cms/ # Thin Next.js shell for Payload admin
|
||||
│ └── storybook/ # Centralized Storybook instance
|
||||
│
|
||||
├── packages/
|
||||
│ ├── core/ # Clean architecture core
|
||||
│ ├── api/ # tRPC router definitions
|
||||
│ ├── api-client/ # Shared React Query hooks
|
||||
│ ├── cms-core/ # Payload config + collections + hooks
|
||||
│ ├── cms-client/ # Dual-mode Payload client (local + HTTP)
|
||||
│ ├── ui/ # shadcn/ui + Atomic Design components
|
||||
│ ├── eslint-config/ # Shared linting rules
|
||||
│ └── typescript-config/ # Shared TS configs
|
||||
│
|
||||
├── tests/
|
||||
│ ├── unit/ # Vitest (mirrors core structure)
|
||||
│ ├── integration/ # Vitest (real DB via test containers)
|
||||
│ └── e2e/ # Playwright (browser tests)
|
||||
│
|
||||
├── docs/ # Architecture guides, ADRs, diagrams
|
||||
├── .mcp.json # MCP server configuration
|
||||
├── docker-compose.yml # Postgres + Payload + all apps + Storybook
|
||||
├── turbo.json # Turborepo task pipeline
|
||||
├── pnpm-workspace.yaml # Workspace config
|
||||
├── CLAUDE.md # Claude Code entry point
|
||||
└── AGENTS.md # Cross-agent root instructions
|
||||
```
|
||||
|
||||
### Framework Support
|
||||
|
||||
Both Next.js and TanStack Start (with TanStack Router and TanStack Query) coexist as first-class reference apps. Both share the same core packages (`@repo/core`, `@repo/api-client`, `@repo/ui`), demonstrating that the clean architecture works across any frontend framework. Projects can use one or both.
|
||||
|
||||
---
|
||||
|
||||
## 2. packages/core — Clean Architecture
|
||||
|
||||
Single `@repo/core` package organized by layer (matching Lazar's reference), with domain-based grouping inside use-cases and controllers (elements of feature-slicing).
|
||||
|
||||
### Layer Structure
|
||||
|
||||
```
|
||||
packages/core/
|
||||
├── src/
|
||||
│ ├── entities/ # INNERMOST: zero deps
|
||||
│ │ ├── models/ # Zod schemas + TS types (user, article, session, cookie)
|
||||
│ │ ├── errors/ # Domain errors (AuthenticationError, NotFoundError, etc.)
|
||||
│ │ └── AGENTS.md
|
||||
│ │
|
||||
│ ├── application/ # USE CASES + INTERFACES
|
||||
│ │ ├── repositories/ # IUsersRepository, IArticlesRepository, etc.
|
||||
│ │ ├── services/ # IAuthService, ITelemetryService, etc.
|
||||
│ │ ├── use-cases/
|
||||
│ │ │ ├── auth/ # sign-in, sign-up, sign-out + AGENTS.md
|
||||
│ │ │ └── content/ # create-article, get-articles + AGENTS.md
|
||||
│ │ └── AGENTS.md
|
||||
│ │
|
||||
│ ├── infrastructure/ # IMPLEMENTATIONS
|
||||
│ │ ├── repositories/ # Payload, Drizzle, and mock implementations
|
||||
│ │ ├── services/ # Better Auth, OpenTelemetry+Sentry, mocks
|
||||
│ │ └── AGENTS.md
|
||||
│ │
|
||||
│ ├── interface-adapters/ # CONTROLLERS
|
||||
│ │ └── controllers/
|
||||
│ │ ├── auth/ # sign-in, sign-up, sign-out controllers
|
||||
│ │ ├── content/ # articles controller
|
||||
│ │ └── AGENTS.md
|
||||
│ │
|
||||
│ └── di/ # INVERSIFYJS WIRING
|
||||
│ ├── container.ts # InversifyJS container
|
||||
│ ├── types.ts # Symbols + DI_RETURN_TYPES
|
||||
│ ├── modules/ # auth.module, content.module
|
||||
│ └── AGENTS.md # Resolution table + registration recipe
|
||||
│
|
||||
└── AGENTS.md # Package overview + dependency rule
|
||||
```
|
||||
|
||||
### Dependency Rule (HARD CONSTRAINTS)
|
||||
|
||||
| Layer | Can import from | NEVER import from |
|
||||
|---|---|---|
|
||||
| entities/ | NOTHING | Everything else |
|
||||
| application/ | entities/ only | infrastructure/, interface-adapters/ |
|
||||
| interface-adapters/ | application/, entities/ | infrastructure/ |
|
||||
| infrastructure/ | application/, entities/, @repo/cms-client, external libs | interface-adapters/ |
|
||||
| di/ | All internal layers | apps/* |
|
||||
|
||||
### Dependency Injection — InversifyJS
|
||||
|
||||
The template uses InversifyJS with symbol-based resolution, following Lazar's reference implementation. Agent documentation mitigates the indirection through:
|
||||
|
||||
1. **Resolution table** in `di/AGENTS.md` mapping every symbol to its interface, production implementation, and mock implementation.
|
||||
2. **Step-by-step registration recipe** for adding new dependencies.
|
||||
3. **tsconfig constraints** documented as "do not remove" (`emitDecoratorMetadata`, `experimentalDecorators`, `reflect-metadata` import).
|
||||
|
||||
DI modules are organized by domain (auth.module.ts, content.module.ts). Test environments swap to mock implementations via `NODE_ENV=test` checks in modules.
|
||||
|
||||
---
|
||||
|
||||
## 3. packages/api + packages/api-client — tRPC & Shared Hooks
|
||||
|
||||
### packages/api — tRPC Router
|
||||
|
||||
```
|
||||
packages/api/
|
||||
├── src/
|
||||
│ ├── trpc.ts # tRPC init, context, middleware
|
||||
│ ├── router/
|
||||
│ │ ├── index.ts # Root appRouter
|
||||
│ │ ├── auth.router.ts # signIn, signUp, signOut procedures
|
||||
│ │ └── content.router.ts # articles CRUD procedures
|
||||
│ └── index.ts # Exports AppRouter type
|
||||
└── AGENTS.md
|
||||
```
|
||||
|
||||
tRPC is the single data path for all data access, including Payload CMS content. Each tRPC procedure calls a controller from `@repo/core/interface-adapters`. Input validation uses Zod schemas from `@repo/core/entities`. Business logic never lives in routers.
|
||||
|
||||
**tRPC HTTP handler:** Each app hosts its own tRPC endpoint. `apps/web-next` uses Next.js API routes (`app/api/trpc/[trpc]/route.ts`), `apps/web-tanstack` uses TanStack Start's server functions. Both import the `appRouter` from `@repo/api` and serve it. The router definition is shared; the HTTP transport is app-specific.
|
||||
|
||||
### packages/api-client — Shared React Query Hooks
|
||||
|
||||
```
|
||||
packages/api-client/
|
||||
├── src/
|
||||
│ ├── provider.tsx # tRPC + QueryClient provider
|
||||
│ ├── hooks/
|
||||
│ │ ├── auth/ # use-sign-in, use-session
|
||||
│ │ ├── content/ # use-articles, use-create-article
|
||||
│ │ └── index.ts # Re-exports all hooks
|
||||
│ └── index.ts
|
||||
└── AGENTS.md
|
||||
```
|
||||
|
||||
Both `apps/web-next` and `apps/web-tanstack` wrap their root with `<ApiProvider>` and use identical hooks. The hooks are framework-agnostic — they never import from Next.js or TanStack internals.
|
||||
|
||||
---
|
||||
|
||||
## 4. Payload CMS Architecture
|
||||
|
||||
### packages/cms-core — Payload Definition
|
||||
|
||||
All Payload CMS configuration lives in this standalone package, not inside `apps/cms`. This includes `payload.config.ts`, all collection definitions, globals, hooks, and access control.
|
||||
|
||||
```
|
||||
packages/cms-core/
|
||||
├── src/
|
||||
│ ├── payload.config.ts # Full Payload config
|
||||
│ ├── collections/
|
||||
│ │ ├── articles/
|
||||
│ │ │ ├── index.ts # CollectionConfig
|
||||
│ │ │ ├── fields.ts # Field definitions
|
||||
│ │ │ ├── hooks/ # Thin adapters → use cases
|
||||
│ │ │ └── access/ # Access control rules
|
||||
│ │ ├── users/
|
||||
│ │ └── media/
|
||||
│ ├── globals/ # Site settings, navigation
|
||||
│ └── index.ts # Exports config + all collections
|
||||
└── AGENTS.md
|
||||
```
|
||||
|
||||
`apps/cms` is a thin Next.js shell that imports the config from `@repo/cms-core` and serves the Payload admin panel. It contains almost no custom code.
|
||||
|
||||
### Payload Hook Architecture
|
||||
|
||||
Hooks are categorized into two types:
|
||||
|
||||
**CMS-operational (stay in cms-core hooks):**
|
||||
- Auto-generating slugs from titles
|
||||
- Image resizing/optimization
|
||||
- Populating default field values
|
||||
- CMS-specific access control
|
||||
|
||||
**Business logic (delegate to use cases):**
|
||||
- Sending notifications on publish
|
||||
- Enforcing business validation rules
|
||||
- Updating related records across domains
|
||||
- Triggering workflows
|
||||
|
||||
Business logic hooks are thin adapters (max 5-10 lines) that map Payload's hook arguments to use case inputs and call use cases from `@repo/core/application`. They never import from `@repo/core/infrastructure` or call external services directly.
|
||||
|
||||
**Rule of thumb:** If deleting the hook would break a business requirement, the logic must be in a use case. If it would only break a CMS convenience feature, it can stay in the hook.
|
||||
|
||||
### packages/cms-client — Dual-Mode Payload Client
|
||||
|
||||
```
|
||||
packages/cms-client/
|
||||
├── src/
|
||||
│ ├── client.ts # createPayloadClient()
|
||||
│ ├── local-client.ts # Local API (direct Payload instance)
|
||||
│ ├── http-client.ts # HTTP REST fallback
|
||||
│ ├── types.ts # Generated via payload generate:types
|
||||
│ └── index.ts
|
||||
└── AGENTS.md
|
||||
```
|
||||
|
||||
The client supports two modes:
|
||||
|
||||
- **Local mode (primary):** Receives a Payload instance, calls `payload.find()`, `payload.findByID()`, etc. directly. Full access to Payload's query capabilities (where, sort, limit, depth, page, populate). Used by all server-side apps.
|
||||
- **HTTP mode (fallback):** Uses Payload's REST API. For external services that don't have access to a Payload instance.
|
||||
|
||||
**Initialization:** The Payload instance is **injected, not imported**. At app startup, each app creates a Payload instance using the config from `@repo/cms-core` and passes it to `createPayloadClient()`. This prevents circular dependencies. The initialization code lives in each app's server entry point (e.g., `apps/web-next/src/lib/payload.ts`, `apps/web-tanstack/src/lib/payload.ts`) — it is NOT in any shared package.
|
||||
|
||||
| Context | Mode | How |
|
||||
|---|---|---|
|
||||
| apps/cms server-side | Local | Same process as Payload |
|
||||
| apps/web-next server-side | Local | Initializes own Payload instance, shares DB |
|
||||
| apps/web-tanstack server-side | Local | Initializes own Payload instance, shares DB |
|
||||
| Client-side (browser) | N/A | Goes through tRPC, server handles it |
|
||||
| External services | HTTP | createPayloadClient({mode: "http", baseURL}) |
|
||||
|
||||
**This package is standalone.** It never imports from `@repo/cms-core`, `@repo/core`, or `apps/*`.
|
||||
|
||||
**Type generation:** Payload's built-in `payload generate:types` reads `payload.config.ts` from `@repo/cms-core` and outputs TypeScript types to `cms-client/src/types.ts`. This runs as a Turborepo task in the build pipeline.
|
||||
|
||||
### Migrations
|
||||
|
||||
Payload CMS manages its own database migrations via `payload migrate`. This is the primary migration system since most data tables are defined as Payload collections. Drizzle migrations are optional — only needed for app-specific tables that Payload doesn't manage (e.g., session tokens, analytics, queues).
|
||||
|
||||
---
|
||||
|
||||
## 5. Data Flow
|
||||
|
||||
Complete request lifecycle from UI to database:
|
||||
|
||||
```
|
||||
UI Component (Next.js or TanStack Start)
|
||||
→ useArticles() @repo/api-client hook
|
||||
→ trpc.content.list @repo/api router procedure
|
||||
→ articlesController.list() @repo/core/interface-adapters
|
||||
→ getArticlesUseCase() @repo/core/application
|
||||
→ getInjection("IArticlesRepo") InversifyJS resolves at runtime
|
||||
→ PayloadArticlesRepository @repo/core/infrastructure
|
||||
→ PayloadClient.find(...) @repo/cms-client (LOCAL mode)
|
||||
→ Payload Local API Direct DB access, no HTTP
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 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
|
||||
|
||||
@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)
|
||||
```
|
||||
|
||||
### Circular Dependency Prevention — HARD RULES
|
||||
|
||||
These rules are non-negotiable and enforced via documentation + linting:
|
||||
|
||||
- **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
|
||||
|
||||
---
|
||||
|
||||
## 7. Technology Stack
|
||||
|
||||
| Concern | Choice | Architecture Layer |
|
||||
|---|---|---|
|
||||
| Monorepo | Turborepo + pnpm workspaces | Infrastructure |
|
||||
| Frameworks | Next.js + TanStack Start (coexist) | Frameworks & Drivers |
|
||||
| CMS | Payload CMS 3.x (standalone in cms-core) | Frameworks & Drivers |
|
||||
| CMS Client | Dual-mode: Local API (primary) + HTTP (fallback) | Infrastructure |
|
||||
| API | tRPC (single data path, wraps all data) | Interface Adapters |
|
||||
| DI | InversifyJS + agent documentation | Frameworks & Drivers |
|
||||
| Validation | Zod | All layers |
|
||||
| Database | Agnostic → Drizzle + PostgreSQL (optional, alongside Payload) | Infrastructure |
|
||||
| Auth | Agnostic → Better Auth default | Infrastructure |
|
||||
| Observability | OpenTelemetry interfaces → Sentry backend | Infrastructure |
|
||||
| State (server) | TanStack Query (via tRPC) | Frameworks & Drivers |
|
||||
| State (client) | Zustand | Frameworks & Drivers |
|
||||
| Styling | Tailwind CSS v4 + shadcn/ui (@repo/ui) | Frameworks & Drivers |
|
||||
| UI Architecture | Atomic Design (atoms, molecules, organisms, templates) | Frameworks & Drivers |
|
||||
| Testing (unit/integ) | Vitest | All layers |
|
||||
| Testing (E2E) | Playwright | Frameworks & Drivers |
|
||||
| Deployment | Docker-first + docker-compose | Infrastructure |
|
||||
| Migrations | Payload primary, Drizzle optional | Infrastructure |
|
||||
| Type generation | payload generate:types → cms-client/types.ts | Build pipeline |
|
||||
|
||||
---
|
||||
|
||||
## 8. UI Architecture — Atomic Design + shadcn/ui + Storybook
|
||||
|
||||
### @repo/ui Package Structure
|
||||
|
||||
```
|
||||
packages/ui/
|
||||
├── src/
|
||||
│ ├── atoms/ # shadcn primitives + custom atoms
|
||||
│ │ ├── button/
|
||||
│ │ │ ├── button.tsx # Component
|
||||
│ │ │ ├── button.stories.tsx # Co-located Storybook story
|
||||
│ │ │ ├── button.test.tsx # Unit test
|
||||
│ │ │ └── index.ts # Export
|
||||
│ │ ├── input/
|
||||
│ │ ├── label/
|
||||
│ │ ├── badge/
|
||||
│ │ ├── ... (separator, skeleton, avatar, icon, spinner, etc.)
|
||||
│ │ ├── index.ts # Re-exports all atoms
|
||||
│ │ └── AGENTS.md
|
||||
│ │
|
||||
│ ├── molecules/ # 2-3 atoms combined, single responsibility
|
||||
│ │ ├── form-field/ # Label + Input + Error
|
||||
│ │ ├── search-bar/ # Input + Button + Icon
|
||||
│ │ ├── tooltip/
|
||||
│ │ ├── popover/
|
||||
│ │ ├── select/
|
||||
│ │ ├── index.ts
|
||||
│ │ └── AGENTS.md
|
||||
│ │
|
||||
│ ├── organisms/ # Complex, self-contained UI sections
|
||||
│ │ ├── data-table/ # With sub-components (header, pagination)
|
||||
│ │ ├── dialog/
|
||||
│ │ ├── card/
|
||||
│ │ ├── header/
|
||||
│ │ ├── sidebar/
|
||||
│ │ ├── command-palette/
|
||||
│ │ ├── index.ts
|
||||
│ │ └── AGENTS.md
|
||||
│ │
|
||||
│ ├── templates/ # Page-level layouts with content slots
|
||||
│ │ ├── dashboard-layout/
|
||||
│ │ ├── auth-layout/
|
||||
│ │ ├── content-layout/
|
||||
│ │ ├── index.ts
|
||||
│ │ └── AGENTS.md
|
||||
│ │
|
||||
│ ├── hooks/ # Shared UI hooks (use-media-query, use-debounce)
|
||||
│ ├── lib/ # Utilities (cn() helper)
|
||||
│ └── styles/ # globals.css, design tokens
|
||||
│
|
||||
├── components.json # shadcn/ui config (aliases point to atoms/)
|
||||
├── tailwind.config.ts
|
||||
└── AGENTS.md # Package overview + atomic classification guide
|
||||
```
|
||||
|
||||
### Atomic Design Import Rules
|
||||
|
||||
| Level | Can import from | NEVER import from |
|
||||
|---|---|---|
|
||||
| Atoms | lib/, hooks/, styles/ | molecules/, organisms/, templates/ |
|
||||
| Molecules | atoms/, lib/, hooks/ | organisms/, templates/ |
|
||||
| Organisms | atoms/, molecules/, lib/, hooks/ | templates/ |
|
||||
| Templates | atoms/, molecules/, organisms/, lib/, hooks/ | (top level) |
|
||||
| Pages | Everything from @repo/ui + @repo/api-client | **Live in apps/, NOT in @repo/ui** |
|
||||
|
||||
### Component Rules
|
||||
|
||||
- **Atoms:** No margins/positioning, no state, no business logic. Pure visual elements.
|
||||
- **Molecules:** Single responsibility, minimal controlled state. Combine 2-3 atoms.
|
||||
- **Organisms:** Can have internal state and sub-components. Self-contained sections.
|
||||
- **Templates:** Use children/slots for content. NEVER hard-code content.
|
||||
- **All levels:** Co-locate `.stories.tsx` and `.test.tsx` next to the component.
|
||||
|
||||
### shadcn/ui Integration
|
||||
|
||||
`pnpm ui add [component]` lands components in `atoms/` by default (configured via `components.json` aliases). After adding, the component is classified using the guide in `AGENTS.md` and relocated to the correct atomic level if needed.
|
||||
|
||||
### Storybook
|
||||
|
||||
`apps/storybook` is a centralized Storybook instance using `@storybook/react-vite`. It pulls stories from `packages/ui/src/**/*.stories.tsx`. Story titles follow the pattern `"Level/ComponentName"` (e.g., `"Atoms/Button"`, `"Organisms/DataTable"`), creating a sidebar organized by atomic level.
|
||||
|
||||
---
|
||||
|
||||
## 9. Agent Infrastructure
|
||||
|
||||
### MCP Server Configuration
|
||||
|
||||
Project-level `.mcp.json` in the monorepo root, shared via git:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"storybook": {
|
||||
"type": "http",
|
||||
"url": "http://localhost:6006/mcp"
|
||||
},
|
||||
"playwright": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["@anthropic-ai/playwright-mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Storybook MCP** (via `@storybook/addon-mcp` in `apps/storybook`):
|
||||
- Component discovery: `list-all-documentation`
|
||||
- Component docs: `get-documentation`, `get-documentation-for-story`
|
||||
- Story authoring: `get-storybook-story-instructions`, `preview-stories`
|
||||
- Testing: `run-story-tests` (accessibility + interaction tests with autonomous fix loop)
|
||||
|
||||
**Playwright MCP:**
|
||||
- Browser automation for E2E validation
|
||||
- Accessibility snapshots
|
||||
- Visual verification of rendered components
|
||||
|
||||
### Agent Workflow
|
||||
|
||||
When building UI:
|
||||
1. Query Storybook MCP to discover existing components
|
||||
2. Read AGENTS.md at the target atomic level for rules
|
||||
3. Write component + co-located story
|
||||
4. Run story tests via Storybook MCP
|
||||
5. Autonomous fix loop if tests fail
|
||||
6. Visual validation via Playwright MCP
|
||||
|
||||
### Documentation Architecture — 4 Tiers
|
||||
|
||||
**Tier 1 — Root:**
|
||||
- `CLAUDE.md`: Claude Code entry point, project overview, quick start commands
|
||||
- `AGENTS.md`: Cross-agent instructions, monorepo package map, dependency flow, hard rules, end-to-end "add a feature" recipe
|
||||
- `docs/`: Architecture guides, how-to guides, ADRs, Mermaid diagrams
|
||||
|
||||
**Tier 2 — Package:**
|
||||
Each package gets an `AGENTS.md` with: purpose, public API, import rules, step-by-step recipes for common tasks. Key packages have specialized content:
|
||||
- `core/AGENTS.md`: Layer diagram, import rules table, DI resolution table, naming conventions
|
||||
- `cms-core/AGENTS.md`: Hook rules (do/don't), collection patterns, access control
|
||||
- `cms-client/AGENTS.md`: Dual-mode usage table, initialization patterns, standalone rule
|
||||
- `ui/AGENTS.md`: Atomic classification guide, shadcn workflow, story template
|
||||
|
||||
**Tier 3 — Layer (inside core):**
|
||||
- `entities/AGENTS.md`: Zero imports rule, model template, error template
|
||||
- `application/AGENTS.md`: Imports entities/ only, use case template, interface naming
|
||||
- `infrastructure/AGENTS.md`: Implementation patterns, mock naming, provider naming
|
||||
- `di/AGENTS.md`: Resolution table, registration recipe, scope guidance
|
||||
- `controllers/AGENTS.md`: Validate → call use case pattern, error mapping
|
||||
|
||||
**Tier 4 — Domain (business logic):**
|
||||
- `use-cases/auth/AGENTS.md`: Auth business rules, invariants, error cases, dependencies
|
||||
- `use-cases/content/AGENTS.md`: Content business rules, publishing workflow, error cases
|
||||
- `atoms/AGENTS.md`: Classification criteria, shadcn atom list, "no margins" rule
|
||||
- `molecules/AGENTS.md`: Single responsibility rule, composition examples
|
||||
- `organisms/AGENTS.md`: Sub-component patterns, internal state guidance
|
||||
- `templates/AGENTS.md`: Content slots pattern, "never hard-code content" rule
|
||||
|
||||
**Total: ~22 AGENTS.md files, ~16 docs files.**
|
||||
|
||||
### docs/ Folder Structure
|
||||
|
||||
```
|
||||
docs/
|
||||
├── architecture/
|
||||
│ ├── overview.md # High-level architecture diagram
|
||||
│ ├── clean-architecture.md # Uncle Bob's principles applied
|
||||
│ ├── dependency-flow.md # Complete dependency graph
|
||||
│ ├── data-flow.md # Request lifecycle
|
||||
│ └── circular-dep-prevention.md # Rules + examples
|
||||
│
|
||||
├── guides/
|
||||
│ ├── adding-a-feature.md # End-to-end walkthrough
|
||||
│ ├── adding-a-collection.md # Payload CMS collection
|
||||
│ ├── adding-a-component.md # Atomic design classification
|
||||
│ ├── testing-strategy.md # What to test at each layer
|
||||
│ ├── deployment.md # Docker build + deploy
|
||||
│ └── mcp-setup.md # Storybook MCP + Playwright MCP
|
||||
│
|
||||
├── decisions/
|
||||
│ ├── adr-001-monorepo-tool.md # Why Turborepo + pnpm
|
||||
│ ├── adr-002-di-framework.md # Why InversifyJS
|
||||
│ ├── adr-003-cms-separation.md # Why cms-core vs cms-client
|
||||
│ ├── adr-004-dual-mode-client.md # Why local + HTTP modes
|
||||
│ └── adr-005-atomic-design.md # Why atomic design for UI
|
||||
│
|
||||
└── diagrams/
|
||||
├── monorepo-structure.md # Mermaid diagram
|
||||
├── dependency-graph.md # Mermaid diagram
|
||||
└── data-flow.md # Mermaid diagram
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Docker & Local Development
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
ports: ["5432:5432"]
|
||||
|
||||
cms:
|
||||
build: ./apps/cms
|
||||
depends_on: [postgres]
|
||||
ports: ["3001:3000"] # Payload admin at localhost:3001
|
||||
|
||||
web-next:
|
||||
build: ./apps/web-next
|
||||
depends_on: [cms]
|
||||
ports: ["3000:3000"] # Next.js at localhost:3000
|
||||
|
||||
web-tanstack:
|
||||
build: ./apps/web-tanstack
|
||||
depends_on: [cms]
|
||||
ports: ["3002:3000"] # TanStack at localhost:3002
|
||||
|
||||
storybook:
|
||||
build: ./apps/storybook
|
||||
ports: ["6006:6006"] # Storybook at localhost:6006
|
||||
```
|
||||
|
||||
One command: `docker compose up` — spins up Postgres, Payload CMS admin, both reference apps, and Storybook.
|
||||
|
||||
---
|
||||
|
||||
## 11. Testing Strategy
|
||||
|
||||
| Layer | Tool | What to test |
|
||||
|---|---|---|
|
||||
| Entities | Vitest (unit) | Zod schema validation, error classes |
|
||||
| Use cases | Vitest (unit) | Business logic with mock implementations via DI |
|
||||
| Controllers | Vitest (unit) | Input validation, use case delegation, error mapping |
|
||||
| Infrastructure | Vitest (integration) | Real DB via test containers, Payload API calls |
|
||||
| UI components | Vitest (unit) + Storybook | Rendering, props, accessibility |
|
||||
| Full app | Playwright (E2E) | User flows across both Next.js and TanStack Start |
|
||||
Reference in New Issue
Block a user