Skip to content

Architecture: decompose Rust ACP and DB god-modules into bounded domain modules #145

@flazouh

Description

@flazouh

Architecture: decompose Rust ACP and DB god-modules into bounded domain modules

Problem

The Rust backend contains several giant files that act as convenience containers for multiple concerns instead of mapping cleanly to product domains.

The two biggest examples are:

  • packages/desktop/src-tauri/src/acp/client/cc_sdk_client.rs (~6717 lines)
  • packages/desktop/src-tauri/src/db/repository.rs (~3330 lines)

These files are evidence that important architectural seams are still implicit.

Why this is a deep problem

Large files are not automatically bad.

They become a smell when a single module mixes responsibilities that should evolve independently.

That is the case here:

  • permissions evolve for different reasons than streaming
  • hooks evolve for different reasons than question handling
  • repositories for projects/settings/session metadata/skills/sql studio evolve for different reasons

When those concerns share a module:

  • unrelated changes collide
  • tests become coarse-grained
  • ownership becomes fuzzy
  • mental load spikes
  • it becomes hard to define stable internal APIs

Evidence in the current codebase

cc_sdk_client.rs

This file contains, among other things:

  • pending question state
  • tool call ID tracking
  • approval callback tracking
  • permission handler implementation
  • hook callback implementation
  • main ClaudeCcSdkClient implementation
  • streaming bridge logic
  • AgentClient implementation
  • large inline test section

Representative markers:

  • struct PendingQuestionState
  • struct ToolCallIdTracker
  • impl cc_sdk::CanUseTool for AcepePermissionHandler
  • struct AcepePermissionRequestHook
  • impl cc_sdk::HookCallback for AcepePermissionRequestHook
  • impl ClaudeCcSdkClient
  • async fn run_streaming_bridge(...)
  • impl AgentClient for ClaudeCcSdkClient

db/repository.rs

This file contains multiple repositories for different domains:

  • ProjectRepository
  • SettingsRepository
  • AppSettingsRepository
  • SessionReviewStateRepository
  • SessionProjectionSnapshotRepository
  • SessionTranscriptSnapshotRepository
  • SessionThreadSnapshotRepository
  • SessionJournalEventRepository
  • SessionMetadataRepository
  • SkillsRepository
  • DatabaseResetRepository
  • SqlStudioRepository

This is not one repository. It is a repository index disguised as a module.

Current shape

cc_sdk_client.rs
   |
   +--> permissions
   +--> hooks
   +--> question handling
   +--> tool ID tracking
   +--> stream bridging
   +--> client lifecycle
   +--> tests

repository.rs
   |
   +--> projects
   +--> settings
   +--> app settings
   +--> review state
   +--> snapshots
   +--> journal events
   +--> session metadata
   +--> skills
   +--> database reset
   +--> SQL studio

GOD architecture target

The cleanest backend architecture should mirror stable product concepts.

acp/
  session_lifecycle/
  streaming/
  permissions/
  interactions/
  tools/
  providers/

db/
  projects/
  settings/
  session_metadata/
  session_journal/
  snapshots/
  skills/
  sql_studio/

And the client-side ACP internals should look more like:

ClaudeCcSdkClient
      |
      +--> session runtime coordinator
      +--> streaming adapter
      +--> permission bridge
      +--> interaction bridge
      +--> projection/event dispatcher

where each box is a real module with a narrow contract.

What should exist after this work

1. ACP modules with explicit domain ownership

Suggested boundaries:

  • acp/client/claude/session_runtime.rs
  • acp/client/claude/streaming_bridge.rs
  • acp/client/claude/permission_bridge.rs
  • acp/client/claude/question_bridge.rs
  • acp/client/claude/tool_tracking.rs

2. Repository modules by aggregate/domain

Suggested boundaries:

  • db/repositories/projects.rs
  • db/repositories/settings.rs
  • db/repositories/session_metadata.rs
  • db/repositories/session_journal.rs
  • db/repositories/skills.rs
  • db/repositories/sql_studio.rs

3. Stable internal interfaces

Examples:

  • streaming bridge emits canonical updates/events
  • permission bridge owns permission-specific state
  • question bridge owns question binding logic
  • repositories expose domain-oriented methods without a catch-all mega module

Suggested implementation direction

  1. Split cc_sdk_client.rs by responsibility, not by arbitrary line ranges.
  2. Move tests next to the extracted modules or to focused integration tests.
  3. Split repository.rs into domain repositories with a shared support module only where necessary.
  4. Introduce module-level contracts that make dependencies directional and explicit.
  5. Keep thin facade modules if external call sites need a transitional import path.

Non-goals

  • file splitting that keeps all hidden coupling intact
  • moving code without clarifying API ownership
  • creating a deep folder tree without meaningful boundaries

Acceptance criteria

  • cc_sdk_client.rs no longer owns permissions, hooks, streaming, tracking, and lifecycle in one file.
  • db/repository.rs is replaced by domain-scoped repository modules or reduced to a small index/facade.
  • Each extracted module has a clear responsibility and corresponding tests.
  • It becomes possible to work on permissions/streaming/repositories with much lower unrelated context load.

Why this matters strategically

Acepe's backend is becoming the control plane for agent execution, reviewability, and durable workflows. That requires backend modules that are structurally aligned with the product, not just organized around what happened to be convenient during growth.

Metadata

Metadata

Assignees

No one assigned

    Labels

    cleanupCode cleanup and maintenanceenhancementNew feature or requestrustRust backend

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions