ADR-0004: Shared kernel + Anti-Corruption Layer for inter-module user lookup
Status
AcceptedTags
architecture, modules, decoupling, ddd, bounded-context, microservice, aclDecision
Modules must not import each other’s domain packages. Cross-module data access goes through a shared kernel interface ininternal/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, andmain.go is the only place that knows which adapter is wired.
Structure
shared/application/port — never the user module.
Trade-offs
| True loose coupling | No direct module dependencies |
| Microservice-ready | Swap adapter from PostgreSQL → gRPC without touching business logic |
| Easy to test | Mock the shared interface; no module dep in test |
| One more indirection | Slightly more files per cross-module dependency |
Rules for agents
- Never
importanother module’sdomain/orapplication/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 inmain.go - The shared kernel contains interfaces and DTOs only — no implementation, no framework imports
- Only
main.goimports module-specific adapters; all other files depend on the shared interface - To swap to microservice mode, replace the adapter in
main.gowith an HTTP/gRPC client that implements the same interface — no other files change