A Carrier project is organized around source files, imports, modules, and generated artifacts. The structure is intentionally simple, because enterprise systems become difficult enough without hiding the build graph.
Carrier source files live under src/. When src/main.carrier exists, Carrier follows the transitive import graph rooted there. Files that are not imported from that graph are not part of the build. This is important for large systems. It means the service boundary is not determined by every file in the repository — it is determined by the source graph.
Recommended Layout
A practical layered layout looks like this:
src/
main.carrier
00_models/
10_types/
20_actions/
30_routes/
40_async/
For larger services, a domain-oriented layout often works better:
The 00_, 10_, 20_ numeric prefixes are not cosmetic. They keep files sorted in dependency order — models before types, types before actions, actions before routes — which makes architectural review faster.
Local Modules
Reusable local packages belong under modules/<org>/<name>/src/. A bare module import resolves to that module source tree:
import "walknorth/forms" # resolves from modules/walknorth/forms/src/# imported declarations stay namespaced:# forms.Submission# forms.submit(...)Imported declarations remain namespaced behind the module name. This avoids global ambiguity and makes dependencies easier to inspect — a useful enterprise pattern. Shared capabilities can be packaged locally without collapsing all domains into one global namespace.
Workflow
The normal development loop is carrier fmt → carrier check → carrier build --target node. When API shape matters, add carrier openapi. When schema changes are involved, add carrier migrate generate. For enterprise architects, project structure matters because it shapes review. A predictable structure makes service boundaries, dependencies, and change impact easier to understand.