Skip to content

Refactor: reduce coupling + centralize cloud_mode/config wiring #490

@phernandez

Description

@phernandez

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_enabled and related env/config checks leak into many modules.
  • ConfigManager().config is accessed deep in the call graph, creating hidden coupling, harder-to-test behavior, and import-time dependency chains.
  • deps.py acts 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:

  1. 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)
  2. Refactor: split deps.py into feature modules + reduce import graph #491: split deps.py into feature modules + reduce import graph
  3. Refactor: explicit config injection (only roots read ConfigManager) #496: explicit config injection (only roots read ConfigManager)
  4. Refactor: introduce ProjectResolver and unify project selection across MCP/API/CLI #493: introduce ProjectResolver and unify project selection across MCP/API/CLI
  5. Refactor: extract typed internal API clients for MCP tools #494: extract typed internal API clients for MCP tools
  6. Refactor: move shared orchestration into basic_memory/app/ services; keep adapters thin #495: move shared orchestration into basic_memory/app/ services; keep adapters thin
  7. 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.py
  • basic_memory/mcp/container.py
  • basic_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.py
  • basic_memory/deps/projects.py
  • basic_memory/deps/services.py
  • basic_memory/deps/sync.py
  • basic_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:

  • BasicMemoryConfig explicitly, 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 / ApiClient
  • ProjectResolver
  • FileReader / FileWriter

Tools/routers depend on these interfaces, not concrete implementations.

Acceptance criteria

  • No new direct usages of ConfigManager().config outside composition roots.
  • cloud_mode checks are centralized in one place (mode resolver / containers).
  • deps.py is 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

  1. Add containers (API/MCP/CLI) with minimal changes (wrap existing factories).
  2. Move cloud_mode branching into container wiring; plumb required factories into the few call sites.
  3. Split deps.py into deps/* modules; update imports.
  4. Replace deep ConfigManager calls with injected config/interfaces in the highest-churn modules first.
  5. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions