DDD Structure And Flow
SkillDev toolsPlace 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.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the DDD Structure And Flow skill
What this skill tells your AI
The instructions your AI receives, as published by ledgerhq/ledger-live in .agents/skills/ddd-structure-flow/SKILL.md and read by ahel’s review.
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/flowpackage; do not promote it to an entity. - Keep an element in
features/flowwhen it belongs to one flow or use case. - Move a feature-level element to
features/platformwhen several flows or use cases need it, especially when it connects them to the domain. - Keep fully generic, business-agnostic elements in
sharedor the existing design-system package. - Keep app screens in each app. Let them compose exported flow entry points.
- Keep
.weband.nativevariants 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 fromshared,domain, orfeatures. - 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.
Signals
- GitHub stars
- 618
- Forks
- 490
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
ddd-structure-flow- Source
- github.com/ledgerhq/ledger-live