TBBN.Merchant Platform docs
Architecturedocs/architecture/monorepo-conventions.md

Monorepo Conventions

Where new code goes

  • apps/* — anything with a UI a human loads in a browser. Next.js. One app per audience (marketing, docs, merchant dashboard, exchange, internal ops, status page) — do not add audience-specific routes to an existing app instead of a new one; do not create a new app for a feature that belongs inside an existing audience's app.
  • services/* — anything that's an independently deployable backend process with its own database access and its own health check. NestJS. One bounded context per service (see service-catalog.md for the current list and each one's responsibility). If you're adding an endpoint that doesn't fit any existing service's stated responsibility, that's a signal to either widen a service's stated responsibility (update its README) or scaffold a new one (npm run new:service) — don't bolt unrelated logic onto an unrelated service.
  • packages/* — anything published/shared and not independently deployable on its own: types, utils, config, SDKs, widgets, ecommerce plugins, core-* shared domain logic consumed by multiple services. A core-* package holds logic two or more services need identically (e.g. the Trade Compatibility Score calculator in core-matching, consumed by both matching-engine and recommendation-service) — it is not a place to dump code you're unsure where else to put.
  • infra/* — how things run. No application code.
  • tools/* — repo-local developer tooling that isn't shipped anywhere (codegen, seeding, the new-service scaffolder).
  • docs/* — narrative and reference documentation. Update the relevant doc in the same PR that changes the behavior it describes — a stale doc here is treated as a bug.

Naming

  • Service/app/package folder names are kebab-case and match their package.json name field exactly (packages are scoped @tbbn/<folder-name>).
  • Database tables are snake_case, plural (trade_sessions, seller_merchant_links).
  • Event names are noun.past_tense_verb (listing.created, trade.completed) — see event-webhook-catalog.md.
  • Environment variables are SCREAMING_SNAKE_CASE, prefixed by service where ambiguous (AUTH_SERVICE_JWT_SECRET, not just JWT_SECRET, once more than one service needs a secret with that shape).
  • API routes are versioned in the path (/v1/...) — see docs/api/api-conventions.md.
  • UUIDs are UUIDv7 (time-ordered), generated via @tbbn/utils' generateId() — never rely on Postgres gen_random_uuid() (v4) directly, since v7 sorts chronologically and keeps b-tree indexes dense.

WORKING vs STUB

Every folder's README states its status. WORKING means real business logic exists and is tested. STUB means the folder boots, has a health check (services) or exports its documented public interface (packages), and has a README describing what it will do and which roadmap phase builds it out — but contains no real business logic yet. service-catalog.md is the single source of truth for current status; keep it in sync when a stub graduates to working.

Cross-workspace imports

Services and apps depend on packages/* purely through normal Node module resolution: an npm workspace dependencies entry (e.g. "@tbbn/types": "*") plus the npm-created node_modules/@tbbn/<name> symlink, resolving to that package's main/types fields (its built dist/, not raw src/) — never via relative ../../../packages/... imports, and not via a tsconfig.json paths shortcut either (an earlier version of tsconfig.base.json had one; it was removed because pointing paths at another package's src/ pulls that package's source files across the compiling package's rootDir boundary, which breaks tsc's per-package rootDir/outDir isolation). This means a package must be built (npm run build -w packages/<name>) before anything consuming it will typecheck — the repo root's build script builds the Phase 0-3 working set in explicit dependency order before falling back to --workspaces --if-present for everything else. Services never import from other services' src/ directly (only their published HTTP/event contract) — cross-service logic sharing goes through a core-* package instead.