Skip to content
Y
YAS.SH
Docs

01_ARCHITECTURE

01 — ARCHITECTURE

Depends on: 00_MASTER.md · Next: 02_TECH_STACK.md


1. Philosophy

Module-centric, not page-centric. Every feature owns its vertical slice. Adding a tool never edits core.

Request → Nginx → Next.js / API → API SDK → REST /v1 → Service → Prisma → MariaDB
                                          ↓
                                    Redis/Valkey (cache/session/rate-limit — optional)

2. C4 Container Diagram

C4Container
  title YAS.SH V3 — Containers
  Person(user, "User", "Creates & clicks links")
  Person(admin, "Admin", "Moderates")
  Container(web, "Web", "Next.js 16", "Marketing, blog, dashboard, docs")
  Container(api, "API", "Hono / Next Routes", "REST /v1, redirect, auth")
  ContainerDb(db, "Database", "MariaDB 11", "Links, clicks, users, audit")
  Container(cache, "Cache/Queue", "Valkey/Redis + pg-boss", "Sessions, rate-limit")
  Container(search, "Search", "DB Full-Text", "Blog search (Meilisearch later)")
  Container(s3, "Storage", "R2/S3", "Images, backups")
  Rel(user, web, "HTTPS")
  Rel(web, api, "HTTPS /v1 + SDK")
  Rel(api, db, "Prisma")
  Rel(api, cache, "session/rate-limit")
  Rel(api, s3, "files")

3. Monorepo Layout (Implemented)

yas-enterprise-v3/
├── apps/
│   ├── web/                  # Next.js 16 App Router
│   │   ├── app/
│   │   │   ├── (marketing)/  # /, /pricing, /features, /tools
│   │   │   ├── (app)/        # /dashboard, /links, /analytics
│   │   │   ├── blog/         # /blog/[slug] from content collections
│   │   │   ├── docs/         # /docs/* (Stripe-like)
│   │   │   ├── api/          # Route Handlers if API colocated (Option A)
│   │   │   └── [code]/       # Redirect route (edge-optimizable)
│   │   ├── components/
│   │   └── content/          # MDX blog when colocated
│   └── api/                  # Standalone API (Option B — Hono)
│       ├── src/modules/      # Re-exports from /modules
│       └── src/index.ts
├── modules/                  # ← SOURCE OF TRUTH FOR FEATURES
│   ├── links/
│   │   ├── module.json       # Manifest (see 06_MODULE_SYSTEM.md)
│   │   ├── schema.prisma     # Prisma fragment (merged)
│   │   ├── routes.ts         # Route registration
│   │   ├── api.ts            # Hono/Next handlers + validation
│   │   ├── permissions.ts    # RBAC matrix
│   │   ├── menu.ts           # Nav registration
│   │   ├── ui/               # React components for this module
│   │   ├── docs.md           # Module documentation
│   │   └── tests/            # unit + integration
│   ├── qr/
│   ├── analytics/
│   ├── blog/
│   ├── bio/
│   └── tools/                # Each tool = module
│       ├── password/
│       ├── utm/
│       └── base64/
├── packages/
│   ├── database/             # Prisma schema (merged), migrations, seeds
│   ├── ui/                   # Design system (see 05_UI_UX.md)
│   ├── config/               # plans.ts, env.ts, tsconfig, eslint
│   └── security/             # isPrivateIP, validateUrl, rate-limit, crypto
├── blog/                     # Alternative location for MDX (if not in apps/web)
├── docker/                   # Dockerfiles + compose.yml + compose.prod.yml
├── nginx/                    # nginx.conf + prod hardened
├── scripts/                  # smoke.sh, security-smoke.sh, deps-check.sh
└── docs/                     # This spec

Rule: /modules/* is auto-scanned at build. No manual import list.


4. Layering Rules

Layer May import May NOT import
apps/web UI packages/ui, modules/*/ui, packages/config, API SDK packages/database directly
apps/api packages/database, packages/security, modules/*/api UI components
modules/*/ui packages/ui, packages/config packages/database
modules/*/api packages/database, packages/security UI
packages/* Nothing app-specific apps/*, modules/*

Frontend → API SDK → REST → Service → DB. Never UI → DB.


5. Module Auto-Discovery

At build: scripts/generate-modules.js scans modules/*/module.json and generates:

  • apps/web/app/(app)/nav.ts (sidebar)
  • apps/api/src/routes.ts (mount)
  • packages/config/plans.ts validation
  • docs sidebar

Adding a module = create folder + module.json → run pnpm gen:modules.


6. Naming Conventions

  • Files: kebab-case (link-analytics.tsx)
  • Components: PascalCase (LinkTable)
  • DB tables: snake_case (click_events), Prisma models PascalCase
  • API routes: /v1/{resource} kebab, cursor pagination ?cursor=...&limit=20
  • Env vars: UPPER_SNAKE (DATABASE_URL, REDIS_URL)
  • Commits: Conventional Commits (feat(links): add expiry)

7. Shared Config

  • TypeScript strict: strict + noUncheckedIndexedAccess + exactOptionalPropertyTypes
  • pnpm-workspace.yaml with catalog: for pinned versions
  • .npmrcengine-strict=true, save-exact=true, minimumReleaseAge=1440
  • packages/config exports: plans.ts (single source for limits), env.ts (Zod boot validation)

8. State & Data Flow

  • Server state: TanStack Query (or SWR) via API SDK
  • Form state: React Hook Form + Zod resolver
  • URL state: Search params for filters/pagination (shareable)
  • Cache: API SDK handles stale-while-revalidate, Redis caches redirects

9. Scaling Strategy

  • Phase 1: Single Next.js (web+api together) — no Redis
  • Phase 2: Split API to Hono on Fly/Coolify, add Valkey for rate-limit
  • Phase 3: Add pg-boss for jobs (no BullMQ yet)
  • Future: Meilisearch, dedicated worker service, Kubernetes — only when metrics justify

Do not over-provision in Phase 1.


Next: 02_TECH_STACK.md — exact versions and governance.

🍪 Cookies & privacy. yas.sh uses only essential cookies to keep you signed in and remember your preferences. We do not run third-party trackers. See our cookie policy and privacy policy.
Settings