-
Notifications
You must be signed in to change notification settings - Fork 148
Description
Refactor: reduce coupling + tame config/cloud_mode tentacles (SOLID wiring)
Problem
The current wiring/config surface area has become a sprawling tentacle across the codebase:
BasicMemoryConfig.cloud_mode_enabledand related env/config checks leak into many modules.ConfigManager().configis accessed deep in the call graph, creating hidden coupling, harder-to-test behavior, and import-time dependency chains.deps.pyacts as a global service locator across features (DB/services/sync/etc.), increasing the import graph and making changes ripple.- Entry points (API / MCP / CLI) don’t fully “own” composition, so infra/runtime concerns (local vs cloud, auth vs none, transports) seep into domain-ish code.
This makes the system less SOLID:
- SRP: modules mix business logic + transport/config/runtime selection.
- DIP: higher-level code imports concrete implementations and config man
- OCP: adding a mode (cloud/local/test) requires touching many places.
Goals
- Make runtime-mode selection (cloud/local/test) an explicit concern of composition roots (entrypoints), not scattered checks.
- Reduce coupling by depending on narrow interfaces/Protocols rather than concrete services/config managers.
- Keep behavior stable while making refactors incremental and test-driven.
Recommended ordering (roadmap)
This issue is the umbrella/epic. Suggested incremental order:
- Refactor: add composition roots (api/container.py, mcp/container.py, cli/container.py) #492: add composition roots (api/container.py, mcp/container.py, cli/container.py)
- Refactor: split deps.py into feature modules + reduce import graph #491: split
deps.pyinto feature modules + reduce import graph - Refactor: explicit config injection (only roots read ConfigManager) #496: explicit config injection (only roots read ConfigManager)
- Refactor: introduce ProjectResolver and unify project selection across MCP/API/CLI #493: introduce ProjectResolver and unify project selection across MCP/API/CLI
- Refactor: extract typed internal API clients for MCP tools #494: extract typed internal API clients for MCP tools
- Refactor: move shared orchestration into basic_memory/app/ services; keep adapters thin #495: move shared orchestration into
basic_memory/app/services; keep adapters thin - Refactor: sync/watch lifecycle via SyncCoordinator #497: sync/watch lifecycle via SyncCoordinator
Notes:
- Steps 1–3 are foundational wiring/coupling reductions; later steps plug into the new structure cleanly.
- Keep this work split across small PRs.
Proposed approach (incremental)
1) Introduce composition roots per entrypoint
Add dedicated “containers” that build dependencies for each world:
basic_memory/api/container.pybasic_memory/mcp/container.pybasic_memory/cli/container.py
Rule: only the composition root reads ConfigManager and environment variables.
2) Split deps.py into feature-scoped modules
Replace the single large deps.py with:
basic_memory/deps/db.pybasic_memory/deps/projects.pybasic_memory/deps/services.pybasic_memory/deps/sync.pybasic_memory/deps/http.py
Routers/tools import only what they need.
3) Centralize mode selection (cloud/local/test)
Create a small module/class (e.g. RuntimeMode / RuntimeResolver) that resolves:
- cloud vs local
- auth vs none
- base URLs/transports
Expose the result (mode +red factories) to the app via container wiring.
4) Explicit config injection (minimize ConfigManager().config usage)
Gradually migrate modules to accept either:
BasicMemoryConfigexplicitly, or- a narrow interface (Protocol) / dataclass with only required fields.
Keep ConfigManager usage at the boundary.
5) Add narrow interfaces at boundaries (Protocols)
Examples:
HttpClientFactory/ApiClientProjectResolverFileReader/FileWriter
Tools/routers depend on these interfaces, not concrete implementations.
Acceptance criteria
- No new direct usages of
ConfigManager().configoutside composition roots. cloud_modechecks are centralized in one place (mode resolver / containers).deps.pyis split or reduced to a thin re-export layer (eventually removed).- MCP tools remain high-coverage; coverage stays at 100% (practical) across the MCP layer.
- All tests continue to pass in SQLite mode; Postgres-specific tests still run under
BASIC_MEMORY_TEST_POSTGRES=1.
Suggested implementation plan
- Add containers (API/MCP/CLI) with minimal changes (wrap existing factories).
- Move
cloud_modebranching into container wiring; plumb required factories into the few call sites. - Split
deps.pyintodeps/*modules; update imports. - Replace deep
ConfigManagercalls with injected config/interfaces in the highest-churn modules first. - Follow-up cleanup: remove transitional shims.
Notes
This should be done in small PRs to avoid risky rewrites. The existing integration-heavy test suite is a good safety net for this refactor.