DDD Structure And Flow
Use the lowest layer that can own the concern without depending on a higher layer.
Read the canonical architecture guide and the package creation checklist before creating a package.
Upstream source: Structure & Flow
Choose The Owner
| Concern |
Location |
Owns |
Does not own |
| Platform entry point |
apps/<app> |
Screens, global routing, store composition, analytics, observability, app glue |
Reusable feature internals |
| User-visible capability |
features/flow/<feature> |
Business-aware UI, user journeys, local state, flow routing |
App-specific screen composition |
| Capability shared across flows |
features/platform/<feature> |
Hooks, selectors, NFR rules, React glue, and components shared by several flows or use cases |
Single-flow internals and fully generic components |
| Business object |
domain/entity/<entity> |
Runtime schema, inferred type, defaults, mocks, selectors, slice |
Network calls and feature state |
| Network/data access |
domain/api/<name> |
API contracts, calls, transformations, RTK Query, thunks |
UI and app composition |
| Business-agnostic primitive or component |
shared/<name> |
Generic schemas, utilities, Redux primitives, and components without domain or feature knowledge |
Domain or app knowledge |
| Development-only tooling |
support/<name> |
Shared test, TypeScript, lint, and format configuration |
Runtime code |
Use these distinctions:
- Keep feature-scoped state in its
features/flow package; do not promote it to an entity.
- Keep an element in
features/flow when it belongs to one flow or use case.
- Move a feature-level element to
features/platform when several flows or use cases need it, especially when it connects them to the domain.
- Keep fully generic, business-agnostic elements in
shared or the existing design-system package.
- Keep app screens in each app. Let them compose exported flow entry points.
- Keep
.web and .native variants beside each other inside the owning feature.
Organize The Package
Every unit is a private package with package.json, src/, explicit exports, and no cross-package relative imports.
features/flow/<feature>/src/
├── components/ # shared by several steps
├── hooks/
├── router/ # flow-local routing only
├── state/ # feature-scoped state
├── steps/<StepName>/
│ ├── components/ # used only by this step
│ ├── viewModel.ts # state and orchestration
│ ├── view.ts # rendering and callbacks
│ ├── view.test.ts
│ └── index.ts
├── utils/
└── index.ts # minimal public API
domain/entity/<entity>/src/
├── schema.ts
├── schema.mock.ts
├── selectors.ts
├── slice.ts
└── index.ts
domain/api/<name>/src/
├── api.ts
└── index.ts
Colocate tests with the files they cover. Add folders only when they group files that change together.
Every index.ts above is a barrel: only export * from "./x" lines, with private code kept in an
internals location. See package-public-api for the rules and the
lint:structure check that enforces them.
Respect Boundaries
Dependencies flow downward:
apps → features/flow → features/platform → domain → shared
More precisely:
| Source |
May depend on |
shared |
shared |
domain/entity |
domain/entity, shared |
domain/api |
domain/api, domain/entity, shared |
features/platform |
features/platform, domain, shared |
features/flow |
features/flow, features/platform, domain, shared |
apps |
Any new-architecture layer |
support |
Development tooling only; consume it through devDependencies |
- Never import internal legacy
libs/* packages from shared, domain, or features.
- Let legacy code consume new architecture only as migration glue.
- Inject private new-architecture packages into published legacy packages at the app composition root.
- Import another package through its npm name, never through a relative path.
- Let Nx infer tags from paths; do not add manual tags unless local tooling requires them.
Name Packages
| Location |
Package name |
shared/<name> |
@shared/<name> |
domain/entity/<name> |
@domain/entity-<name> |
domain/api/<name> |
@domain/api-<name> |
features/platform/<name> |
@features/platform-<name> |
features/flow/<name> |
@features/flow-<name> |
support/<name> |
@support/<name> |
Review
- Confirm the concern sits in the lowest valid layer.
- Confirm folders express ownership and colocation, not arbitrary categories.
- Confirm apps only compose reusable flows and platform concerns.
- Confirm feature, entity, API, and development-only state have distinct owners.
- Confirm imports follow the dependency table and package public APIs.
- Confirm every
index.* is a pure barrel and no package re-exports another — see
package-public-api.
1---2name: ddd-structure-flow3description: Place and organize Ledger Wallet code in the DDD monorepo. Use when creating, moving, or reviewing code under apps, features, domain, shared, or support; deciding which layer owns a concern; structuring packages and flow steps; or checking package names, dependency boundaries, platform variants, and legacy imports.4---56# DDD Structure And Flow78Use the lowest layer that can own the concern without depending on a higher layer.910Read [the canonical architecture guide](../../../docs/ddd-monorepo-architecture.md) and [the package creation checklist](../../../docs/new-library.md) before creating a package.1112Upstream source: [Structure & Flow](https://ledgerhq.atlassian.net/wiki/spaces/WXP/pages/6111232117/Guideline+Monorepo+DDD+Re-architecture+Structure+Flow)1314## Choose The Owner1516| Concern | Location | Owns | Does not own |17| ---------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------- |18| Platform entry point | `apps/<app>` | Screens, global routing, store composition, analytics, observability, app glue | Reusable feature internals |19| User-visible capability | `features/flow/<feature>` | Business-aware UI, user journeys, local state, flow routing | App-specific screen composition |20| Capability shared across flows | `features/platform/<feature>` | Hooks, selectors, NFR rules, React glue, and components shared by several flows or use cases | Single-flow internals and fully generic components |21| Business object | `domain/entity/<entity>` | Runtime schema, inferred type, defaults, mocks, selectors, slice | Network calls and feature state |22| Network/data access | `domain/api/<name>` | API contracts, calls, transformations, RTK Query, thunks | UI and app composition |23| Business-agnostic primitive or component | `shared/<name>` | Generic schemas, utilities, Redux primitives, and components without domain or feature knowledge | Domain or app knowledge |24| Development-only tooling | `support/<name>` | Shared test, TypeScript, lint, and format configuration | Runtime code |2526Use these distinctions:2728- Keep feature-scoped state in its `features/flow` package; do not promote it to an entity.29- Keep an element in `features/flow` when it belongs to one flow or use case.30- Move a feature-level element to `features/platform` when several flows or use cases need it, especially when it connects them to the domain.31- Keep fully generic, business-agnostic elements in `shared` or the existing design-system package.32- Keep app screens in each app. Let them compose exported flow entry points.33- Keep `.web` and `.native` variants beside each other inside the owning feature.3435## Organize The Package3637Every unit is a private package with `package.json`, `src/`, explicit exports, and no cross-package relative imports.3839```text40features/flow/<feature>/src/41├── components/ # shared by several steps42├── hooks/43├── router/ # flow-local routing only44├── state/ # feature-scoped state45├── steps/<StepName>/46│ ├── components/ # used only by this step47│ ├── viewModel.ts # state and orchestration48│ ├── view.ts # rendering and callbacks49│ ├── view.test.ts50│ └── index.ts51├── utils/52└── index.ts # minimal public API5354domain/entity/<entity>/src/55├── schema.ts56├── schema.mock.ts57├── selectors.ts58├── slice.ts59└── index.ts6061domain/api/<name>/src/62├── api.ts63└── index.ts64```6566Colocate tests with the files they cover. Add folders only when they group files that change together.6768Every `index.ts` above is a **barrel**: only `export * from "./x"` lines, with private code kept in an69`internals` location. See [package-public-api](../package-public-api/SKILL.md) for the rules and the70`lint:structure` check that enforces them.7172## Respect Boundaries7374Dependencies flow downward:7576```text77apps → features/flow → features/platform → domain → shared78```7980More precisely:8182| Source | May depend on |83| ------------------- | -------------------------------------------------------------- |84| `shared` | `shared` |85| `domain/entity` | `domain/entity`, `shared` |86| `domain/api` | `domain/api`, `domain/entity`, `shared` |87| `features/platform` | `features/platform`, `domain`, `shared` |88| `features/flow` | `features/flow`, `features/platform`, `domain`, `shared` |89| `apps` | Any new-architecture layer |90| `support` | Development tooling only; consume it through `devDependencies` |9192- Never import internal legacy `libs/*` packages from `shared`, `domain`, or `features`.93- Let legacy code consume new architecture only as migration glue.94- Inject private new-architecture packages into published legacy packages at the app composition root.95- Import another package through its npm name, never through a relative path.96- Let Nx infer tags from paths; do not add manual tags unless local tooling requires them.9798## Name Packages99100| Location | Package name |101| -------------------------- | --------------------------- |102| `shared/<name>` | `@shared/<name>` |103| `domain/entity/<name>` | `@domain/entity-<name>` |104| `domain/api/<name>` | `@domain/api-<name>` |105| `features/platform/<name>` | `@features/platform-<name>` |106| `features/flow/<name>` | `@features/flow-<name>` |107| `support/<name>` | `@support/<name>` |108109## Review110111- Confirm the concern sits in the lowest valid layer.112- Confirm folders express ownership and colocation, not arbitrary categories.113- Confirm apps only compose reusable flows and platform concerns.114- Confirm feature, entity, API, and development-only state have distinct owners.115- Confirm imports follow the dependency table and package public APIs.116- Confirm every `index.*` is a pure barrel and no package re-exports another — see117 [package-public-api](../package-public-api/SKILL.md).