SOIT Docs
Architecture

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 registration

Dependency direction

  • app/api can call modules and stable kernel contracts; it stays thin.
  • app/modules can call kernel contracts, runtime services, ports, and adapters.
  • app/adapters implements kernel ports and contains no business logic.
  • app/kernel depends only on kernel-owned code — never on modules, API, adapters, infra, or wiring.
  • app/wiring registers concrete providers; tests enforce that provider registration happens nowhere else.
  • app/settings holds 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 in app/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.

On this page