Dependency Injection

The backend uses a container-based DI pattern for service wiring, testability, and loose coupling.

Container Assembly

backend/src/services/create-services.ts exposes two factories:

  • createProductionContainer() — full ServiceContainer with real implementations
  • createTestContainer(overrides?) — noop/mock defaults with per-slice overrides

Container Slices

The ServiceContainer type (backend/src/interfaces/service-container.ts) has four slices:

SliceContains
infrastructurestorage, notificationService
crossCuttingloggerFactory
domainDomain-layer operation modules (I#2013) — sole persistence owners, actor-free, shared by services and job processors as peer clients. First member: fulfillmentFiles
services~70 business services (as of 2026-06)

ProcessorContainer

Job processors see a narrowed view — ProcessorContainer = Pick<ServiceContainer, 'domain' | 'infrastructure' | 'crossCutting'>. No services slice: processors are batch presentation and consume the domain layer directly (see Persistence Topology and Background Jobs).

Injection via Hono Context

Services are injected into Hono route handlers via context variables:

Context VariableAccess PatternContains
c.var.servicesBusiness servicesThe container’s services slice
c.var.containerFull containerinfrastructure, crossCutting, domain, services

Route Access (MANDATORY)

// Always destructure directly
const { documentRequests, portal } = c.var.services;
const { infrastructure } = c.var.container;
 
// NEVER alias
// const svc = c.var.services.documentRequests;

Interface Contracts

Each service implements an interface from backend/src/interfaces/ (71 files, as of 2026-06):

InterfaceService
buildings-service.tsbuildings-service.ts
quote-service.tsquote-service.ts
contact-service.tscontact-service.ts
service-container.tsContainer type definition
database-client.tsDB connection interface
cache-client.tsRedis/cache interface
storage-client.tsFile storage interface

Infrastructure interfaces (database-client.ts, cache-client.ts, storage-client.ts, logger-factory.ts) enable swapping implementations between environments.

Testing Setup

Tests MUST set both container and services on the Hono context:

c.set('container', container);
c.set('services', container.services);

Missing either will cause runtime errors in route handlers.

Benefits

  • Testability — mock any service via its interface, or override slices in createTestContainer
  • Reuse — domain modules work in services AND job processors (peer clients)
  • Loose coupling — services depend on interfaces, not implementations
  • Single assembly pointcreate-services.ts is the only place dependencies are wired
  • Compile-time layeringProcessorContainer makes “processors never call services” a type-level property

See Also