|
| 1 | +# Refactor Command Handlers to Modular Directory Structure |
| 2 | + |
| 3 | +**Issue**: #56 |
| 4 | +**Type**: Refactoring |
| 5 | +**Related**: Follows pattern established in `CreateCommandHandler` (see `src/application/command_handlers/create/`) |
| 6 | + |
| 7 | +## Overview |
| 8 | + |
| 9 | +Refactor the existing single-file command handlers (`provision`, `configure`, `destroy`) to follow the new modular directory structure pattern established in `CreateCommandHandler`. This improves code organization, maintainability, and scalability by separating concerns into dedicated files. |
| 10 | + |
| 11 | +Currently, three command handlers use a single-file pattern: |
| 12 | + |
| 13 | +- `provision.rs` (752 lines) - Too large, mixing handler logic, errors, and tests |
| 14 | +- `destroy.rs` (631 lines) - Getting unwieldy |
| 15 | +- `configure.rs` (370 lines) - Manageable but would benefit from separation |
| 16 | + |
| 17 | +The `CreateCommandHandler` demonstrates a better structure with separate files for handler logic, errors, and tests. |
| 18 | + |
| 19 | +## Goals |
| 20 | + |
| 21 | +- [ ] Migrate `ProvisionCommandHandler` to modular structure |
| 22 | +- [ ] Migrate `ConfigureCommandHandler` to modular structure |
| 23 | +- [ ] Migrate `DestroyCommandHandler` to modular structure |
| 24 | +- [ ] Improve code maintainability and readability |
| 25 | +- [ ] Make test organization clearer and more scalable |
| 26 | +- [ ] Establish consistent patterns across all command handlers |
| 27 | + |
| 28 | +## 🏗️ Architecture Requirements |
| 29 | + |
| 30 | +**DDD Layer**: Application Layer |
| 31 | +**Module Path**: `src/application/command_handlers/{provision|configure|destroy}/` |
| 32 | +**Pattern**: Command Handler with modular organization |
| 33 | + |
| 34 | +### Module Structure Requirements |
| 35 | + |
| 36 | +- [ ] Follow DDD layer separation (see [docs/codebase-architecture.md](../docs/codebase-architecture.md)) |
| 37 | +- [ ] Maintain existing dependency injection patterns |
| 38 | +- [ ] Use appropriate module organization (see [docs/contributing/module-organization.md](../docs/contributing/module-organization.md)) |
| 39 | +- [ ] Preserve existing public API surface (no breaking changes) |
| 40 | + |
| 41 | +### Architectural Constraints |
| 42 | + |
| 43 | +- [ ] No changes to command handler business logic (refactoring only) |
| 44 | +- [ ] Error handling follows project conventions (see [docs/contributing/error-handling.md](../docs/contributing/error-handling.md)) |
| 45 | +- [ ] Testing strategy aligns with layer responsibilities (see [docs/contributing/testing.md](../docs/contributing/testing.md)) |
| 46 | +- [ ] All existing tests must continue to pass |
| 47 | +- [ ] Preserve traceability and observability features |
| 48 | + |
| 49 | +**Note**: Breaking changes to import paths are acceptable since this library is not yet publicly used. Simplifying the module structure takes priority over backward compatibility. |
| 50 | + |
| 51 | +### Anti-Patterns to Avoid |
| 52 | + |
| 53 | +- ❌ Changing business logic during refactoring |
| 54 | +- ❌ Mixing refactoring with new features |
| 55 | +- ❌ Creating incomplete module structures |
| 56 | +- ❌ Half-migrating modules (finish one completely before moving to next) |
| 57 | + |
| 58 | +## Specifications |
| 59 | + |
| 60 | +### Current Structure (Single-File Pattern) |
| 61 | + |
| 62 | +```text |
| 63 | +src/application/command_handlers/ |
| 64 | +├── configure.rs (370 lines) |
| 65 | +├── destroy.rs (631 lines) |
| 66 | +├── provision.rs (752 lines) |
| 67 | +└── test.rs |
| 68 | +``` |
| 69 | + |
| 70 | +**Problems**: |
| 71 | + |
| 72 | +- Large files difficult to navigate (provision.rs: 752 lines) |
| 73 | +- Mixed concerns: handler logic, errors, tests all in one file |
| 74 | +- Poor scalability: adding features bloats single file |
| 75 | +- Harder code reviews: changes to errors require reviewing entire handler |
| 76 | +- Test organization unclear |
| 77 | + |
| 78 | +### Target Structure (Modular Pattern) |
| 79 | + |
| 80 | +Based on the `create/` command handler pattern: |
| 81 | + |
| 82 | +```text |
| 83 | +src/application/command_handlers/ |
| 84 | +├── configure/ |
| 85 | +│ ├── mod.rs # Module documentation & public API |
| 86 | +│ ├── handler.rs # Command handler implementation |
| 87 | +│ ├── errors.rs # Error types with .help() |
| 88 | +│ └── tests/ |
| 89 | +│ ├── mod.rs # Test module entry |
| 90 | +│ ├── builders.rs # Test builders and fixtures |
| 91 | +│ └── integration.rs # Integration tests |
| 92 | +├── destroy/ |
| 93 | +│ ├── mod.rs |
| 94 | +│ ├── handler.rs |
| 95 | +│ ├── errors.rs |
| 96 | +│ └── tests/ |
| 97 | +│ ├── mod.rs |
| 98 | +│ ├── builders.rs |
| 99 | +│ └── integration.rs |
| 100 | +├── provision/ |
| 101 | +│ ├── mod.rs |
| 102 | +│ ├── handler.rs |
| 103 | +│ ├── errors.rs |
| 104 | +│ └── tests/ |
| 105 | +│ ├── mod.rs |
| 106 | +│ ├── builders.rs |
| 107 | +│ └── integration.rs |
| 108 | +└── test.rs |
| 109 | +``` |
| 110 | + |
| 111 | +### File Responsibilities |
| 112 | + |
| 113 | +#### `mod.rs` (Module Documentation & Public API) |
| 114 | + |
| 115 | +- Module-level documentation |
| 116 | +- Re-exports public types (`pub use`) |
| 117 | +- Declares submodules |
| 118 | +- Serves as clean entry point |
| 119 | + |
| 120 | +**Example** (based on `create/mod.rs`): |
| 121 | + |
| 122 | +```rust |
| 123 | +//! [Command Name] Command Module |
| 124 | +//! |
| 125 | +//! This module implements the delivery-agnostic `[Command]CommandHandler` |
| 126 | +//! for orchestrating [command purpose] business logic. |
| 127 | +//! |
| 128 | +//! ## Architecture |
| 129 | +//! |
| 130 | +//! The `[Command]CommandHandler` implements the Command Pattern... |
| 131 | + |
| 132 | +pub mod errors; |
| 133 | +pub mod handler; |
| 134 | + |
| 135 | +#[cfg(test)] |
| 136 | +pub mod tests; |
| 137 | + |
| 138 | +// Re-export public API |
| 139 | +pub use errors::{[Command]CommandHandlerError}; |
| 140 | +pub use handler::{[Command]CommandHandler}; |
| 141 | +``` |
| 142 | + |
| 143 | +#### `handler.rs` (Command Handler Implementation) |
| 144 | + |
| 145 | +- Command handler struct definition |
| 146 | +- Business logic orchestration |
| 147 | +- Dependency injection via constructor |
| 148 | +- Public `execute()` method |
| 149 | +- No error definitions (moved to `errors.rs`) |
| 150 | + |
| 151 | +**Size**: 200-300 lines typical |
| 152 | + |
| 153 | +#### `errors.rs` (Error Types) |
| 154 | + |
| 155 | +- Error enum definitions using `thiserror` |
| 156 | +- `Traceable` trait implementation |
| 157 | +- `.help()` methods for actionable error messages |
| 158 | +- Error documentation |
| 159 | + |
| 160 | +**Size**: 150-200 lines typical |
| 161 | + |
| 162 | +#### `tests/` (Test Organization) |
| 163 | + |
| 164 | +- `mod.rs`: Test module entry, re-exports |
| 165 | +- `builders.rs`: Test builders and fixtures |
| 166 | +- `integration.rs`: Integration test cases |
| 167 | + |
| 168 | +### Migration Strategy (Per Command Handler) |
| 169 | + |
| 170 | +For each command handler (`provision`, `configure`, `destroy`): |
| 171 | + |
| 172 | +1. **Create Directory Structure** |
| 173 | + |
| 174 | + ```bash |
| 175 | + mkdir -p src/application/command_handlers/{command}/tests |
| 176 | + ``` |
| 177 | + |
| 178 | +2. **Split Files** |
| 179 | + |
| 180 | + - Extract errors → `errors.rs` |
| 181 | + - Move handler → `handler.rs` |
| 182 | + - Create `mod.rs` with documentation |
| 183 | + - Organize tests → `tests/` |
| 184 | + |
| 185 | +3. **Update Imports Throughout Codebase** |
| 186 | + |
| 187 | + - Update parent `mod.rs` to use new module structure |
| 188 | + - Fix ALL imports in other modules that use the command handler |
| 189 | + - Update CLI/presentation layer imports |
| 190 | + - Update any test imports |
| 191 | + - **Breaking changes acceptable** - simplify paths where possible |
| 192 | + |
| 193 | +4. **Verify** |
| 194 | + |
| 195 | + - Run `cargo test` - all tests must pass |
| 196 | + - Run `cargo clippy` - no new warnings |
| 197 | + - Run `./scripts/pre-commit.sh` - all checks pass |
| 198 | + |
| 199 | +## Implementation Plan |
| 200 | + |
| 201 | +**Important**: Migrate and commit each command handler independently. Run pre-commit checks after completing each one before moving to the next. |
| 202 | + |
| 203 | +### Subtask 1: Migrate ProvisionCommandHandler (3-4 hours) |
| 204 | + |
| 205 | +- [ ] Create `src/application/command_handlers/provision/` directory |
| 206 | +- [ ] Create `provision/mod.rs` with module documentation and re-exports |
| 207 | +- [ ] Create `provision/handler.rs` - move handler implementation from `provision.rs` |
| 208 | +- [ ] Create `provision/errors.rs` - extract error types from `provision.rs` |
| 209 | +- [ ] Create `provision/tests/mod.rs` - test module entry point |
| 210 | +- [ ] Create `provision/tests/builders.rs` - extract or create test builders |
| 211 | +- [ ] Create `provision/tests/integration.rs` - move existing tests |
| 212 | +- [ ] Update `src/application/command_handlers/mod.rs` to use new structure |
| 213 | +- [ ] **Update ALL imports across codebase** (CLI, presentation, other modules) |
| 214 | +- [ ] Delete old `provision.rs` file |
| 215 | +- [ ] Run `cargo test` to verify all tests pass |
| 216 | +- [ ] Run `cargo clippy` to check for issues |
| 217 | +- [ ] Run `./scripts/pre-commit.sh` to verify quality |
| 218 | +- [ ] **Commit this refactoring independently before moving to next handler** |
| 219 | + |
| 220 | +### Subtask 2: Migrate ConfigureCommandHandler (2-3 hours) |
| 221 | + |
| 222 | +- [ ] Create `src/application/command_handlers/configure/` directory |
| 223 | +- [ ] Create `configure/mod.rs` with module documentation and re-exports |
| 224 | +- [ ] Create `configure/handler.rs` - move handler implementation from `configure.rs` |
| 225 | +- [ ] Create `configure/errors.rs` - extract error types from `configure.rs` |
| 226 | +- [ ] Create `configure/tests/mod.rs` - test module entry point |
| 227 | +- [ ] Create `configure/tests/builders.rs` - extract or create test builders |
| 228 | +- [ ] Create `configure/tests/integration.rs` - move existing tests |
| 229 | +- [ ] Update `src/application/command_handlers/mod.rs` to use new structure |
| 230 | +- [ ] **Update ALL imports across codebase** (CLI, presentation, other modules) |
| 231 | +- [ ] Delete old `configure.rs` file |
| 232 | +- [ ] Run `cargo test` to verify all tests pass |
| 233 | +- [ ] Run `cargo clippy` to check for issues |
| 234 | +- [ ] Run `./scripts/pre-commit.sh` to verify quality |
| 235 | +- [ ] **Commit this refactoring independently before moving to next handler** |
| 236 | + |
| 237 | +### Subtask 3: Migrate DestroyCommandHandler (2-3 hours) |
| 238 | + |
| 239 | +- [ ] Create `src/application/command_handlers/destroy/` directory |
| 240 | +- [ ] Create `destroy/mod.rs` with module documentation and re-exports |
| 241 | +- [ ] Create `destroy/handler.rs` - move handler implementation from `destroy.rs` |
| 242 | +- [ ] Create `destroy/errors.rs` - extract error types from `destroy.rs` |
| 243 | +- [ ] Create `destroy/tests/mod.rs` - test module entry point |
| 244 | +- [ ] Create `destroy/tests/builders.rs` - extract or create test builders |
| 245 | +- [ ] Create `destroy/tests/integration.rs` - move existing tests |
| 246 | +- [ ] Update `src/application/command_handlers/mod.rs` to use new structure |
| 247 | +- [ ] **Update ALL imports across codebase** (CLI, presentation, other modules) |
| 248 | +- [ ] Delete old `destroy.rs` file |
| 249 | +- [ ] Run `cargo test` to verify all tests pass |
| 250 | +- [ ] Run `cargo clippy` to check for issues |
| 251 | +- [ ] Run `./scripts/pre-commit.sh` to verify quality |
| 252 | +- [ ] **Commit this refactoring independently** |
| 253 | + |
| 254 | +### Subtask 4: Final Verification and Documentation (1 hour) |
| 255 | + |
| 256 | +- [ ] Run full test suite: `cargo test` |
| 257 | +- [ ] Run E2E tests: `cargo run --bin e2e-tests-full` |
| 258 | +- [ ] Verify all command handlers follow consistent structure |
| 259 | +- [ ] Update any relevant documentation mentioning command handler structure |
| 260 | +- [ ] Ensure all imports across the codebase are correct |
| 261 | +- [ ] Final pre-commit verification: `./scripts/pre-commit.sh` |
| 262 | +- [ ] **Final commit with any documentation updates** |
| 263 | + |
| 264 | +## Acceptance Criteria |
| 265 | + |
| 266 | +**Quality Checks**: |
| 267 | + |
| 268 | +- [ ] Pre-commit checks pass: `./scripts/pre-commit.sh` |
| 269 | + |
| 270 | +**Refactoring-Specific Criteria**: |
| 271 | + |
| 272 | +- [ ] All three command handlers use modular directory structure |
| 273 | +- [ ] Directory structure matches `create/` pattern exactly |
| 274 | +- [ ] All existing tests continue to pass |
| 275 | +- [ ] No changes to business logic (pure refactoring) |
| 276 | +- [ ] File sizes reduced to manageable levels (< 300 lines per file) |
| 277 | +- [ ] Clear separation of concerns (handler, errors, tests) |
| 278 | +- [ ] Module documentation is comprehensive |
| 279 | +- [ ] Error types include `.help()` methods |
| 280 | +- [ ] Test organization is clear and scalable |
| 281 | +- [ ] All imports updated correctly throughout entire codebase |
| 282 | +- [ ] No clippy warnings introduced |
| 283 | +- [ ] Code follows module organization conventions |
| 284 | + |
| 285 | +**Import Updates**: |
| 286 | + |
| 287 | +- [ ] All imports in CLI/presentation layer updated |
| 288 | +- [ ] All imports in tests updated |
| 289 | +- [ ] All imports in other application modules updated |
| 290 | +- [ ] Parent `mod.rs` correctly re-exports types for convenience |
| 291 | + |
| 292 | +**Structure Verification**: |
| 293 | + |
| 294 | +- [ ] Each command handler has `mod.rs`, `handler.rs`, `errors.rs` |
| 295 | +- [ ] Each has `tests/` subdirectory with `mod.rs`, `builders.rs`, `integration.rs` |
| 296 | +- [ ] Parent `mod.rs` correctly imports and re-exports all handlers |
| 297 | +- [ ] No orphaned files or unused code |
| 298 | + |
| 299 | +**Commit Strategy**: |
| 300 | + |
| 301 | +- [ ] Each command handler refactoring committed independently |
| 302 | +- [ ] Pre-commit checks pass before each commit |
| 303 | +- [ ] Clear, descriptive commit messages following conventional commits format |
| 304 | +- [ ] Final documentation commit separate from refactoring commits |
| 305 | + |
| 306 | +## Related Documentation |
| 307 | + |
| 308 | +- [Module Organization Guide](../contributing/module-organization.md) - File organization conventions |
| 309 | +- [Error Handling Guide](../contributing/error-handling.md) - Error type patterns and `.help()` methods |
| 310 | +- [Testing Conventions](../contributing/testing.md) - Test organization and naming |
| 311 | +- [Codebase Architecture](../codebase-architecture.md) - DDD layer structure |
| 312 | +- Reference Implementation: `src/application/command_handlers/create/` - Example of target structure |
| 313 | + |
| 314 | +## Benefits |
| 315 | + |
| 316 | +### Maintainability ✅ |
| 317 | + |
| 318 | +- **Smaller files**: Easier to navigate (200-300 lines vs 752 lines) |
| 319 | +- **Single responsibility**: Each file has one clear purpose |
| 320 | +- **Easier reviews**: Changes to errors don't require reviewing handler logic |
| 321 | + |
| 322 | +### Scalability ✅ |
| 323 | + |
| 324 | +- **Room to grow**: Can add validators, mappers without bloating main file |
| 325 | +- **Test organization**: Can add more test modules easily |
| 326 | +- **Better IDE support**: Smaller files improve code navigation |
| 327 | + |
| 328 | +### Consistency ✅ |
| 329 | + |
| 330 | +- **Uniform patterns**: All command handlers follow same structure |
| 331 | +- **Predictable locations**: Developers know where to find handler vs errors vs tests |
| 332 | +- **Easier onboarding**: New contributors understand structure quickly |
| 333 | + |
| 334 | +### Testing ✅ |
| 335 | + |
| 336 | +- **Dedicated test builders**: Clear separation of test fixtures |
| 337 | +- **Integration tests isolated**: Easier to maintain and extend |
| 338 | +- **Better test discoverability**: Tests organized by purpose |
| 339 | + |
| 340 | +## Notes |
| 341 | + |
| 342 | +### Migration Order Rationale |
| 343 | + |
| 344 | +Starting with `provision` (largest, most complex) first because: |
| 345 | + |
| 346 | +1. It demonstrates the most value (752 → ~600 total lines split appropriately) |
| 347 | +2. Establishes patterns for subsequent migrations |
| 348 | +3. Most complex case first helps validate the approach |
| 349 | + |
| 350 | +### Preserving Git History |
| 351 | + |
| 352 | +Consider using `git mv` where possible to preserve file history: |
| 353 | + |
| 354 | +```bash |
| 355 | +# When creating new files, use git mv for better history tracking |
| 356 | +git mv src/application/command_handlers/provision.rs \ |
| 357 | + src/application/command_handlers/provision/handler.rs |
| 358 | +``` |
| 359 | + |
| 360 | +However, since we're splitting files, manual creation may be clearer. |
| 361 | + |
| 362 | +### Breaking Changes: Acceptable |
| 363 | + |
| 364 | +**Important**: Since this library is not yet publicly used, breaking changes to import paths are acceptable and even encouraged if they simplify the refactoring: |
| 365 | + |
| 366 | +- ✅ Import paths will change (e.g., `use crate::application::command_handlers::provision::ProvisionCommandHandler`) |
| 367 | +- ✅ Can simplify re-exports if it improves clarity |
| 368 | +- ✅ Focus on best final structure, not backward compatibility |
| 369 | +- ⚠️ Business logic must remain unchanged (pure refactoring) |
| 370 | +- ⚠️ All existing tests must still pass (behavior unchanged) |
| 371 | + |
| 372 | +This freedom allows us to: |
| 373 | + |
| 374 | +- Create the cleanest possible module structure |
| 375 | +- Avoid complex re-export chains just for compatibility |
| 376 | +- Optimize for maintainability over backward compatibility |
| 377 | + |
| 378 | +### Future Work |
| 379 | + |
| 380 | +After this refactoring, consider: |
| 381 | + |
| 382 | +- Adding more comprehensive test coverage where gaps exist |
| 383 | +- Extracting common patterns into shared utilities |
| 384 | +- Creating ADR documenting this as the standard command handler pattern |
0 commit comments