> [Agent navigation index](/llms.txt)

# 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

```sh
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:

```text
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](/docs/linting-formatting#configure-a-workspace-at-the-root) 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:

```sh
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:

```ts
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:

```ts
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](/docs/api-routes).

## Manage shared dependencies

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

```sh
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.
