Backend Boundaries
Keep SOIT backend dependencies aligned across API, modules, kernel, adapters, infra, and wiring.
SOIT backend boundaries protect the kernel and keep product modules free to evolve. The layout is enforced by import-linter in CI, so a change that crosses a forbidden boundary fails the build.
Backend layout
app/
├── api/ # HTTP/WS/SSE transport layer (v1 routes and schemas)
├── kernel/ # Stable core (contracts/ports/specs/identity/runtime)
├── modules/ # Product domains (agent/workflow/knowledge, etc.)
├── adapters/ # Port implementations (LLM/vector/storage/secrets)
├── infra/ # Infrastructure (DB/session, etc.)
├── middleware/ # request_id / error / envelope middleware
├── plugins/ # Plugin SDK and loader
├── settings/ # Configuration and env parsing
├── wiring/ # Dependency wiring
└── main.py # FastAPI entry and router registrationDependency direction
app/apican call modules and stable kernel contracts; it stays thin.app/modulescan call kernel contracts, runtime services, ports, and adapters.app/adaptersimplements kernel ports and contains no business logic.app/kerneldepends only on kernel-owned code — never on modules, API, adapters, infra, or wiring.app/wiringregisters concrete providers; tests enforce that provider registration happens nowhere else.app/settingsholds configuration and feature flags with no external calls.
Kernel data that originates in product or infrastructure layers must cross an explicit provider boundary registered in wiring/ — for example, resource grants for identity checks and scoped egress policy lookups.
Kernel runtime structure
Runtime execution and persistence state is centralized under app/kernel/runtime/:
runtime/db/models/— all kernel-owned SQLModel table classes live here, and only here.runtime/tasks/— task service, repository, schemas, status language, and task events. Task status transitions are defined inapp/kernel/runtime/tasks/status.py.runtime/threads/— thread service, repository, schemas, and protocols.runtime/runs/— run/step/artifact/cost models, the trace writer, run service, and exporters.runtime/responses/— response repository, service, schemas, and projection orchestrator.
Kernel extension rules
- Port policy gateways share timeout, retry, run-id, and error helper behavior from
app/kernel/ports/common/policy.py. - Runtime services type their persistence dependencies as Protocols; SQLModel repositories are the default implementations.
- New SQLModel tables go under
app/kernel/runtime/db/models/; other kernel packages remain pure logic, providers, ports, contracts, or orchestration. - Kernel-owned outbox event payload versions are registered in
app/kernel/events/payload_registry.py. - Typed contract dataclasses are additive wrappers — they must not change HTTP schemas or database JSON shapes unless separately versioned.
Placement rules
- HTTP request/response schemas belong in API schemas.
- Domain schemas belong inside the relevant module.
- Cross-layer stable contracts belong in kernel contracts.
- Provider implementations belong in adapters.
Review focus
Before merging backend changes, check imports, tenant and workspace scoping, external gateway use, trace creation, and schema compatibility. The Testing and Quality Gate page lists the exact commands.