Design, audit, and evolve physical source and package structures that expose real architectural boundaries while keeping related behavior together. Use when creating or reviewing repository trees, deciding where files or packages belong, reducing flat folders, organizing by feature, route, screen, use case, bounded context, workflow or endpoint, structuring frontend state/data/UI ownership and design systems, making hexagonal inside/outside boundaries visible, locating ports, adapters and composition roots, structuring BFF routes, planning folder migrations, or enforcing package and import direction. For one module's logical responsibility, deepening, splitting, or caller contract use codebase-design; for repository-wide architecture investment discovery use improve-codebase-architecture. A physical flattening alone is not a reduce-system-complexity result.
Design, audit, and evolve physical source and package structures that expose real architectural boundaries while keeping related behavior together. Use when creating or reviewing repository trees, deciding where files or packages belong, reducing flat folders, organizing by feature, route, screen, use case, bounded context, workflow or endpoint, structuring frontend state/data/UI ownership and design systems, making hexagonal inside/outside boundaries visible, locating ports, adapters and composition roots, structuring BFF routes, planning folder migrations, or enforcing package and import direction. For one module's logical responsibility, deepening, splitting, or caller contract use codebase-design; for repository-wide architecture investment discovery use improve-codebase-architecture. A physical flattening alone is not a reduce-system-complexity result.
Structure Codebase
Design the source tree as a truthful map of meaning, ownership, runtime boundaries, and change cohesion. Do not start from a universal template. Select the lightest structure that makes this project's real seams easy to see and mechanically difficult to violate.
Use this skill for physical placement and dependency enforcement, including audits specifically about source trees, folders, packages, exports, or import direction. Load codebase-design when the question is which coherent responsibility and caller-facing contract a module should own; load improve-codebase-architecture when the unresolved question is which broader architecture investment is worth making and why now. Load reduce-system-complexity when a flatter tree is intended to support an evidence-backed claim that real dependencies, hops, states, or operational mechanism disappeared—a physical move alone is not reduction. Load evaluate-existing-solutions only when monorepo, build, workspace, or boundary-enforcement tooling is materially unresolved. Load hexagonal-architecture for port and adapter implementation, and domain-driven-design for domain modeling. Those patterns are independent; never infer that using one requires the other.
Read the relevant reference before producing a target tree:
Read references/visible-hexagon.md for a ports-and-adapters backend, DDD plus hexagonal architecture, or questions about ports, test actors, read models, and package isolation.
Read references/backend-patterns.md for a BFF, ordinary feature-first service, framework-constrained backend, operational tool, CLI, or small package.
Read references/frontend-patterns.md for a frontend, design system, client-side feature architecture, meta-framework UI, or full-stack request.
Read a well-structured codebase from broadest meaning to narrowest detail:
Product or capability — why the code exists and who owns it.
Architectural boundary — inside versus outside, but only when that boundary is real.
Feature, domain concept, use case, workflow, or endpoint — what changes together.
Implementation detail — framework, persistence, SDK, protocol, and small helpers.
Treat “screaming architecture” as a fitness question, not a folder taxonomy: can a newcomer tell what the system does? Technical names such as hexagon, adapters, driving, driven, and composition are useful at real seams. They are harmful only when they replace product meaning or claim boundaries that do not exist.
Folder names make claims. Packages, exports, imports, tests, and automated boundary rules prove them.
Keep the Axes Distinct
Axis
It answers
Do not confuse it with
Capability
Why does this code exist and change?
A technical layer
Bounded context
Where does language and model authority change?
A noun, table, team label, or folder of convenience
Hexagon
What policy must remain independently testable from external technology?
DDD, a generic core, or every pure library
Feature/use case
What behavior changes together?
A required package or bounded context
Endpoint
Where does a BFF request enter?
The owner of shared workflow state
Package/module
What boundary can tooling enforce?
A business boundary by itself
Composition root
Where is the runtime object graph constructed and owned?
A mandatory folder in every library
Workflow
1. Preserve the Requested Scope
Determine whether the user asked for an audit, proposal, migration plan, or implementation. Do not move files for a suggestion-only request. Do not broaden a folder change into a domain redesign without authorization.
2. Inspect Before Naming
Read enough of the actual system to avoid designing from filenames alone:
Project rules, architecture decisions, glossary, roadmap, and package index.
Routes, screens, commands, jobs, components, state/data owners, tests, public exports, and user-facing language.
Package manifests, source imports, runtime/deployment units, and provider dependencies.
Recent changes or ownership signals that reveal which files change together.
Record uncertainty. Candidate bounded contexts and package splits are hypotheses until language, invariants, ownership, or dependency direction justify them.
3. Select the Smallest Honest Shape
Project signal
Primary shape
Tiny package or early prototype
Flat or nearly flat
Ordinary backend without ports/adapters
Feature or use-case first
DDD without hexagonal architecture
Bounded-context and domain-language first
Explicitly hexagonal backend
Capability, then visible inside/outside, then featureful interior
BFF or endpoint-heavy edge service
URL/resource-first endpoints plus sibling workflows and composition
Framework-constrained backend
Thin required entrypoints delegating to feature-oriented behavior
Small frontend
Framework-native routes and shallow colocation
Feature-rich frontend
Route/screen composition plus product-feature modules and named foundations
Meta-framework full-stack app
Framework route tree with explicit client/server module boundaries
Established coherent frontend
Preserve or evolve it according to the requested scope and evidence
Frontend monorepo or design system
App/product scope plus earned packages and enforced public APIs
Ops, migration, or automation package
Workflow or command first
Monorepo
Product/capability grouping plus real leaf package boundaries
These shapes can combine. A monorepo is an enforcement overlay. A BFF is often a driving host for other hexagons without being a hexagon itself. DDD may exist with or without ports and adapters.
4. Test Whether a Hexagon Is Earned
Use a visible hexagon only when the project explicitly chooses ports and adapters and the proposed boundary has:
Meaningful application policy that must run without real external systems.
At least one purposeful conversation with a driving or driven actor.
Ports expressed in application language rather than cloned SDK APIs.
A test interactor at each port, or a concrete plan to add one.
Import, package, or architecture tests capable of enforcing inside versus outside.
Do not give adapter wrappers, provider integrations, simple CRUD, operational scripts, or vocabulary-only packages ceremonial hexagons.
5. Produce the Structure and Its Rules Together
Every proposal must include:
The selected shape and the evidence supporting it.
An annotated ASCII tree distinguishing grouping directories, real packages, and executable hosts. Show only evidence-backed target paths: every new directory displayed must own at least one named file, package, or concrete responsibility. Collapse unknown candidates into prose; use an ellipsis only for an existing unchanged subtree or an explicitly repeated established pattern.
Placement rules for new files.
Allowed and forbidden dependency directions.
Proportionate automated enforcement.
A staged migration when an existing tree must change.
Assumptions, deliberately deferred decisions, and structures intentionally not introduced.
Visible Hexagonal Backends
For an opted-in monorepo capability, prefer this grammar:
<capability>/ # grouping directory, not a package
├── hexagon/ # unmistakable INSIDE
│ └── <inside-package>/ # real workspace package
│ └── src/
│ └── <feature-or-use-case>/
├── adapters/ # unmistakable OUTSIDE
│ ├── driving/
│ │ └── <transport-adapter>/ # real workspace package
│ └── driven/
│ └── <provider-adapter>/ # real workspace package
└── testing/ # outside test interactors
└── <fake-or-contract-suite>/ # real workspace package when reusable
Apply these semantics:
Put pure business rules in domain modules.
Put provider-free application policy and orchestration in the hexagon even when it calls injected ports and is not referentially pure.
Put each port with the innermost consumer that owns the conversation. Driving ports and infrastructure-facing repository/gateway ports are normally application-owned; domain owns a port only when the domain model itself consumes it.
Put every concrete transport, database, filesystem, queue, clock, UUID generator, cloud service, and provider SDK outside.
Put reusable fakes, test drivers, and behavioral adapter contracts under testing/, never inside the production hexagon. Keep ordinary behavior tests colocated with the code they verify.
Put concrete construction, configuration, resource ownership, and shutdown in the executable host's composition root.
Organize inside packages by feature, domain concept, or use case. Do not replace one flat god file with a flat layer folder.
Keep domain and application as separate packages only when that dependency boundary earns its cost. One cohesive inside package is valid.
In a single-package service, keep capability first (for example, src/ordering/hexagon/ beside src/ordering/adapters/) and protect it with path-based import or architecture tests. Do not replace a repository-wide package boundary with root technical buckets such as src/hexagon/ and src/adapters/.
DDD Placement
Let bounded contexts define language and model authority; let the hexagon define the technology test wall. One does not imply the other.
Use a separate context only when terminology, invariants, lifecycle, ownership, or integration contracts diverge.
Keep domain concepts as nouns and use cases/workflows as verbs.
Communicate across contexts through explicit contracts, events, or anti-corruption layers.
Keep any shared kernel minimal, stable, and assigned to one explicit owner; never use a repository-root catch-all.
Keep a concept as a feature module inside an existing context when the context evidence is weak.
Load domain-driven-design instead of duplicating aggregate, entity, value-object, event, or ubiquitous-language guidance here.
Frontend Structure
Treat frontend architecture as a first-class mode. Read references/frontend-patterns.md and classify route ownership, product-feature cohesion, UI reuse level, state/data ownership, runtime boundaries, and package ownership before choosing a tree. Preserve an established frontend only when the requested scope or evidence supports preservation; redesign it when that is the task. Do not project backend hexagon/, ports, adapters, DDD entities, or composition-root vocabulary onto client code unless the frontend explicitly adopts those patterns for a real boundary. In a full-stack repository, classify client and server modules independently and enforce the framework's client/server graph.
When the frontend uses statecharts, state/data ownership includes which layer owns machines and actors. Declare it explicitly and enforce it the same way as any other import rule, because an agent producing an otherwise correct machine in a forbidden layer is a routine failure — and one this repository would rather catch in a boundary test than in CI. xstate defers to whatever this skill and the repository declare.
Composition Roots
Only executable applications have composition roots. Keep concrete selection near the entry point and nowhere else.
Use composition/ when a host has a nontrivial graph, configuration families, owned resources, or lifecycle/shutdown behavior.
Let main.ts, a framework startup file, or a small factory serve the role inline when the graph is trivial.
Do not make libraries, bounded contexts, or every feature contain an empty composition/ folder.
Do not let ordinary endpoints, use cases, or adapters construct their concrete collaborators. When a serverless framework makes a handler or consumer the executable deployment entrypoint, that entrypoint may also host trivial inline composition; keep wiring visibly separate from translation and extract it as soon as the graph is shared, stateful, or nontrivial.
Cohesion and Sharing
Keep files that change together close, including focused tests, schemas, fixtures, and mappers owned by one slice.
Expose a small explicit public API from every package or cross-feature module.
Prevent sibling features from importing one another's internals.
Promote code only when more than one current owner needs it and the promoted capability has a stable, purpose-specific name.
Give reused concepts purpose-named owners such as monetary-values, runtime-clock, or order-event-contracts; do not hide ownership under root shared, common, utils, helpers, or services buckets.
Split a folder when navigation or change cohesion improves; split a package when a dependency, ownership, release, or trust boundary needs mechanical enforcement.
Proportionate Enforcement
For a small package, public exports and review may be sufficient.
For a single application, use path aliases, restricted imports, architecture tests, and framework boundary rules.
For a monorepo, validate manifests, project references, public exports, workspace discovery, and dependency direction.
For a large/deep workspace, derive rules from physical roles or a small package-role registry instead of hand-maintained package allowlists.
Test recursive discovery at the deepest proposed path before moving packages there.
Read references/enforcement-and-migration.md for the dependency matrix and migration gates.
Anti-Patterns
Reject these structures:
Cosmetic src/hexagon inside a package that mixes provider code or permits outward imports.
Global domain, application, controllers, services, or repositories buckets spanning unrelated capabilities.
A package, bounded context, port, or hexagon for every noun or tiny interface.
Ports that reproduce SQL, HTTP, ORM, or SDK APIs.
Test adapters and reusable fakes beneath hexagon/.
Business behavior, provider construction, or shared runtime state hidden in endpoint handlers.
HTTP route leaves or CLI command entries buried inside capability or hexagon folders instead of the public interface tree (endpoints/, commands/).
Production BFF endpoints mounted around the route catalog, or endpoint leaves with no explicit access classification (bff-entry-points owns the classification and registrar model).
Production code importing development-only routes or test packages.
shared, common, utils, helpers, or services as unowned dumping grounds.
Empty folder skeletons added for symmetry.
Deep trees that workspace, build, test, coverage, and lint tooling cannot discover.
A big-bang migration that combines behavior changes, dependency inversion, and mass file moves.
Treating every pure formatter or utility as domain policy.
Fighting required framework locations instead of keeping them thin.
Global components, hooks, stores, or api buckets that obscure frontend product ownership.
Client bundles that can reach server-only modules, secrets, or privileged provider code.
Completion Check
Can a newcomer identify the product capabilities before the framework?
If hexagonal architecture is used, is inside versus outside unmistakable and mechanically true?
Is the interior navigable by domain concept or behavior rather than flat files?
Are endpoints and framework entrypoints thin?
Is concrete construction owned by an application entry point?
Are bounded contexts justified by language and model authority?
Are small and non-hexagonal projects spared architecture they have not earned?
In a frontend, are route, feature, UI-reuse, state/data, and runtime owners explicit without ceremonial layers?
Do package discovery and boundary checks support the proposed depth?
Does the proposal state where the next new file belongs and what it may import?
Does every new directory shown have a real owner now rather than serving as an empty or speculative placeholder?