Overview
Currently, the UserOutput type is instantiated in multiple places throughout the presentation layer, which can lead to inconsistent user-facing messages with different verbosity levels, styles, or formatting. This task centralizes UserOutput instantiation in the application bootstrap phase and passes it down through the command execution chain using dependency injection via Arc<UserOutput>.
Problem: Decentralized construction creates inconsistency risk in:
src/presentation/commands/mod.rs - Error handler creates its own instance
src/presentation/commands/create/subcommands/template.rs - Template generation creates its own instance
src/presentation/commands/context.rs - Command context creates its own instance
Solution: Bootstrap a single UserOutput instance during application initialization and pass it down using Arc<UserOutput> for shared ownership.
Specification
See detailed specification: docs/issues/107-centralize-user-output-via-dependency-injection.md
🏗️ Architecture Requirements
DDD Layer: Infrastructure (Container), Presentation (usage)
Module Path:
- Container:
src/bootstrap/container.rs (new module)
- Bootstrap integration:
src/bootstrap/app.rs (existing)
- Presentation layer: Multiple files
Pattern: Dependency Injection Container + Function Parameter Threading
Module Structure Requirements
Architectural Constraints
Anti-Patterns to Avoid
- ❌ Global mutable state - Don't use
static mut or lazy_static! for UserOutput
- ❌ Service locator pattern - Don't create a global registry for dependency lookup
- ❌ Hidden dependencies - All functions should explicitly declare
Arc<UserOutput> parameter
- ❌ Layer violations - Don't pass container into domain or application layers
Implementation Plan
Phase 1: Create Bootstrap Container (1-2 hours)
Phase 2: Integrate Container in Bootstrap (1 hour)
Phase 3: Update Presentation Layer Signatures (2-3 hours)
Phase 4: Remove Local UserOutput Constructions (1-2 hours)
Phase 5: Update Tests (2-3 hours)
Phase 6: Documentation and Cleanup (1 hour)
Acceptance Criteria
Note for Contributors: These criteria define what the PR reviewer will check. Use this as your pre-review checklist before submitting the PR to minimize back-and-forth iterations.
Quality Checks:
Architecture Checks:
Behavior Checks:
Testing Checks:
Documentation Checks:
Related
Overview
Currently, the
UserOutputtype is instantiated in multiple places throughout the presentation layer, which can lead to inconsistent user-facing messages with different verbosity levels, styles, or formatting. This task centralizesUserOutputinstantiation in the application bootstrap phase and passes it down through the command execution chain using dependency injection viaArc<UserOutput>.Problem: Decentralized construction creates inconsistency risk in:
src/presentation/commands/mod.rs- Error handler creates its own instancesrc/presentation/commands/create/subcommands/template.rs- Template generation creates its own instancesrc/presentation/commands/context.rs- Command context creates its own instanceSolution: Bootstrap a single
UserOutputinstance during application initialization and pass it down usingArc<UserOutput>for shared ownership.Specification
See detailed specification: docs/issues/107-centralize-user-output-via-dependency-injection.md
🏗️ Architecture Requirements
DDD Layer: Infrastructure (Container), Presentation (usage)
Module Path:
src/bootstrap/container.rs(new module)src/bootstrap/app.rs(existing)Pattern: Dependency Injection Container + Function Parameter Threading
Module Structure Requirements
Containertype insrc/bootstrap/container.rsfor centralized service initializationsrc/bootstrap/app.rsafter logging initializationArc<UserOutput>through presentation layer function signaturesUserOutputconstruction sites to use injected dependencyArchitectural Constraints
Arc<UserOutput>for shared ownership, not global variablesUserOutputmust be thread-safe (already is withArc)Anti-Patterns to Avoid
static mutorlazy_static!forUserOutputArc<UserOutput>parameterImplementation Plan
Phase 1: Create Bootstrap Container (1-2 hours)
src/bootstrap/container.rswithContainertypeArc<UserOutput>field toContainernew()anduser_output()methodssrc/bootstrap/mod.rsto exportContainerContainerconstruction anduser_output()accessPhase 2: Integrate Container in Bootstrap (1 hour)
Containerinsrc/bootstrap/app.rsafter logging initializationcontainer.user_output()topresentation::execute()container.user_output()topresentation::handle_error()Phase 3: Update Presentation Layer Signatures (2-3 hours)
presentation::execute()signature to acceptArc<UserOutput>presentation::handle_error()signature to acceptArc<UserOutput>create::handle_create_command()signaturedestroy::handle_destroy_command()signaturecreate/subcommands/anddestroy/subcommands/Phase 4: Remove Local UserOutput Constructions (1-2 hours)
UserOutput::new()call inpresentation::handle_error()UserOutput::new()call increate/subcommands/template.rsCommandContext::new()to acceptuser_output: Arc<UserOutput>parameterCommandContext::new()call sites to pass injecteduser_outputUserOutput::new()calls and update themPhase 5: Update Tests (2-3 hours)
Arc<UserOutput>Phase 6: Documentation and Cleanup (1 hour)
src/bootstrap/container.rsdocs/codebase-architecture.mdabout centralized services./scripts/pre-commit.shAcceptance Criteria
Quality Checks:
./scripts/pre-commit.shArchitecture Checks:
Containertype exists insrc/bootstrap/container.rsContaineris bootstrapped insrc/bootstrap/app.rsafter logging initializationUserOutput::new()calls exist in presentation layer (except in tests)Arc<UserOutput>parameterCommandContextrequiresArc<UserOutput>in constructor and stores it as a fieldCommandContext::new()acceptsuser_output: Arc<UserOutput>parameterUserOutputBehavior Checks:
DEFAULT_VERBOSITY) is used consistentlyTesting Checks:
UserOutputDocumentation Checks:
Related