Skip to content

Tach DDD / Feature Slice Boundary Standard

Tach DDD / Feature Slice Boundary Standard

Section titled “Tach DDD / Feature Slice Boundary Standard”

FractalOps Backend uses layer-first DDD. Layer fixes dependency authority first; bounded context names ownership inside that layer; feature names one outcome.

backend/src/fractalops/
domain/<context>/features/<feature>/
application/<context>/features/<feature>/
adapters/<context>/features/<feature>/
interfaces/<transport>/<context>/features/<feature>/
orchestration/<context>/features/<feature>/
platform/

Do not restore contexts/<context>/<layer>/, top-level features/, anonymous tests/, or source-era infrastructure/ and presentation/ dialects.

Dependency direction:

domain <- application <- adapters/interfaces <- orchestration
platform -> composition only
  • Domain owns pure rules and ports. No FastAPI, Pydantic, or SQLAlchemy.
  • Application owns use cases. No adapter, interface, orchestration, or platform imports.
  • Adapters implement owned ports and translate provider data.
  • Interfaces bind HTTP, CLI, or MCP transports.
  • Orchestration composes cross-feature process flow.
  • Platform owns runtime wiring, configuration, telemetry, and infrastructure clients.

Sibling features collaborate through public contracts or ports. Deep implementation imports are defects.

CQRS lives inside one application feature only when read and write behavior genuinely diverge:

application/<context>/features/<feature>/
commands/
queries/

Do not create empty command/query folders or split CRUD mechanically.

Repository tach.toml is executable authority:

Terminal window
tach check --dependencies --interfaces --exact
tach check-external

Representative rules:

[[modules]]
path = "fractalops.application.*.features"
cannot_depend_on = [
"fractalops.adapters.*.features",
"fractalops.interfaces.*.*.features",
"fractalops.orchestration.*.features",
"fractalops.platform.**",
]
[[modules]]
path = "fractalops.domain.*.features"
cannot_depend_on_external = ["fastapi", "pydantic", "sqlalchemy"]
cannot_depend_on = [
"fractalops.application.*.features",
"fractalops.adapters.*.features",
"fractalops.interfaces.*.*.features",
"fractalops.orchestration.*.features",
"fractalops.platform.**",
]

Do not weaken rules to hide a violation. Move behavior to its owner or introduce a small public port.

Tests stay beside the smallest behavior owner:

domain/<context>/features/<feature>/*_test.py
application/<context>/features/<feature>/*_test.py
adapters/<context>/features/<feature>/*_contract_test.py
interfaces/<transport>/<context>/features/<feature>/*_test.py

Cross-repository runtime journeys belong in Assembly scenarios/<outcome>/. Generated contract conformance belongs in Contract. Frontend tests stay beside the component, route, or feature they exercise.

Move one outcome at a time:

  1. Name owner context and feature.
  2. Move behavior and its tests together.
  3. Replace deep sibling imports with public contracts.
  4. Run focused tests, Tach exact gate, and external dependency gate.
  5. Delete compatibility facade after live references reach zero.

Compatibility trees are delete-only. Existing stale ADR or documentation does not override current source, Tach, and accepted ADR lineage.