Skip to content

Project structure

The Quickstart fit in one main.py. A real service grows into the four layers — and a directory layout that mirrors them keeps the dependency rules easy to follow.

A layout that maps to the layers

myservice/
├── domain/            # aggregates, commands, read models, invariants — pure Python
│   └── orders.py
├── application/       # specs, custom handlers, operation registries
│   └── orders.py
├── infrastructure/    # deps modules, lifecycle plans, client construction
│   └── wiring.py
├── interface/         # routes / event handlers, the runtime + lifespan
│   └── http.py
└── main.py            # compose the runtime and the app

Nothing forces these names — Forze imposes no project layout. What matters is the dependency direction, and folders make it visible.

What goes where

Layer Holds May import
domain Document subclasses, create/update commands, read models, @invariant / @update_validator, mixins nothing else in your app
application DocumentSpec (and friends), build_*_registry, custom handlers, stage hooks domain
infrastructure deps modules (PostgresDepsModule, …), LifecyclePlan, client setup application, domain
interface FastAPI / Socket.IO routes, ExecutionRuntime construction, the context dependency application, infrastructure

The one rule

Dependencies point inward: domain imports nothing from your other layers, application imports only domain, and infrastructure and interface sit on the outside. This is what lets you swap Postgres for Mongo, or FastAPI for a CLI, without touching business logic — and it's enforced by import-linter contracts, not just convention. See Architecture.

The name is the seam

One logical name — "orders" — appears in the spec (application), the deps module route (infrastructure), and is resolved in routes (interface). That shared string is the contract between the layers; keep it consistent and everything wires up. Get it wrong and a port won't resolve.