Refactor AppContext into scoped dependency structs per module #1

Closed
opened 2026-06-09 00:33:13 +00:00 by GKaszewski · 0 comments
Owner

Problem

AppContext is a god object — every use case receives 20+ repositories and 8+ services even if it only needs 2.

pub struct AppContext {
    pub repos: Repositories,    // 20+ fields
    pub services: Services,     // 8+ fields
    pub config: AppConfig,
}

This means:

  • A use case's real dependencies are invisible from its signature
  • Tests build a full context even when testing one repo interaction
  • Adding a new repo forces recompilation of everything that touches AppContext

Proposed solution

Replace AppContext with scoped dependency structs per module. Each use case module declares a slim struct with only its dependencies:

pub struct GoalsDeps {
    pub goal: Arc<dyn GoalRepository>,
    pub events: Arc<dyn EventPublisher>,
}

pub struct DiaryDeps {
    pub movie: Arc<dyn MovieRepository>,
    pub review: Arc<dyn ReviewRepository>,
    pub watchlist: Arc<dyn WatchlistRepository>,
    pub metadata: Arc<dyn MetadataClient>,
    pub events: Arc<dyn EventPublisher>,
}

Composition roots (worker/presentation main.rs) build these from the master set of Arcs.

Notes

  • enrich_movie and request_enrichment already use individual Arc params (intentionally — called from adapters, not handlers). The scoped-struct approach is the middle ground.
  • This touches every use case, handler, composition root, and test — do it in one focused PR.
  • The ReviewLogger trait extraction (done) was a step in this direction.
## Problem `AppContext` is a god object — every use case receives 20+ repositories and 8+ services even if it only needs 2. ```rust pub struct AppContext { pub repos: Repositories, // 20+ fields pub services: Services, // 8+ fields pub config: AppConfig, } ``` This means: - A use case's real dependencies are invisible from its signature - Tests build a full context even when testing one repo interaction - Adding a new repo forces recompilation of everything that touches AppContext ## Proposed solution Replace `AppContext` with **scoped dependency structs per module**. Each use case module declares a slim struct with only its dependencies: ```rust pub struct GoalsDeps { pub goal: Arc<dyn GoalRepository>, pub events: Arc<dyn EventPublisher>, } pub struct DiaryDeps { pub movie: Arc<dyn MovieRepository>, pub review: Arc<dyn ReviewRepository>, pub watchlist: Arc<dyn WatchlistRepository>, pub metadata: Arc<dyn MetadataClient>, pub events: Arc<dyn EventPublisher>, } ``` Composition roots (worker/presentation main.rs) build these from the master set of Arcs. ## Notes - `enrich_movie` and `request_enrichment` already use individual Arc params (intentionally — called from adapters, not handlers). The scoped-struct approach is the middle ground. - This touches every use case, handler, composition root, and test — do it in one focused PR. - The `ReviewLogger` trait extraction (done) was a step in this direction.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: GKaszewski/movies-diary#1