Node.js and TypeScript monorepos
Default for new products
New DoWEB software products use a Node.js and TypeScript monorepo unless there is a documented reason not to. The default toolchain is:
- Node.js 24 LTS, pinned in
.nvmrcor.tool-versionsand in the Dockerfile; - pnpm workspaces, with the pnpm version pinned in the root
package.jsonpackageManagerfield; - Turborepo for running cached workspace tasks; and
- TypeScript in strict mode.
Node versions are reviewed every six months. Production applications use only an actively supported Node.js LTS release.
Repository layout
cus-acme-portal/
├── apps/
│ ├── web/ # customer-facing web application
│ ├── api/ # HTTP API
│ └── worker/ # optional background jobs
├── packages/
│ ├── api-client/ # reusable API client
│ ├── ui/ # reusable UI components
│ ├── types/ # shared domain types
│ ├── eslint-config/ # shared lint configuration
│ └── tsconfig/ # shared TypeScript configuration
├── infra/ # deployment-only configuration when needed
├── package.json
├── pnpm-workspace.yaml
├── pnpm-lock.yaml
├── turbo.json
└── README.md
apps/ contains independently buildable and deployable products. packages/ contains deliberately reusable code and configuration. Do not use packages/ as a dumping ground: a package must have a named owner, a clear API, and at least two consumers or a stated future reuse case.
Deploy applications from apps/; do not deploy a shared package directly.
Product architecture
Start with the smallest architecture that meets the customer need: normally one web application, one API, and one database. Add a worker only for genuinely asynchronous work. Do not introduce microservices, event infrastructure, or extra databases merely for future flexibility.
Each deployable application must:
- own its route handlers, domain logic, and tests;
- expose configuration through a single validated configuration module;
- read secrets only from environment variables or injected secret files;
- emit structured logs to standard output; and
- expose a health endpoint when deployed as a service.
Shared packages may not import from an application. Applications may depend on shared packages through their public package exports, never through another workspace's src/ directory.
Root scripts and local workflow
Every new monorepo provides these root scripts:
pnpm format:check
pnpm lint
pnpm typecheck
pnpm test
pnpm build
Use corepack enable and pnpm install --frozen-lockfile in CI. A contributor should be able to clone the repository, copy .env.example to a local .env, install dependencies, and run the same checks locally.
Quality baseline
- TypeScript strict mode is enabled for application and shared-package code.
- Use one root ESLint and Prettier configuration; applications may extend it but may not replace it without an exception.
- Add automated tests for business rules, integration boundaries, and regressions. A UI-only snapshot is not sufficient coverage for customer-critical logic.
- Keep API contracts and database migrations in the application that owns them.
Exceptions
Use a separate repository, a different language, or a different architecture only when it materially reduces risk or cost. Document the decision, its owner, and the review date in the repository README. Examples include a customer-required platform, a vendor integration, or a small isolated script that does not benefit from a workspace.