0008: Compose backend operations with Effect
Domain services and runtime Layers own backend dependencies and resources
- Status: Accepted
- Date: 2026-09-15
- Decision maker: Trevor Gerhardt
Context
Backend packages once used constructor objects, injected clocks, and repeated runtime assembly. They also needed one rule for scoped resources and expected failures.
Decision
Backend operations in Persistence, Auth, Services, API, SDK internals, email, and Rackspace return Effects. A capability defines its service contract and canonical Layer. Domain Layers remain open to their repository and external requirements. Application and CLI entry points compose live adapters and own the resource scope. Tests provide small replacement Layers.
React, Next.js, vendor callbacks, and the default SDK keep ordinary TypeScript and Promise interfaces. The Effect SDK entry point serves Effect programs. Framework adapters execute Effects at their boundary. The module-shape standard owns file layout and the Effect toolchain decision owns diagnostic scope.
Repositories return domain facts and hide Drizzle handles. Persistence owns transactions and audit atomicity under ADR 0001. Effect platform services provide portable host capabilities. Small named Node adapters handle operations that need Node semantics. A runtime root chooses those adapters. Domain Layers do not choose them for callers.
Consequences
- Backend services can compose without passing aggregate dependency objects through every method.
- Resource lifetimes stay with the runtime or operation that opens them.
- Expected domain refusals remain typed failures. Defects and internal causes stay out of safe HTTP responses.
- Production and test graphs use the same service contracts.
History
An earlier pilot covered audited writes and Project creation. The current boundary covers backend composition while keeping framework and Promise interfaces.