Skip to content
brix.v2 stable

Search documentation

70 guides

Documentation menu

Workspace architecture and libraries

Create a workspace, inherit root tooling policy, and share libraries, contracts, and exact dependencies across explicit project boundaries.

Create a workspace

brix create workspace commerce-platform
cd commerce-platform
brix create app browse-bff
brix create app checkout-bff
brix create react-app customer-portal
brix create library commerce-contracts
brix create ui-library member-ui

Bare application names go under apps/; bare library names go under packages/. Pass an explicit path when the workspace uses custom globs.

A typical layout is:

commerce-platform/
├── apps/
│   ├── browse-bff/
│   ├── checkout-bff/
│   └── customer-portal/
├── packages/
│   ├── commerce-contracts/
│   └── member-ui/
├── .brix-hooks/
├── .node-version
├── oxfmt.config.mts
├── oxlint.config.mts
├── package.json
├── pnpm-workspace.yaml
└── pnpm-lock.yaml

Put projects with runtime configuration and a deployment under apps/. Put code imported by other projects under packages/. Each application owns its routes, environment, tests, build, and deployment; shared libraries are not deployed as services.

Inherit root tool policy

Workspace members inherit the root .node-version, oxfmt.config.mts, oxlint.config.mts, managed skills, and Git hooks. Creation does not copy them into each member. Keep formatting and linting decisions in the root configs and express member exceptions with root-relative overrides, such as apps/admin/src/**/*.tsx. Config globs are relative to the file that declares them.

Open the workspace root in editors so the official Oxc extension finds the shared local binaries and configs. Add a member config only for a genuine separate boundary: it replaces ancestor discovery rather than automatically extending the root, so explicitly compose any root settings that should still apply and retain the Brix config helper. Member-owned TypeScript/build config, environment, .brix, and project-specific guidance remain in the member.

Hooks live only at the Git root under .brix-hooks/. Member brix init and workspace upgrades remove unchanged generated member Oxc configs, matching Node pins, and default hook directories so members inherit root policy; customized files are left untouched. See linting and formatting for examples.

Establish import boundaries

Applications must not depend on or import other deployable applications. Move intentionally shared contracts, components, business logic, or test utilities into a clearly named library.

Link every library directly to each consumer:

brix workspace link apps/checkout-bff packages/commerce-contracts --dry-run
brix workspace link apps/checkout-bff packages/commerce-contracts
brix workspace link apps/checkout-bff packages/test-support --dev

Use --dev only when the dependency is needed exclusively for build or test work. Import through the package's public API:

import { checkoutApiContract } from "@commerce/contracts";

Do not use parent-relative paths, file: tricks, or symlinks into another member's src/. Brix resolves static imports, exports, known dynamic imports, and require() calls to canonical paths. brix workspace link rejects application dependencies, and brix doctor catches equivalent hand-written edges. Library cycles are rejected.

Keep application-owned island wrappers and stores inside the application so its build can assign stable identities. A shared UI library can export ordinary Preact components; the consuming application declares the island boundary.

Share contracts, not implementations

A browser-safe contract package describes the intentional relationship between independently deployed consumers:

import { createApiContract } from "brix/contracts";
import { z } from "brix/zod";

export const basketApiContract = createApiContract({
  method: "GET",
  path: "/api/baskets/:basketId",
  response: z.object({ id: z.string(), itemCount: z.number().int().nonnegative() }),
});

The owning BFF implements that contract. Other applications consume the contract package, never the BFF's route implementation. See API routes.

Manage shared dependencies

Add registry dependencies to the consuming member with a focused Brix command:

brix deps#checkout-bff add date-fns
brix deps#member-ui add lucide-preact
brix deps#checkout-bff why date-fns

Brix stores exact third-party versions in the default catalog in pnpm-workspace.yaml and writes catalog: in member manifests. brix deps#checkout-bff update date-fns updates the shared catalog entry, so all consumers on that entry move together. Use a named catalog only when the workspace deliberately supports incompatible dependency lines.

Local libraries use direct workspace:* edges rather than catalog entries. Every imported library must be direct; do not rely on root hoisting. The root and every Brix member retain an explicit catalog-backed Brix dependency, while unrelated dependencies in external applications remain owned by their generator.