10 Migration Inventory
Status (2026-10-04 doc reconciliation): The migration is complete — the
internal/servicesdirectory has been removed entirely and all business capabilities are closed out underinternal/modules/*(capped at 27 top-level modules). This doc is a migration-history archive of the M1 inventory phase; the "closeout complete" conclusions per capability have been delivered.
This file records the migration state of handlers -> services -> modules (M1 inventory snapshot), supporting the M1 inventory phase of 10-service-to-module-migration.
Current observations
ticket- Already has a fairly strong modularization base
handlers/ticket_handler.goworks through theticketHandlerServiceinterface- A module delivery adapter layer already exists at
modules/ticket/delivery/handler_adapter.go - The runtime already uses
modules/ticket/delivery.NewHandlerServiceWithDependencies(...)directly - The old
services/TicketServiceis deleted - Current rule: HTTP handlers should depend on
modules/ticket/delivery.HandlerService - Conclusion:
tickethas completed main-path closeout and finished by deleting the legacy service
agentservices/AgentServicehas clearly become a compatibility layer- Core reads/writes go through
modules/agent/application.Service - The handler-facing DTO and transfer runtime contract have been recovered into
modules/agent/delivery agentQueuesis removed; the remaining online-agent compatibility cache backfill, background cleanup scheduling, and service assembly are split into standalone runtime helpers/adapters/assembly, and background jobs now start explicitly only at the server runtime assembly point- Downstream services such as
workspace/session transfernow depend on narrow interfaces instead of binding directly to*AgentService - The
app/serverruntime/router no longer exposes*services.AgentServicefields either, keeping only handler-facing and business-specific dependencies - The
workspace handlerandwebsocket hubhave further switched to workspace/transfer interfaces, continuing to shrinkapp/server's concrete exposure ofWorkspaceService/SessionTransferService - Current rule: HTTP handlers should depend on
modules/agent/delivery.HandlerService - Conclusion: falls into the "module landed, but runtime compatibility state not yet closed out" category
analyticsservices/StatisticsServiceis basically a module facade- Core statistics capabilities go through
modules/analytics/application.Service - It still keeps an event bus subscription and DTO-mapping compatibility wrapper
- The
app/serverruntime no longer exposes*services.StatisticsServicefields, keeping only the handler-facing contract and local wiring - Conclusion: low migration risk; a good template for later closeouts
routing / session transfer- The main runtime no longer assembles
services.SessionTransferService - handler/runtime already uses
modules/routing/delivery.HandlerServiceAdapterdirectly - Session assignment sync goes through the
modules/conversation/deliveryruntime adapter and no longer updatesSessiondirectly - Transfer / waiting system message writes go through the
modules/conversation/deliveryruntime adapter and no longer writeMessagedirectly - Waiting queue reads, inserts, cancels, queries, and status synchronization, plus transfer record writes, are closed out into the routing module state machine
- waiting -> transferred and the transfer record write are inside the same transaction, no longer relying on post-transaction patch-up sync
- The system message write on waiting queue cancel has also switched to the
modules/conversation/deliveryruntime adapter, reducing legacyMessagedirect-write branches - The ticket assignment sync triggered by session transfer goes through the
modules/ticket/deliveryruntime adapter and no longer writesTicket/TicketStatusdirectly - Agent load adjustments go through the
modules/agent/deliveryruntime adapter; the import cycle previously caused by the handler contract back-depending onserviceshas been removed - The websocket notification dependency is narrowed to
sessionTransferNotifier;WebSocketHubis no longer kept as a concrete field inside the service - The session read entrypoint has switched to
modules/conversation/delivery.RuntimeService.LoadTransferSession(...) - Under the default assembly, adapter errors from
LoadTransferSession(...)propagate directly instead of silently falling back to legacy DB queries, avoiding masking conversation runtime failures - The session active/unassigned sync when joining the waiting queue has also switched to
modules/conversation/delivery.RuntimeService.SyncWaitingAssignment(...) - Websocket notifications are narrowed to a notifier interface;
WebSocketHubis no longer kept as an internal concrete dependency - The old
services/SessionTransferServiceis deleted; the leftoverservices/session_transfer_legacy_store.go(a DB compatibility read/write orphan with no production callers) is also deleted - Conclusion:
routing / session transferhas completed main-path closeout and finished by deleting the legacy service
- The main runtime no longer assembles
ai- The old
services/AIServiceand the enhanced AI implementation still exist, but are no longer exposed to the handler/runtime top level throughAIServiceInterface - The handler has started closing out to
modules/ai/delivery.HandlerService AIAssemblyhas dropped the unconsumedLegacyService *services.AIServiceexposure, keeping only the handler-facing contract, the runtime-facingRuntimeService, and knowledge provider state- The old
services/AIServiceInterface/services.OrchestratedAIServiceare deleted services/router.goandservices/websocket.godepend only on a local minimal AI runtime interface- Conclusion:
aihas completed handler/runtime main-path closeout; the follow-up focus is continuing to shrink the local runtime interfaces
- The old
legacy handler-only capabilities- Capabilities such as
satisfaction,csat public,macro,app integration,custom field, andshiftare not yet modularized - These handlers now depend on minimal interfaces defined inside the
handlerspackage, instead of exposing*services.*concrete types to theapp/serverruntime/router surface - Conclusion: although not yet completed
services -> modulesmigration items, they have gone through a round of "assembly-surface closeout", reducing the spread of concrete legacy services across the top-level runtime
- Capabilities such as
message router- Both the lightweight runtime and the main runtime still need the
Start/Stoplifecycle, but the HTTP layer only cares about platform statistics reads services.MessageRouterRuntimehas been extracted;app/serverandhandlers.MessageHandlerno longer expose the concrete*services.MessageRouter- The unused message router injection into
MetricsHandlerhas been removed
- Both the lightweight runtime and the main runtime still need the
realtime runtime internalsWebSocketHub/WebRTCServiceare runtime assembly details and should not keep being exposed as public concrete fields ofRuntime/RealtimeRuntime- They are now closed into internal
Run()startup dependencies and gateway adapter assembly details; externally onlyRealtimeGateway/RTCGateway/MessageRouterRuntimeremain
slaSLAHandlerhas long worked internally against a minimal method set, but theapp/servertop-level assembly still exposed*services.SLAService- The handler contract has now been explicitly promoted to
handlers.SLAService/handlers.SLATicketReader ticketdelivery.ReaderServiceAdapteris reused directly as the ticket-read dependency; the top-level runtime/router no longer exposes the concrete*services.SLAService
customerservices/CustomerServiceis already a lightweight facade overmodules/customer/application.Service- The HTTP handler consumes only request/response DTOs and compatibility methods; core business has sunk into module application + infra repository
- The
app/serverruntime no longer exposes*services.CustomerServicefields, and customer handler assembly has switched directly tomodules/customer/delivery.NewHandlerService(db), no longer relaying through the legacy facade - Conclusion: the handler main path sits directly on the module; the old facade mainly keeps DTO compatibility for historical callers
automationservices/AutomationServicehas sunk the core triggers and execution queries intomodules/automation/application.Service- The legacy service mainly retains event bus subscriber registration, test-compatibility helpers, and module assembly
- The
app/serverruntime/router no longer exposes*services.AutomationServicefields, and automation handler assembly has switched directly tomodules/automation/delivery.NewHandlerService(db) - Conclusion: the handler main path sits directly on the module; the old service mainly retains the event bus subscriber and test-compatibility glue
knowledgemodules/knowledge/applicationexists, but previously lacked a Gorm repository and a handler-facing delivery contract- The old
services/KnowledgeDocServicemainly carries CRUD and tag/category DTOs and fits being turned into a module facade - The
app/serverruntime no longer exposes*services.KnowledgeDocServicefields; knowledge handler assembly has switched directly tomodules/knowledge/delivery.NewHandlerServiceWithProvider(db, ...), no longer relaying through the legacy facade modules/knowledge/infrahas filled in the Gorm persistence repository forKnowledgeIndexJob, and module delivery now supports runtime-injected providers- Conclusion: knowledge's handler/runtime main paths both sit close to the module; the legacy facade retains only historical-caller compatibility
suggestionmodules/suggestion/application,contract,infra, anddeliveryhave landed- handler/runtime already consumes
modules/suggestion/delivery.HandlerServicedirectly - The old
services/SuggestionServiceis deleted; DTO aliases and helpers have all sunk into module application / contract - Conclusion:
suggestionhas completed main-path closeout and finished by deleting the legacy service
gamificationmodules/gamification/application,contract,infra, anddeliveryhave landed- handler/runtime already consumes
modules/gamification/delivery.HandlerServicedirectly - The old
services/GamificationServiceis deleted; points / badge logic and DTO aliases have all sunk into module application / contract - Conclusion:
gamificationhas completed main-path closeout and finished by deleting the legacy service
Grouped by migration maturity
A. A clear module facade already exists
ticketagentanalyticscustomerautomationknowledgesuggestiongamification
Common traits of these modules:
modules/*/applicationalready serves as the core business entrypoint- Old
services/*mostly carry compatibility wrappers, DTO mapping, and runtime glue - They suit defining the "single entrypoint" first and freezing new logic in old services
B. Module adapter wired in, but runtime hardening still needed
- websocket/runtime paths related to
conversation
Common traits of these modules:
- The module exists
- Key persistence entrypoints have switched to the module delivery adapter
- Runtime objects such as the connection hub, notifications, and transfer coordination still need to stay behind narrow interfaces
- The follow-up focus is avoiding reintroducing business direct-write paths inside the realtime runtime, so the completed migration does not regress
C. Multiple implementations coexist; the default main path must be decided first
- Atypical
services -> modulescapabilities such asvoice
Traits:
- They do not fully follow this round's
services -> modulesmigration template - The next step looks more like boundary tightening and runtime assembly cleanup than simple call-site replacement
Recommended migration order
ticketagentanalyticscustomerautomationknowledgesuggestiongamification- Continuously harden
conversation / websocket runtime - Expand scorecard coverage
Current high-risk points
- The same business capability has multiple entry layers at once: handler interface, legacy service, module service, delivery adapter
- Old services still mix in runtime state and side effects, preventing simple replacement
- The
aipath still has multiple provider / enhanced implementations; without continued narrowing, the runtime contract could fork again conversation's main persistence entrypoint is closed out, but the realtime connection hub remains a core runtime object; business write paths must keep being kept from flowing back- Without continued guarding, un-modularized but thin legacy handler services could spread concrete types back into the
app/serverruntime/router
Next-step suggestions
- Keep holding the line on the
conversationruntime write paths; avoid bypassing module contracts - Clarify which of the remaining
services/*types are runtime state holders / event bus glue / historical compatibility entrypoints - Split minimal task packages for capabilities still in
legacyand record them in the scorecard in sync
Confirmed entrypoint rules
ticket- HTTP handler entrypoint:
modules/ticket/delivery.HandlerService - Old
services/TicketServicestatus: deleted
- HTTP handler entrypoint:
agent- HTTP handler entrypoint:
modules/agent/delivery.HandlerService - Old
services/AgentServicerole: compatibility facade + runtime state holder
- HTTP handler entrypoint:
analytics- HTTP handler entrypoint:
modules/analytics/delivery.HandlerService - Old
services/StatisticsServicerole: compatibility facade + event bus subscriber / DTO mapping glue
- HTTP handler entrypoint:
customer- HTTP handler entrypoint:
modules/customer/delivery.HandlerService - Old
services/CustomerServicerole: compatibility facade + DTO mapping + module application assembly
- HTTP handler entrypoint:
automation- HTTP handler entrypoint:
modules/automation/delivery.HandlerService - Old
services/AutomationServicerole: compatibility facade + event bus subscriber + module application assembly
- HTTP handler entrypoint:
knowledge- HTTP handler entrypoint:
modules/knowledge/delivery.HandlerService - Runtime assembly entrypoint:
modules/knowledge/delivery.NewHandlerServiceWithProvider(db, ...) - Old
services/KnowledgeDocServicerole: compatibility facade + module application / repository assembly
- HTTP handler entrypoint:
suggestion- HTTP handler entrypoint:
modules/suggestion/delivery.HandlerService - Runtime assembly entrypoint:
modules/suggestion/delivery.NewHandlerService(db) - Old
services/SuggestionServicestatus: deleted
- HTTP handler entrypoint:
gamification- HTTP handler entrypoint:
modules/gamification/delivery.HandlerService - Runtime assembly entrypoint:
modules/gamification/delivery.NewHandlerService(db) - Old
services/GamificationServicestatus: deleted
- HTTP handler entrypoint:
routing / session transfer- HTTP handler entrypoint:
modules/routing/delivery.HandlerService - Old
services/SessionTransferServicestatus: deleted
- HTTP handler entrypoint:
conversation / websocket runtime- Websocket persistence entrypoint:
modules/conversation/delivery.WebSocketMessageWriter services/WebSocketHubrole: runtime connection hub; no longer defines its own conversation-private persistence interface
- Websocket persistence entrypoint:
ai- HTTP handler entrypoint:
modules/ai/delivery.HandlerService AIAssemblyrole: explicitly separates the handler-facing AI contract from the runtime-facingRuntimeService
- HTTP handler entrypoint:
Confirmed compatibility responsibility boundaries
services/AgentService- Allowed to retain: compatibility entrypoints for old callers, a few compatibility methods
- Must not add: new direct HTTP handler dependencies, core business write paths outside
modules/agent/application.Service
services/StatisticsService- Allowed to retain: compatibility entrypoints for old callers, event bus subscription registration, DTO mapping, statistics background job scheduling
- Must not add: new direct HTTP handler dependencies, main statistics read/write paths that bypass
modules/analytics/application.Service
services/CustomerService- Allowed to retain: compatibility entrypoints for old callers, DTO mapping, assembly of
modules/customer/application.Serviceand repositories - Must not add: new direct HTTP handler dependencies, core customer read/write paths that bypass
modules/customer/application.Service
- Allowed to retain: compatibility entrypoints for old callers, DTO mapping, assembly of
services/AutomationService- Allowed to retain: compatibility entrypoints for old callers, event bus subscriber registration, test helper methods, assembly of
modules/automation/application.Service - Must not add: new direct HTTP handler dependencies, main trigger and execution-record entrypoints that bypass
modules/automation/application.Service
- Allowed to retain: compatibility entrypoints for old callers, event bus subscriber registration, test helper methods, assembly of
services/KnowledgeDocService- Allowed to retain: compatibility entrypoints for old callers, DTO mapping, compatibility assembly of
modules/knowledge/application.Serviceand repositories - Must not add: new direct HTTP handler dependencies, main document CRUD entrypoints that bypass
modules/knowledge/application.Service
- Allowed to retain: compatibility entrypoints for old callers, DTO mapping, compatibility assembly of
services/WebSocketHub- Allowed to retain: connection management, broadcast, protocol message dispatch, runtime glue for AI/transfer
- Must not add: new conversation-private persistence interfaces, message persistence paths that bypass
modules/conversation/delivery.WebSocketMessageWriter
- legacy AI types under
services- Allowed to retain: provider implementations, the enhanced chain, local runtime coordination
- Must not add: new direct HTTP handler dependencies, re-exposing wide interfaces like the global
AIServiceInterface
Current automated guards
scripts/check-module-boundaries.sh- Verifies that handler constructors for
ticket/agent/analytics/customer/automation/knowledge/routing/aimust depend onmodules/*/delivery.HandlerService - Verifies that router/runtime injection types for these eight modules must stay on the handler-facing contract, that the
knowledgeruntime must be assembled directly through module delivery, and that theconversationwebsocket persistence entrypoint must go through the module delivery adapter - Verifies that thin handler dependencies such as
satisfaction/macro/app integration/custom field/shiftmust stay on handler-local contracts, preventing theapp/servertop-level assembly from regressing to expose concrete legacy services - Verifies that handler/runtime entrypoints for
suggestion/gamificationmust stay on module delivery contracts - Verifies that newly narrowed points such as
workspace/websocket transfermust depend on interfaces likeWorkspaceOverviewReaderandSessionTransferRuntime, and forbids theapp/serverruntime/router from regressing to expose several concrete legacy services - Purpose: lock down the entrypoints of completed migrations first, avoiding regression to handlers wired directly to the old concrete services
- Verifies that handler constructors for