- Rename eslint-config → core-eslint, typescript-config → core-typescript in all docs (package map, AGENTS.md, overview.md, dependency-flow.md, etc.) - Document the five-tag model (app, feature, core, core-composition, tooling) — refinement of ADR-006's three-tag mention - Document core-trpc's core-composition tag (transitively reaches features through core-api's AppRouter type) - Note Turborepo boundaries as a second enforcement layer alongside ESLint - Add ADR-010 explaining the two-layer enforcement decision and five-tag refinement Files updated: - docs/architecture/overview.md: package map, enforcement layers, five-tag section - docs/architecture/dependency-flow.md: boundary rules, enforcement strategy - docs/architecture/vertical-feature-spec.md: package names, five-tag model - AGENTS.md: package map, boundary rules, commands - CLAUDE.md: tooling package names, quick-start command - packages/core-eslint/AGENTS.md: tag clarification - packages/core-typescript/AGENTS.md: tag clarification - packages/core-api/AGENTS.md: core-composition tag - packages/core-cms/AGENTS.md: core-composition tag - packages/core-trpc/AGENTS.md: core-composition tag + rationale - docs/decisions/adr-010-turbo-boundaries.md: new ADR Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
82 lines
3.7 KiB
Markdown
82 lines
3.7 KiB
Markdown
# Dependency Flow
|
|
|
|
```
|
|
+-------------+ +-----------------+ +-----------+
|
|
| 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 | | | | |
|
|
+--------+ +-----------+ +----------------+
|
|
|
|
Boundary rules (enforced by ESLint + Turborepo boundaries):
|
|
app → app, core, core-composition, feature, tooling
|
|
feature → core, tooling
|
|
core → core, core-composition, tooling
|
|
core-composition → core, core-composition, feature, tooling
|
|
tooling → tooling
|
|
|
|
Composition exceptions:
|
|
core-api → @repo/<feature>/api (subpath only)
|
|
core-cms → @repo/<feature>/cms (subpath only)
|
|
```
|
|
|
|
## Concrete examples
|
|
|
|
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
|
|
```
|
|
|
|
Disallowed:
|
|
```ts
|
|
// in packages/blog (cross-feature)
|
|
import { Article } from "@repo/marketing-pages"; // ❌ feature → feature
|
|
|
|
// in packages/blog (deep import past public exports)
|
|
import { articles } from "@repo/blog/src/integrations/cms/collections/articles"; // ❌ no-private
|
|
|
|
// in packages/core-shared
|
|
import { blogRouter } from "@repo/blog/api"; // ❌ core → feature
|
|
|
|
// in packages/core-trpc
|
|
import { someBlogThing } from "@repo/blog"; // ❌ core → feature (only core-api/core-cms have exception)
|
|
```
|
|
|
|
## Enforcement strategy
|
|
|
|
Three layers work in tandem:
|
|
|
|
1. **`package.json` dependencies** — if you didn't declare it, you can't import it
|
|
2. **`exports` map** — blocks deep imports; only public subpaths are accessible
|
|
3. **Two parallel automated checks** (both enforcing the same five-tag model):
|
|
- **ESLint `eslint-plugin-boundaries`** runs at lint time, catching direct-import violations
|
|
- **Turborepo `boundaries`** runs at build time, validating the entire workspace graph including transitive dependencies
|
|
|
|
The two enforcement layers are independent but complementary. ESLint is stricter on per-import context (e.g., file-specific exemptions via `// @boundaries-ignore`), while Turborepo catches transitive issues that lint-time checking misses. Run `pnpm lint` and `pnpm turbo boundaries` in CI to catch all violations.
|