ADR-0004: Shared kernel + Anti-Corruption Layer for inter-module user lookup

Status

Accepted

Tags

architecture, modules, decoupling, ddd, bounded-context, microservice, acl

Decision

Modules must not import each other’s domain packages. Cross-module data access goes through a shared kernel interface in internal/shared/application/port/. The implementing module provides an adapter; wiring happens only in main.go.

Why

Direct module-to-module imports create tight coupling: a breaking change in the user module ripples into every module that imports it, circular dependency risk grows, and independent deployment becomes impossible. The Shared Kernel + ACL pattern breaks this: modules depend on a lightweight interface (no implementation), the owning module provides one adapter, and main.go is the only place that knows which adapter is wired.

Structure

The leave module (and any other consumer) imports only shared/application/port — never the user module.

Trade-offs

Rules for agents

  • Never import another module’s domain/ or application/ package from inside a different module
  • Cross-module data access: define an interface in internal/shared/application/port/, add an adapter in the owning module, wire in main.go
  • The shared kernel contains interfaces and DTOs only — no implementation, no framework imports
  • Only main.go imports module-specific adapters; all other files depend on the shared interface
  • To swap to microservice mode, replace the adapter in main.go with an HTTP/gRPC client that implements the same interface — no other files change

Bad pattern (do not generate)

Good pattern