313 lines
16 KiB
Markdown
313 lines
16 KiB
Markdown
---
|
|
name: development-monorepo-pnpm--3darch
|
|
description: >
|
|
Create, review, refactor, debug, and maintain full-stack monorepos built on pnpm workspaces
|
|
and Turborepo, split four ways — applications-backend, applications-frontend,
|
|
packages-backend, packages-frontend. Backend services default to NestJS; browser
|
|
applications default to React with IBM Carbon Design, unless the project architecture states
|
|
otherwise. Use when adding or changing a service, controller, module, React page, component
|
|
or hook; assigning ports and API base paths; sharing API contract types across the
|
|
front/back boundary; wiring Module Federation; editing shared eslint or typescript presets;
|
|
cutting changesets; editing Dockerfiles, Helm charts, or Turborepo tasks; running filtered
|
|
validation or blast-radius checks; or fixing workspace, dependency, lockfile, or
|
|
build-pipeline problems — even when the user only names their repository instead of saying
|
|
"monorepo".
|
|
license: Proprietary
|
|
metadata:
|
|
author: workspace-skills-code-agent
|
|
version: "1.0"
|
|
spec: agentskills.io/specification
|
|
compatibility: Requires pnpm (via corepack) and Node. Designed for Cline, Claude Code, GitHub Copilot, OpenAI Codex, and other compatible agent environments
|
|
---
|
|
|
|
# Full-Stack pnpm + Turborepo Monorepo
|
|
|
|
## 3D Architecture Wizzard Project Adoption
|
|
|
|
This is the primary project-adopted skill for **3D Architecture Wizzard** (`3darch`).
|
|
|
|
- Central source: https://gitea.lego-cloud.eu/home-v1-skills-code-agent/development-monorepo-pnpm
|
|
- Source branch: `test`
|
|
- Source commit: `ff69bcca2fb3b673297230710a74c70410fd1996`
|
|
- Adopted repository: https://gitea.lego-cloud.eu/corp-v1-3darch-skills-code-agent/development-monorepo-pnpm--3darch
|
|
- Application: https://gitea.lego-cloud.eu/corp-v1-3darch/corp-v1-3darch
|
|
- Documentation: https://gitea.lego-cloud.eu/corp-v1-3darch/corp-v1-3darch-documentation
|
|
- Environment namespace: `CORP_V1_3DARCH_*`
|
|
- Global diagram skill: https://gitea.lego-cloud.eu/home-v1-skills-code-agent/drawio-main
|
|
- Global glossary skill: https://gitea.lego-cloud.eu/home-v1-skills-code-agent/corp-v1--glossary
|
|
- Project automation: none; no scheduler job is authorized.
|
|
|
|
Project engineering peers:
|
|
- `development-branching-strategy--3darch` — https://gitea.lego-cloud.eu/corp-v1-3darch-skills-code-agent/development-branching-strategy--3darch
|
|
- `development-gitops-argo-cd--3darch` — https://gitea.lego-cloud.eu/corp-v1-3darch-skills-code-agent/development-gitops-argo-cd--3darch
|
|
- `development-monorepo-pnpm--3darch` — https://gitea.lego-cloud.eu/corp-v1-3darch-skills-code-agent/development-monorepo-pnpm--3darch
|
|
- `development-scripts--3darch` — https://gitea.lego-cloud.eu/corp-v1-3darch-skills-code-agent/development-scripts--3darch
|
|
- `devsecops-ci-cd-gitea--3darch` — https://gitea.lego-cloud.eu/corp-v1-3darch-skills-code-agent/devsecops-ci-cd-gitea--3darch
|
|
- `documentation-docusaurus--3darch` — https://gitea.lego-cloud.eu/corp-v1-3darch-skills-code-agent/documentation-docusaurus--3darch
|
|
- `template-engine-copier--3darch` — https://gitea.lego-cloud.eu/corp-v1-3darch-skills-code-agent/template-engine-copier--3darch
|
|
|
|
|
|
Work rules for monorepos holding **both** backend services and browser applications, using
|
|
**pnpm workspaces** for dependency resolution and **Turborepo** for task orchestration.
|
|
|
|
The defining choice in this layout is that the front/back split is expressed in the
|
|
**directory names themselves**, not in a nested convention. Four top-level workspace roots,
|
|
each with one job.
|
|
|
|
## Scope routing
|
|
|
|
Pick the reference that matches the change; do not load all of them.
|
|
|
|
| The change touches | Read |
|
|
|---|---|
|
|
| A service under `applications-backend/*` — modules, controllers, providers, config | `references/applications-backend.md` |
|
|
| An app under `applications-frontend/*` — components, routing, Carbon, Module Federation | `references/applications-frontend.md` |
|
|
| Anything under `packages-backend/*` or `packages-frontend/*` — exports, presets, changesets | `references/packages.md` |
|
|
| `pnpm-workspace.yaml`, `turbo.json`, root scripts, formatting, dependency overrides | `references/workspace-and-tooling.md` |
|
|
| Dockerfiles, images, Helm charts, values, deploy automation | `references/containers-and-helm.md` |
|
|
|
|
## 1. Canonical repository shape
|
|
|
|
```text
|
|
<repo>/
|
|
├── applications-backend/ # deployable services — NestJS by default
|
|
│ └── <service-name>/
|
|
├── applications-frontend/ # deployable browser apps — React + Carbon by default
|
|
│ └── <app-name>/
|
|
├── packages-backend/ # libraries consumed by services
|
|
│ └── <package-name>/
|
|
├── packages-frontend/ # libraries consumed by browser apps
|
|
│ └── <package-name>/
|
|
├── helm-charts/<chart-name>/ # deployment assets
|
|
├── .scripts/ # shell modules: docker, images, helm, pipelines
|
|
├── package.json # root scripts + packageManager + engines + overrides
|
|
├── pnpm-workspace.yaml # the four globs below
|
|
├── turbo.json # task pipeline
|
|
├── pnpm-lock.yaml
|
|
├── .prettierrc # single formatting authority
|
|
└── Taskfile.yml # thin delegator — includes .scripts/ modules only
|
|
```
|
|
|
|
```yaml
|
|
# pnpm-workspace.yaml
|
|
packages:
|
|
- "applications-backend/*"
|
|
- "applications-frontend/*"
|
|
- "packages-backend/*"
|
|
- "packages-frontend/*"
|
|
```
|
|
|
|
Applications sit **directly** under their root — `applications-backend/<name>`, not
|
|
`applications-backend/<name>/core`. The four-way split already gives each side its own
|
|
namespace, so no extra nesting layer is needed.
|
|
|
|
**A workspace exists only if a glob matches it.** Creating a directory is not enough: confirm
|
|
the glob, then run `pnpm install`. A directory outside these four roots is not part of the
|
|
build graph, and Turborepo will silently ignore it.
|
|
|
|
### Why the split is in the directory name
|
|
|
|
One glance at a path tells you which runtime a file ends up in. That matters because the two
|
|
sides have genuinely different constraints — a browser bundle ships to untrusted clients and
|
|
pays for every kilobyte, a service does not; a service holds credentials, a bundle must never.
|
|
When both live under one `packages/` root, the only thing standing between a secret-handling
|
|
utility and a browser bundle is someone reading the import path carefully.
|
|
|
|
## 2. Default stacks
|
|
|
|
| Layer | Default | Applies unless |
|
|
|---|---|---|
|
|
| `applications-backend/*` | **NestJS** — modules, controllers, providers, DI | the project architecture specifies another framework |
|
|
| `applications-frontend/*` | **React** with **IBM Carbon Design** (`@carbon/react`) | the project architecture specifies another framework or design system |
|
|
|
|
These are defaults, not mandates. **The project's stated architecture always wins** — read the
|
|
paired code-agent repository's `memory-bank/` (§9) and any architecture decision record
|
|
*before* scaffolding. If the architecture is silent, use the defaults above and record the
|
|
choice; if it disagrees, follow it and do not "correct" it toward these defaults.
|
|
|
|
Do not mix design systems inside one frontend application. Carbon components and a second
|
|
component library in the same app produce two type scales, two grid systems, and a token set
|
|
that fights itself.
|
|
|
|
## 3. The one sanctioned cross-boundary dependency
|
|
|
|
Frontend workspaces must not import from `packages-backend/*`, and backend workspaces must not
|
|
import from `packages-frontend/*` — with exactly one exception:
|
|
|
|
**API contract types.** The service that defines an endpoint owns its contract, so contract
|
|
packages live under `packages-backend/<domain>-contracts` and expose **types only** — no
|
|
runtime code, no dependencies, no side effects. Frontend consumers import them type-only:
|
|
|
|
```ts
|
|
import type { UserDto } from "@<scope>/user-contracts";
|
|
```
|
|
|
|
`import type` is erased at compile time, so nothing from the backend package reaches the
|
|
browser bundle. A plain `import` from the same package does reach it — that is the failure
|
|
this rule exists to prevent, and it will not show up as a type error.
|
|
|
|
A contract package that acquires a runtime dependency has stopped being a contract package.
|
|
Split it before it ships anything into a bundle.
|
|
|
|
## 4. Tooling baseline
|
|
|
|
Root `package.json` pins the contract for every contributor and CI runner: `packageManager`
|
|
(exact pnpm version, read by corepack), `engines.node`, `pnpm.overrides` for security pins,
|
|
and root scripts that always delegate to Turborepo — never to a single workspace.
|
|
|
|
```bash
|
|
pnpm build # turbo run build
|
|
pnpm dev # turbo run dev
|
|
pnpm lint # turbo run lint
|
|
pnpm check-types # turbo run check-types
|
|
pnpm format # prettier --write
|
|
pnpm publish-packages # build:ci + lint, then changeset version && changeset publish
|
|
pnpm prepush # turbo build --filter=...[HEAD^] — only what the branch touched
|
|
```
|
|
|
|
`build` depends on `^build`, `check-types`, and `lint`, so a type error or lint error is a
|
|
build failure for every downstream workspace, not a warning. Full pipeline, cache outputs, and
|
|
override rules → `references/workspace-and-tooling.md`.
|
|
|
|
## 5. Before making changes
|
|
|
|
1. Identify the exact workspace path, and therefore which of the four roots you are in.
|
|
2. Read that workspace's `package.json` — scripts, dependencies, whether it is publishable.
|
|
3. Inspect its `tsconfig.json`, lint config, build config, and source layout before adding.
|
|
4. Map what it consumes from shared packages, and what consumes it.
|
|
5. For Module Federation changes, map the host↔remote contract before editing either side.
|
|
6. Read the paired code-agent repository's `rules/` and `memory-bank/` when one exists (§9) —
|
|
it carries architecture decisions the code does not.
|
|
|
|
```bash
|
|
find applications-backend applications-frontend -maxdepth 2 -name package.json -print
|
|
find packages-backend packages-frontend -maxdepth 2 -name package.json -print
|
|
pnpm list --depth 0
|
|
task --list
|
|
```
|
|
|
|
## 6. Implementation rules
|
|
|
|
### Workspace boundaries
|
|
|
|
- Application logic stays inside its own workspace.
|
|
- Promote into a shared package only when **more than one** application needs it *and* the
|
|
abstraction has stopped changing. Config presets are the exception — shared from day one.
|
|
Premature promotion is the most expensive mistake here: every consumer inherits the churn.
|
|
- A shared package goes in the root matching its **consumers**, never its author.
|
|
- Never cross a workspace boundary with a relative import (`../../other-app/src`). Depend on
|
|
the workspace package, or the Module Federation contract, or not at all.
|
|
|
|
### TypeScript
|
|
|
|
- Every workspace extends a shared config from the `typescript-config` preset. Do not restate
|
|
`compilerOptions` locally beyond `outDir`, `rootDir`, `include`, `exclude`.
|
|
- `check-types` is `tsc --noEmit`, named identically in every workspace so Turborepo can run
|
|
it across the graph.
|
|
- Keep types explicit at public boundaries; avoid broad `any`.
|
|
|
|
### Dependencies
|
|
|
|
- Add a dependency to the **smallest** workspace that needs it.
|
|
- Root dependencies are monorepo tooling only — turbo, prettier, husky, changesets,
|
|
typescript. Nothing an application imports at runtime.
|
|
- Inter-workspace dependencies use `workspace:*`.
|
|
- Run `pnpm install` after every `package.json` edit and commit the lockfile change with the
|
|
edit that caused it.
|
|
- Never remove a `pnpm.overrides` entry without a security review — removing one silently
|
|
reintroduces a vulnerable transitive version and nothing in the build complains.
|
|
|
|
### Formatting
|
|
|
|
Root `.prettierrc` is the single authority. Do not add per-package Prettier config; a second
|
|
config produces churn diffs every time someone formats a file from the other side.
|
|
|
|
## 7. Validation
|
|
|
|
Validate the narrowest scope covering the change, then widen if it touched shared code.
|
|
|
|
```bash
|
|
# Single workspace
|
|
pnpm check-types --filter="@<scope>/<name>"
|
|
pnpm lint --filter="@<scope>/<name>"
|
|
pnpm build --filter="@<scope>/<name>"
|
|
|
|
# Shared package or cross-workspace change — unfiltered, plus each consumer
|
|
pnpm check-types && pnpm lint && pnpm build
|
|
pnpm build --filter="@<scope>/<consumer>"
|
|
|
|
# Only what the branch changed
|
|
pnpm prepush
|
|
```
|
|
|
|
Module Federation changes additionally require **host and remote running together** — a green
|
|
build on each side separately proves nothing about a runtime contract:
|
|
|
|
```bash
|
|
pnpm dev --filter="@<scope>/<host>"
|
|
pnpm dev --filter="@<scope>/<remote>"
|
|
```
|
|
|
|
Record the commands run and their outcome in the task record (§9). "It builds", without the
|
|
filter you used, is not a validation result.
|
|
|
|
## 8. Blast-radius checks
|
|
|
|
Before changing anything another workspace can observe — an exported symbol, a REST base path,
|
|
a port, an environment variable name, an MFE `exposes` key:
|
|
|
|
1. Enumerate the surface.
|
|
2. Search every consumer across all four roots.
|
|
3. Decide explicitly whether the change is backward-compatible.
|
|
4. If it is not: update consumers in the same change, or ship a compatible wrapper first.
|
|
5. Validate each affected consumer with its own `--filter` build.
|
|
|
|
```bash
|
|
grep -Rn "from '@<scope>/<package>'" applications-backend applications-frontend \
|
|
packages-backend packages-frontend
|
|
grep -Rn "api/<name>" applications-frontend # base-path callers
|
|
```
|
|
|
|
A port or base path change is never local: it also lands in the Helm chart values, the gateway
|
|
routing, and every frontend caller. See `references/containers-and-helm.md`.
|
|
|
|
## 9. Durable records
|
|
|
|
Monorepos in this family pair with a `<repo>-code-agent/` repository holding `rules/`,
|
|
`memory-bank/`, and `tasks/`. Update `memory-bank/` when durable facts change — application
|
|
inventory, ports, base paths, MFE topology, chosen frameworks and design system, chart
|
|
structure. Create a task record under `tasks/` for substantial work.
|
|
|
|
Never place task records inside the target repository unless explicitly asked.
|
|
|
|
## 10. Common mistakes to avoid
|
|
|
|
- Importing a `packages-backend/*` package from a frontend workspace with a value import
|
|
instead of `import type` — it silently pulls server code into the browser bundle.
|
|
- Putting a shared package in the root that matches its author rather than its consumers.
|
|
- Adding a workspace outside the four roots, or without updating `pnpm-workspace.yaml`.
|
|
- Re-introducing a `core/` nesting layer under an application directory.
|
|
- Changing a service port or base path without updating Helm values, gateway routing, and
|
|
every caller.
|
|
- Changing an MFE `exposes` key, remote name, or remote URL on one side only.
|
|
- Removing a `pnpm.overrides` entry without a security review.
|
|
- Adding a root-level dependency for a need local to one workspace.
|
|
- Skipping `--filter` validation for the exact workspace you edited.
|
|
- Mixing a second component library into a Carbon application.
|
|
- Overriding the project's stated architecture because it differs from the defaults in §2.
|
|
- Adding tasks directly to root `Taskfile.yml` instead of a `.scripts/` module.
|
|
- Editing generated artifacts (`dist/`, rendered manifests, lockfiles) by hand.
|
|
- Committing `.env`, `key.pem`, `cert.pem`, registry tokens, or `.npmrc` secrets — including
|
|
into memory-bank or task documentation.
|
|
|
|
## Reference files
|
|
|
|
| File | Contents | Read when |
|
|
|---|---|---|
|
|
| `references/applications-backend.md` | NestJS service layout, modules/controllers/providers, config and env, ports and base paths, health checks, service Dockerfile | Touching `applications-backend/*` |
|
|
| `references/applications-frontend.md` | React app layout, IBM Carbon setup and theming, routing, state, Module Federation wiring and failure modes, nginx image | Touching `applications-frontend/*` |
|
|
| `references/packages.md` | Package tiers per root, contract packages, public-API safety, export and dependency rules, changesets and publishing | Touching `packages-backend/*` or `packages-frontend/*` |
|
|
| `references/workspace-and-tooling.md` | Workspace globs, `turbo.json` pipeline and caching, root scripts, Prettier, `pnpm.overrides`, lockfile discipline | Touching root config or the task pipeline |
|
|
| `references/containers-and-helm.md` | `turbo prune` image builds for both sides, chart layout, values and secrets, port/base-path propagation | Touching Dockerfiles, images, or charts |
|
|
| `assets/example-shared-package/` | Minimal shared TypeScript package — ESM, barrel export, shared tsconfig and eslint | Scaffolding a package under either packages root |
|