Building an AI-Proof Monorepo: Architecture as Automated Enforcement

by Engineering Team
monorepoarchitectureaitypescriptautomation

There's a fundamental problem with architectural rules: they live in documentation that developers (and AI agents) may or may not read. Code reviews catch violations after the code is written. What if the architecture could enforce itself as code is generated?

This article explains how we built a TypeScript monorepo optimized for AI coding agents—where architectural violations are caught in under one second, and the rules are so explicit that AI agents follow them perfectly.

The Problem: AI Agents and Ambiguity

AI coding agents are exceptional at pattern matching but terrible at ambiguous requirements. Ask an AI to "add a user creation endpoint" without clear architectural guidelines, and you'll get:

// AI makes reasonable but inconsistent choices
app.post('/users', async (c) => {
  const body = await c.req.json()

  // Business logic in the route handler
  const user = {
    id: crypto.randomUUID(),      // ID generation
    ...body,
    emailVerified: false,         // Default values
    createdAt: new Date(),        // Timestamps
  }

  // Direct database access
  await db.insert(users).values(user)

  return c.json(user, 201)
})

Nothing wrong with this code—it works. But it violates thin-client architecture, mixes layers, and creates technical debt. Traditional solutions (documentation, code review) are too slow for the AI development loop.

The Solution: Architecture as Code

We built a four-layer validation system that makes architectural violations impossible to merge:

Layer 1: Package Isolation

Our monorepo has three core packages, each with zero workspace dependencies:

packages/core/          # Pure business logic
├── schemas/            # Zod validation schemas
├── types/              # z.infer types (never hand-written)
├── domain/             # Pure business logic
└── rpc/                # oRPC contracts

packages/db/            # Data access layer
├── schema/             # Drizzle table definitions
└── repositories/       # Database queries only

packages/services/      # Orchestration layer
└── *ApplicationService.ts  # Coordinates domain + db

Validation:

pnpm run check:isolation
# ✅ Core has zero workspace deps
# ✅ DB has zero workspace deps
# ✅ Services depends only on core + db

Layer 2: ESLint Custom Rules

We created ESLint rules that catch semantic violations:

no-business-logic-in-apps: Detects patterns like crypto.randomUUID(), new Date(), .map(), .filter() in route handlers.

no-framework-in-packages: Prevents framework imports (Hono, Express, React) in packages—keeping them portable.

no-hand-written-types: Enforces z.infer<typeof schema> instead of manually typing interfaces.

Result: AI agents get instant feedback when they violate patterns.

Layer 3: Dependency Graph Validation

pnpm run check:circular
# Scans 99 files
# ✅ No circular dependencies found

This prevents the classic monorepo problem: Package A imports Package B which imports Package A. The validation suite catches this immediately.

Layer 4: Schema-First Enforcement

pnpm run check:schema
# ✅ All types use z.infer
# ✅ Schema/type correspondence verified

Types are always inferred from Zod schemas, never hand-written. This creates a single source of truth for data structures.

The AI Development Loop

Here's the workflow that makes this architecture powerful:

# 1. Give AI explicit instructions
"Read docs/ai-agent-instructions.md and implement Product domain"

# 2. AI generates 7 files following templates:
✓ packages/core/src/schemas/product.ts
✓ packages/core/src/types/product.ts
✓ packages/core/src/domain/product/productService.ts
✓ packages/db/src/repositories/productRepository.ts
✓ packages/services/src/productApplicationService.ts
✓ packages/db/src/schema/products.ts
✓ apps/api-v2/src/routes/product.ts

# 3. Run validation (takes ~2 seconds)
pnpm run check:all

# 4. Two outcomes:
✅ All checks pass → AI followed architecture perfectly
❌ Violations found → AI sees errors → self-corrects

The key: Feedback happens in seconds, not hours. AI agents learn the patterns through immediate validation.

Why This Matters for Solo Developers

Traditional architectural enforcement assumes teams:

  • Senior developers review code
  • Regular architecture discussions
  • Collective discipline and buy-in

Solo developers using AI agents need different guardrails:

Traditional approach (doesn't scale):

  • Write docs → Hope AI reads them
  • Manually review every file
  • Catch violations after they're written
  • ⏱️ Time-consuming, error-prone

Automated approach (scales perfectly):

  • AI reads machine-parseable templates
  • Validation suite reviews automatically
  • Catch violations as code generates
  • ⏱️ Instant feedback loop

The Decision Tree: Where Does Code Go?

AI agents excel when given explicit decision trees. Our architecture provides exactly that:

Q: Where does this code belong?

Is it a Zod schema?
  → packages/core/src/schemas/{entity}.ts

Is it a type?
  → packages/core/src/types/{entity}.ts (use z.infer)

Pure business logic (no DB, no HTTP)?
  → packages/core/src/domain/{entity}/{entity}Service.ts

Database query?
  → packages/db/src/repositories/{entity}Repository.ts

Orchestrates domain + database?
  → packages/services/src/{entity}ApplicationService.ts

HTTP routing?
  → apps/*/src/routes/{entity}.ts (max 15 lines)

There's one correct answer for every piece of code. No ambiguity. No interpretation needed.

Real-World Example: Thin Route Handlers

Before automation:

// 80 lines, business logic embedded
app.post('/users', async (c) => {
  const validated = schema.parse(body)
  const user = { id: crypto.randomUUID(), ... } // ❌ Logic
  const discount = calculateDiscount(user)       // ❌ Logic
  await db.insert(users).values(user)           // ❌ Direct DB
  return c.json(user)
})

After automation:

// 6 lines, pure delegation
app.post('/users', async (c) => {
  const body = await c.req.json()
  const user = await userApplicationService.createUser(body)
  return c.json(user, 201)
})

ESLint enforces the 15-line limit and detects business logic patterns. AI agents learn to delegate immediately.

The Validation Suite

Four automated checks run in parallel:

  1. check:deps - Validates workspace dependencies and protocols
  2. check:circular - Prevents circular dependencies
  3. check:schema - Ensures schema-first development
  4. check:isolation - Verifies package self-containment

Run them all:

pnpm run check:all
# Executes in ~3 seconds
# Reports 4 validation layers
# Zero manual review needed

Benefits Beyond AI

While optimized for AI agents, this architecture benefits human developers too:

Clarity: Every package has one responsibility, zero ambiguity Testability: Pure business logic tested without HTTP/database mocks Portability: Packages work in Node, Deno, Edge, Browser Onboarding: New developers follow existing patterns via validation errors Refactoring: Automated checks prevent breaking changes

The Honest Trade-off

This architecture isn't free:

Costs:

  • More packages (core, db, services vs. monolithic)
  • Indirection (route → service → domain → repository)
  • Setup time (ESLint rules, validation scripts)

Benefits:

  • AI agents follow architecture perfectly
  • Instant feedback on violations
  • Impossible to merge broken patterns
  • Self-documenting via validation

For solo developers using AI coding agents, the benefits dramatically outweigh costs. The automation is the architecture review.

Conclusion: Architecture That Enforces Itself

The future of development isn't choosing between AI agents and architectural quality—it's building systems where architecture enforces itself through automation.

By making rules explicit, validation instant, and patterns unambiguous, we've created a monorepo where AI coding agents literally cannot make architectural mistakes. The guardrails are the architecture.

The result: generate entire domains in seconds, validate in seconds, ship with confidence.

Resources:

  • Full implementation: See /docs/yaml/ for architecture specs
  • AI instructions: /docs/ai-agent-instructions.md
  • Validation suite: pnpm run check:all

Architecture as code. Enforcement as automation. Mistakes caught in milliseconds. That's the future.