Skip to content

Commit 06f10af

Browse files
Copilotjosecelano
andcommitted
docs: add command module structure patterns
Co-authored-by: josecelano <58816+josecelano@users.noreply.github.com>
1 parent 863f465 commit 06f10af

File tree

1 file changed

+143
-0
lines changed

1 file changed

+143
-0
lines changed

docs/contributing/module-organization.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,149 @@ These guidelines are general principles, not absolute rules. Consider deviating
449449

450450
Use your judgment, but **always prioritize readability and maintainability**.
451451

452+
## 📂 Command Module Structure Patterns
453+
454+
For presentation layer commands in `src/presentation/commands/`, we follow standardized folder structures that make it clear whether a command has subcommands or is a simple single-purpose command.
455+
456+
### Pattern 1: Simple Commands (No Subcommands)
457+
458+
For commands that perform a single operation (like `destroy`):
459+
460+
```
461+
src/presentation/commands/destroy/
462+
├── mod.rs // Module documentation and re-exports
463+
├── handler.rs // Main command implementation
464+
├── errors.rs // Error types
465+
└── tests/ // Test modules
466+
├── mod.rs
467+
└── integration.rs
468+
```
469+
470+
**Key characteristics:**
471+
- Uses `handler.rs` for the main command logic
472+
- Direct implementation without routing
473+
- Clean and focused on single responsibility
474+
475+
**Example:**
476+
```rust
477+
// In handler.rs
478+
pub fn handle_destroy_command(
479+
environment_name: &str,
480+
working_dir: &Path,
481+
) -> Result<(), DestroySubcommandError> {
482+
// Direct implementation
483+
}
484+
```
485+
486+
### Pattern 2: Commands with Subcommands
487+
488+
For commands that route to multiple subcommands (like `create`):
489+
490+
```
491+
src/presentation/commands/create/
492+
├── mod.rs // Module documentation and re-exports
493+
├── handler.rs // Router that delegates to subcommands
494+
├── errors.rs // Shared error types
495+
├── config_loader.rs // Shared utilities (if needed)
496+
├── subcommands/ // 🆕 Dedicated subcommands folder
497+
│ ├── mod.rs // Subcommands module and re-exports
498+
│ ├── environment.rs // Environment creation subcommand
499+
│ └── template.rs // Template generation subcommand
500+
└── tests/ // Test modules
501+
├── mod.rs
502+
├── integration.rs
503+
└── fixtures.rs
504+
```
505+
506+
**Key characteristics:**
507+
- `handler.rs` acts as a simple router/dispatcher
508+
- Each subcommand has its own focused module in `subcommands/`
509+
- Subcommands are isolated and single-responsibility
510+
- Easy to add new subcommands without cluttering main files
511+
512+
**Example:**
513+
```rust
514+
// In handler.rs (router)
515+
pub fn handle_create_command(
516+
action: CreateAction,
517+
working_dir: &Path,
518+
) -> Result<(), CreateSubcommandError> {
519+
match action {
520+
CreateAction::Environment { env_file } => {
521+
subcommands::handle_environment_creation(&env_file, working_dir)
522+
}
523+
CreateAction::Template { output_path } => {
524+
let template_path = output_path.unwrap_or_else(CreateAction::default_template_path);
525+
subcommands::handle_template_generation(&template_path)
526+
}
527+
}
528+
}
529+
530+
// In subcommands/environment.rs
531+
pub fn handle_environment_creation(
532+
env_file: &Path,
533+
working_dir: &Path,
534+
) -> Result<(), CreateSubcommandError> {
535+
// Focused implementation for environment creation
536+
}
537+
538+
// In subcommands/template.rs
539+
pub fn handle_template_generation(
540+
output_path: &Path,
541+
) -> Result<(), CreateSubcommandError> {
542+
// Focused implementation for template generation
543+
}
544+
```
545+
546+
### When to Use Each Pattern
547+
548+
**Use Pattern 1 (Simple Commands)** when:
549+
- The command performs a single, focused operation
550+
- No routing or branching logic is needed
551+
- The implementation fits naturally in one module
552+
553+
**Use Pattern 2 (Commands with Subcommands)** when:
554+
- The command has multiple distinct subcommands
555+
- Each subcommand has significant implementation
556+
- You want to isolate different behaviors for clarity
557+
- You anticipate adding more subcommands in the future
558+
559+
### Benefits of These Patterns
560+
561+
**Clear Visual Distinction**: Folder structure immediately shows command complexity
562+
**Consistent Naming**: All commands use `handler.rs` for their main entry point
563+
**Single Responsibility**: Each subcommand module has one clear purpose
564+
**Easy Extension**: Adding new subcommands is straightforward
565+
**Better Testing**: Each subcommand can be tested independently
566+
**Improved Navigation**: Developers can quickly find the right code
567+
568+
### Migration Guide
569+
570+
When refactoring existing commands to follow these patterns:
571+
572+
1. **For simple commands**: Rename `command.rs``handler.rs`
573+
2. **For commands with subcommands**:
574+
- Create `subcommands/` directory
575+
- Move subcommand implementations to individual files in `subcommands/`
576+
- Rename main file to `handler.rs` and simplify to a router
577+
- Update `mod.rs` to include the `subcommands` module
578+
- Update re-exports to use the new structure
579+
580+
**Example migration:**
581+
```bash
582+
# Before
583+
create/
584+
└── subcommand.rs (contains all logic)
585+
586+
# After
587+
create/
588+
├── handler.rs (router only)
589+
└── subcommands/
590+
├── mod.rs
591+
├── environment.rs
592+
└── template.rs
593+
```
594+
452595
## 🔗 Related Documentation
453596

454597
- [Testing Conventions](./testing.md) - How to organize test code

0 commit comments

Comments
 (0)