10 Module Boundaries
Status (2026-10-04 doc reconciliation): after the migration completed the
serviceslayer no longer exists; read the three-layer responsibility descriptions in this doc as migration-period archives. The current dependency direction follows current-architecture.md, while the executable rules remainscripts/module-boundaries.rules(still in force).
This file defines the responsibilities and dependency directions of the handlers, services, and modules layers during the migration phase, supporting boundary governance for M5.
Executable rule sources:
- scripts/module-boundaries.rules
- This is the rule list for the current boundary check
- scripts/check-module-boundaries.sh
- This is the rule executor
Maintenance conventions:
- When adding a newly closed-out module, update
scripts/module-boundaries.rulesfirst - The doc's "currently locked boundaries" should stay consistent with the rule list
- If the doc and the rules conflict, the rule list and CI results win; backfill the doc afterwards
Target rules
handlers/*- Depend only on
modules/*/deliverycontracts, a few platform interfaces, and request/response DTOs - Do not depend directly on concrete legacy services as the default business entrypoint
- Depend only on
app/server/*- Owns runtime assembly
- May hold legacy concrete services, but HTTP injection should prefer exposing
modules/*/deliverycontracts - Route registration and runtime assembly should be split into files by domain or access surface, avoiding re-concentration into a single giant entrypoint file
services/*- Retained temporarily only when unmigrated runtime glue, state synchronization, or side-effect assembly genuinely remains
- Once closed out and no longer serving a necessary role, delete it outright instead of letting it grow into the default business center
modules/*/application- Owns new core business logic
modules/*/delivery- Owns handler-facing / runtime-facing adapters and contracts
Allowed dependency directions
handlers -> modules/*/deliveryapp/server -> modules/*/deliveryapp/server -> services/*services/* -> modules/*/applicationservices/* -> modules/*/deliverymodules/*/delivery -> modules/*/applicationmodules/*/application -> modules/*/domain|infra
Patterns forbidden from spreading further
handlers -> *services.ConcreteTypehandlers -> gorm.DBhandlers -> modules/*/applicationassembling business logic directlyservices/*becoming the default handler entrypoint again for new requirements- The runtime adding another legacy entrypoint parallel to an existing module contract
- Re-piling cross-domain assembly logic into a single
router.goorruntime.gounderapp/server
Currently locked boundaries
ticket- The HTTP entrypoint must go through
modules/ticket/delivery.HandlerService
- The HTTP entrypoint must go through
agent- The HTTP entrypoint must go through
modules/agent/delivery.HandlerService
- The HTTP entrypoint must go through
analytics- The HTTP entrypoint must go through
modules/analytics/delivery.HandlerService
- The HTTP entrypoint must go through
routing / session transfer- The HTTP entrypoint must go through
modules/routing/delivery.HandlerService
- The HTTP entrypoint must go through
ai- The HTTP / health / metrics entrypoints must go through
modules/ai/delivery.HandlerService
- The HTTP / health / metrics entrypoints must go through
customer- The HTTP entrypoint must go through
modules/customer/delivery.HandlerService
- The HTTP entrypoint must go through
automation- The HTTP entrypoint must go through
modules/automation/delivery.HandlerService
- The HTTP entrypoint must go through
knowledge- The HTTP entrypoint must go through
modules/knowledge/delivery.HandlerService
- The HTTP entrypoint must go through
suggestion- The HTTP entrypoint must go through
modules/suggestion/delivery.HandlerService
- The HTTP entrypoint must go through
gamification- The HTTP entrypoint must go through
modules/gamification/delivery.HandlerService
- The HTTP entrypoint must go through
conversation- The websocket persistence entrypoint must go through
modules/conversation/delivery.WebSocketMessageWriter
- The websocket persistence entrypoint must go through
- handler-local contracts
- For thin capabilities not yet fully modularized —
satisfaction,macro,app integration,custom field,shift,sla— the HTTP handler and router/runtime surface must stay on handler-local contracts, avoiding regression to concrete legacy services
- For thin capabilities not yet fully modularized —
Corresponding rules:
- The
handler/runtimerules are recorded inscripts/module-boundaries.rules - The websocket persistence rule for
conversationand the dual-entrypoint rules forAIAssemblyare also recorded in the same rule file scripts/check-module-boundaries.shadditionally import-scans non-test handlers, forbidding direct connections tomodules/*/application,modules/*/infra, andgorm
Legacy Service Freeze Policy
- Capabilities already in
stabilized:- legacy
services/*only allows necessary runtime glue, state synchronization, and side-effect assembly changes - New business logic goes into
modules/*/applicationfirst - New handler/router wiring must not regress to concrete services
- If a legacy service no longer has a necessary role, delete it rather than keeping an empty-shell facade
- legacy
- Capabilities still in
legacy:- Temporary changes are allowed, but every addition of business logic should be annotated with its target module
Retirement conditions
A legacy service can enter the "retirement candidate" state only if at least the following hold:
- HTTP handlers all depend on
modules/*/delivery - Runtime injection no longer needs to expose concrete legacy types to the router
- Key compatibility responsibilities have been absorbed by adapters or the platform layer
- Tests cover the key behaviors before and after the migration
- The scorecard status reaches
stabilizedand stays regression-free for a period