1.5 KiB
ADR 0002: Manual constructor injection
- Status: Accepted
- Date: 2026-08-20
Context
The capability-oriented modular monolith needs testable components and explicit lifecycle ownership. At this stage its dependency graph is small and still evolving. A compile-time generator or runtime container would introduce tooling, indirection, and debugging costs without a demonstrated wiring problem.
Decision
Use manual constructor injection. Each component declares required dependencies as constructor parameters, and thin cmd/ entry points act as composition roots that create configuration, infrastructure adapters, capability services, handlers, and process lifecycle in an explicit order.
Interfaces are defined by the consuming package where substitution is useful. Constructors return concrete implementations unless callers benefit from an interface. Database pools and other stateful resources are created once, passed explicitly, health-checked, and closed during graceful shutdown.
Consequences
- Dependency graphs remain visible to readers, the compiler, and tests.
- Tests can supply focused fakes at real boundaries without a service locator.
- Packages must not initialize services through globals or
init(). - Containers must never be passed through the application as dependency lookup mechanisms.
- Manual wiring may become verbose as the graph grows. A DI tool may be reconsidered only through a new ADR based on measured composition or lifecycle complexity; preference is compile-time safety over runtime reflection if that need arises.