When Folders Become Contracts
When Folders Become Contracts
A folder structure is not just file classification — it can become a device that promises dependency direction and the scope of change between code modules. This article covers facts about Feature-Sliced Design, Domain-Driven Design, and Hexagonal Architecture.
1. Where folders become contracts
When folder names are merely "misc / utils / helpers", the location of new code is decided by human improvisation. Over time, code with the same responsibility gets scattered across many folders, and a single folder ends up mixing different responsibilities.
When a folder carries rules like "this directory has only this responsibility" or "this directory imports only from upper directories", the folder itself becomes a contract that enforces dependency direction and responsibility.
2. Feature-Sliced Design (FSD)
A frontend structure standard defined by the official feature-sliced.design documentation. The 1.0 specification stabilized around 2022.
Layers — only top-down dependencies are allowed. Imports between modules in the same layer are in principle forbidden:
| Layer | Role |
|---|---|
app |
Entry, global providers, routing root |
pages |
Composition by route. Place widgets and features into a page |
widgets |
Page-level large blocks (header, sidebar) |
features |
User actions (login, payment) |
entities |
Domain objects (User, Product) |
shared |
UI kit, libraries, configuration |
Within a layer, split by domain (slices), and within those split by technology (ui/, model/, api/, lib/).
The intent of this structure (official docs):
- Even as the project scales, the scope of change can be confined within folder boundaries.
- Onboarding new team members becomes easier.
- Reusability and isolation are tuned by layer position.
- Naming centers on business domains.
- A module in one layer cannot import from the same layer or upper layers (isolation).
3. Domain-Driven Design (DDD)
The source is Eric Evans's 2003 book Domain-Driven Design: Tackling Complexity in the Heart of Software. The central concept is the Bounded Context.
Each context has its own model and language (Ubiquitous Language), and connects to other contexts only through explicit integration patterns (Anti-Corruption Layer, Open Host Service, etc.). In folder structure, a directory per domain is the primary marker of a context boundary:
ordering/
billing/
inventory/
shared-kernel/
Even for the same "Customer", the Customer in ordering and the Customer in billing don't need to be the same code. Within a context, code is cohesive, and contexts are loosely coupled to each other.
4. Hexagonal Architecture (Ports and Adapters)
Articulated by Alistair Cockburn in the 2005 article "Hexagonal architecture". The core idea places the domain logic at the center, and surrounds the outside world (web, DB, message queue, CLI) with ports (interfaces) + adapters (implementations).
Translated to folders:
domain/ # Business rules. No external library dependencies.
application/ # Use cases. Define ports (interfaces).
adapters/
in/web/ # HTTP controllers
in/cli/
out/persistence/ # DB adapters
out/messaging/
Core rule — dependencies point only inward. domain doesn't know application; application doesn't know adapters. When this rule holds, swapping the DB or replacing HTTP with gRPC doesn't touch domain code.
Robert C. Martin's Clean Architecture (2017) restates the same idea with a concentric circles diagram.
5. Means of enforcement
To prevent folder contracts from being broken, tools can enforce them:
- Import boundary linters — ESLint's
import/no-restricted-paths,eslint-plugin-boundaries, JS Steiger, etc. - Dependency graph inspection — dependency-cruiser, madge.
- JVM ArchUnit — verifies package dependency rules with tests.
- Module systems — Java 9+ modules, TypeScript
paths/ project references, Rust modules / crates.
Even with tools, the rule that humans agree on is what ultimately makes them work.
6. Other paths
All three patterns vary in suitability by environment:
- FSD — frequently adopted in frontends, especially mid-to-large React/Vue SPAs.
- DDD — fits domains rich in business rules where modeling is core. Heavy for simple CRUD.
- Hexagonal · Clean — clearly effective when external dependencies (DB, external APIs) change often or testability matters.
For small projects, a flat folder layout is often enough. The cost of structure is never zero.
7. Common pitfalls
A folder is only a marker of the contract, not enforcement — without enforcement (linters, tests), one person's import breaks the contract.
Drawing DDD context boundaries wrong makes every change ripple across multiple contexts — splitting folders without conversation with domain experts is the most common failure.
Splitting FSD slices too finely forces small changes to span many folders — the balance of cohesion and separation differs per project.
A Hexagonal interface abstraction with only one implementation violates YAGNI — introducing it when a second adapter is actually needed matches the cost.
Closing thoughts
When folders go beyond classification and become contracts of dependency direction and responsibility, the scope of change in a large codebase narrows noticeably. FSD, DDD, and Hexagonal each suit different places. For small projects, a plain flat layout may be the right answer. With tools (ESLint, ArchUnit, dependency-cruiser) enforcing the contracts, human improvisation can be kept from disturbing the folders.
Next
- tradeoff-not-bestpractice
- progressive-refactor
Feature-Sliced Design official docs · Alistair Cockburn Hexagonal Architecture · Domain-Driven Design Reference (Eric Evans, 2015) · Robert C. Martin Clean Architecture · ArchUnit · Vaughn Vernon Implementing DDD · dependency-cruiser for reference.