Architecture

Monorepo Strategy

Syntropy uses pnpm workspaces for package management and Turborepo for task orchestration. The workspace is defined in pnpm-workspace.yaml:

packages:
  - apps/*
  - packages/*

All packages use the @syntropy/ scope and are marked private: true (not published to npm).

Dependency Graph

┌──────────────────────┐      ┌───────────────────────────┐
│ @syntropy/frontend   │      │ @syntropy/ingestion-      │
│ (React 19 + Vite)    │      │ service                   │
└──────────┬───────────┘      │ (Node worker/service)     │
           │                  └────────────┬──────────────┘
     ┌─────┴─────┐                         │
     │           │                 ┌───────▼─────────────┐
┌────▼─────┐ ┌───▼────────────┐    │ @syntropy/queue-    │
│@syntropy/│ │ @syntropy/ui   │    │ handler             │
│utils     │ │ (Sanity-based  │    └────────┬────────────┘
│          │ │ design system) │             │
└────┬─────┘ └──────┬─────────┘             │
     │              │                       │
     └──────┬───────┴───────────────┬───────┘
            │                       │
   ┌────────▼─────────┐      ┌──────▼──────────────┐
   │ @syntropy/       │      │ Catalog-managed     │
   │ tsconfig         │      │ external deps       │
   │ (build-time only)│      │ (e.g. AWS SDK,      │
   └──────────────────┘      │ React, Hono, Vite)  │
                             └─────────────────────┘

The unified frontend depends on @syntropy/utils and @syntropy/ui at runtime. The ingestion service depends on @syntropy/queue-handler. Internal packages depend on @syntropy/tsconfig at build time, and workspace packages share external dependency versions through the pnpm catalog.

Design Decisions

Single Docker Image with Runtime Selection

All apps are built into one Docker image. At startup, the APP environment variable selects which app to run via scripts/entrypoint.sh:

APP=frontend           # starts apps/frontend/dist/server/server.js
APP=ingestion-service  # starts apps/ingestion-service/dist/index.js

This simplifies the CI/CD pipeline — one build, one image, multiple deployments.

pnpm Catalog

Shared dependency versions are centralized in pnpm-workspace.yaml using the pnpm catalog feature:

catalog:
  typescript: ^5.7.0
  hono: ^4.7.0
  react: ^19.0.0
  # ...

Packages reference catalog versions with "catalog:" instead of hardcoding version ranges, ensuring consistency across the monorepo.

ESM-Only

All packages set "type": "module". TypeScript is configured with "module": "ESNext" and "moduleResolution": "bundler". No CommonJS fallbacks.

Workspace Protocol

manypkg enforces "workspaceProtocol": "require" — all internal dependencies must use "workspace:*", preventing accidental version mismatches.

Composite TypeScript Builds

Internal packages (like utils) use TypeScript's composite: true with declaration and declarationMap enabled. This enables project references and incremental builds. See Tooling > TypeScript for details.

Package Conventions

ConventionValue
Scope@syntropy/
PublishedNo (private: true)
Module systemESM ("type": "module")
Sourcesrc/ directory
Outputdist/ directory
Standard scriptsbuild, dev, clean, lint, typecheck, test