torrust-tracker-deployer 0.1.0

Torrust Tracker Deployer - Deployment Infrastructure with Ansible and OpenTofu
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
# DDD Layer Placement Guide

This guide provides clear guidelines on which code belongs in which Domain-Driven Design (DDD) layer. Following these guidelines ensures proper separation of concerns, maintainability, and testability.

> **Note on Examples**: This guide uses illustrative code patterns to demonstrate layer placement principles. These are generic examples designed to show common patterns rather than exact code from the repository, making them more maintainable and easier to understand.

## 🎯 Why DDD Matters

The Torrust Tracker Deployer follows Domain-Driven Design principles to maintain clear separation between business logic, use cases, external integrations, and user interfaces. This architecture ensures:

- **Clear boundaries** - Each layer has a specific responsibility
- **Better testability** - Components can be tested in isolation
- **Easier maintenance** - Changes in one layer don't cascade to others
- **Type safety** - Compile-time guarantees for valid state transitions
- **Team collaboration** - Consistent patterns make onboarding easier

For a comprehensive overview of the architecture, see [Codebase Architecture](../codebase-architecture.md).

## πŸ“š Layer Overview

The project uses a four-layer architecture with strict dependency rules:

```text
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚     Presentation Layer              β”‚  User Interface
β”‚  (CLI, User Output, Command         β”‚  - Clap command definitions
β”‚   Dispatch, Error Display)          β”‚  - User input parsing
β”‚  src/presentation/                  β”‚  - Output formatting
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚ depends on
             ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚      Application Layer              β”‚  Use Cases & Orchestration
β”‚  (Commands, Use Cases, Steps)       β”‚  - Command handlers
β”‚  src/application/                   β”‚  - DTOs for data transfer
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  - Application services
             β”‚ depends on
             ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚       Domain Layer                  β”‚  Business Logic
β”‚  (Business Logic, Entities,         β”‚  - Domain entities
β”‚   Value Objects)                    β”‚  - Value objects
β”‚  src/domain/                        β”‚  - Domain traits
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             ↑
             β”‚ depends on
             β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚    Infrastructure Layer             β”‚  External Integrations
β”‚  (External Tools, File System,      β”‚  - File I/O
β”‚   SSH, Templates, Trace Writers)    β”‚  - SSH, HTTP clients
β”‚  src/infrastructure/                β”‚  - OpenTofu, Ansible
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

### Dependency Rule

**Dependencies flow inward toward the domain**:

- βœ… Presentation β†’ Application β†’ Domain
- βœ… Infrastructure β†’ Domain
- ❌ Domain β†’ Application (Forbidden)
- ❌ Domain β†’ Infrastructure (Forbidden)
- ❌ Application β†’ Presentation (Forbidden)

This ensures the domain layer remains pure business logic, free from technical implementation details.

## πŸ›οΈ Domain Layer (`src/domain/`)

### Purpose

The domain layer contains pure business logic, entities, value objects, and domain events. It represents the core problem space and business rules without any technical implementation details.

### What Belongs Here

- βœ… **Value Objects** with validation - `EnvironmentName`, `Username`, `TraceId`
- βœ… **Domain Entities** - `Environment<S>` with type-state pattern
- βœ… **Domain Traits** - `Clock`, `EnvironmentRepository` (interfaces)
- βœ… **Business Rules** - Validation logic, domain constraints
- βœ… **Domain Events** - Events representing business occurrences
- βœ… **Serde derives on entities** - For persistence (pragmatic trade-off, see below)

### What Does NOT Belong Here

- ❌ **File I/O operations** - `std::fs`, `tokio::fs`
- ❌ **HTTP clients** - `reqwest`, `hyper`
- ❌ **External APIs** - OpenTofu, Ansible, SSH clients
- ❌ **DTOs with primitives** - `String` types meant for deserialization
- ❌ **Manual serialization** - Custom `impl Serialize` (put in infrastructure)

### Red Flags

Watch for these indicators that code might be in the wrong layer:

- Using `serde` on DTOs with raw `String` primitives (not domain entities)
- Importing `std::fs`, `tokio::fs`, `reqwest`, `hyper`
- Methods that read/write files directly
- Raw `String` types without domain semantics
- Database queries or external API calls

### Nuance: Serde on Domain Entities

Using `#[derive(Serialize, Deserialize)]` on domain entities for **persistence** is a pragmatic trade-off:

**βœ… Acceptable Use:**

```rust
use serde::{Deserialize, Serialize};

/// Domain entity with serde for persistence
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct EnvironmentName(String);

/// Domain entity with business logic and persistence
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Environment<S> {
    context: EnvironmentContext,
    state: S,
}
```

**❌ Not Acceptable:**

```rust
/// DTO masquerading as domain entity
#[derive(Serialize, Deserialize)]
pub struct ConfigDto {
    pub name: String,  // Raw primitive, no domain semantics
    pub path: String,  // Should be PathBuf or domain type
}
```

**Rationale:**

- Serde derives are code generation (external to your domain code)
- Actual serialization logic lives in the serde library
- Many Rust projects make this pragmatic choice to avoid boilerplate
- Domain entities have business logic; DTOs are just data containers

**When NOT to use serde in domain:**

- If you need **custom serialization logic**, implement it in the infrastructure layer
- Keep domain types pure and let infrastructure handle the serialization details

### Examples and Patterns

#### βœ… Pattern: Value Object with Validation

Value objects encapsulate primitives with business rules and validation:

```rust
/// Validated value object (e.g., EnvironmentName, Username, Email)
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ValidatedName(String);

impl ValidatedName {
    /// Constructor with validation - enforces business rules
    pub fn new(value: String) -> Result<Self, ValidationError> {
        Self::validate(&value)?;
        Ok(Self(value))
    }

    fn validate(value: &str) -> Result<(), ValidationError> {
        // Business rules go here
        if value.is_empty() {
            return Err(ValidationError::Empty);
        }
        if !value.chars().all(|c| c.is_alphanumeric() || c == '-') {
            return Err(ValidationError::InvalidCharacters);
        }
        Ok(())
    }
}
```

**Why this is domain:**

- Contains business rules (validation logic)
- Uses serde for persistence (pragmatic trade-off)
- No infrastructure concerns (file I/O, HTTP, etc.)

#### βœ… Pattern: Domain Entity with Business Logic

Domain entities are core business objects with identity and lifecycle:

```rust
/// Domain entity with business logic (e.g., Environment, Deployment, Order)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Entity {
    id: EntityId,
    name: ValidatedName,
    status: EntityStatus,
}

impl Entity {
    pub fn new(name: ValidatedName) -> Self {
        // Domain logic for creating entities
        Self {
            id: EntityId::generate(),
            name,
            status: EntityStatus::Created,
        }
    }

    pub fn activate(&mut self) -> Result<(), DomainError> {
        // Business rules for state transitions
        match self.status {
            EntityStatus::Created => {
                self.status = EntityStatus::Active;
                Ok(())
            }
            _ => Err(DomainError::InvalidStateTransition),
        }
    }
}
```

**Why this is domain:**

- Core business object with identity
- Contains business logic and state transitions
- Serde for persistence (pragmatic choice)
- No external dependencies

#### βœ… Pattern: Repository Trait (Domain Interface)

Domain defines persistence contracts without implementation:

```rust
/// Repository interface - domain defines the contract
#[async_trait]
pub trait EntityRepository: Send + Sync {
    async fn save(&self, entity: &Entity) -> Result<(), RepositoryError>;
    async fn find_by_id(&self, id: &EntityId) -> Result<Option<Entity>, RepositoryError>;
    async fn delete(&self, id: &EntityId) -> Result<(), RepositoryError>;
}
```

**Why this is domain:**

- Defines contract for persistence (interface only)
- No implementation details (infrastructure will implement)
- Domain stays independent of persistence technology

## πŸ“‹ Application Layer (`src/application/`)

### Purpose

The application layer orchestrates domain and infrastructure services to implement use cases. It contains command handlers, DTOs for data transfer, and application services.

### What Belongs Here

- βœ… **Command Handlers** - `ProvisionCommandHandler`, `DestroyCommandHandler`
- βœ… **DTOs** - Data transfer objects like `EnvironmentCreationConfig`
- βœ… **Serde** - For JSON/TOML deserialization
- βœ… **Use Cases** - Orchestration of domain operations
- βœ… **Application Services** - Coordination between layers
- βœ… **Steps** - Individual units of work in command workflows
- βœ… **Application Errors** - Error types for application-level failures

### What Does NOT Belong Here

- ❌ **Business Logic** - Belongs in domain layer
- ❌ **Direct File I/O** - Use infrastructure traits instead
- ❌ **Direct External APIs** - Use infrastructure abstractions

### Examples and Patterns

#### βœ… Pattern: Command Handler (Use Case Orchestrator)

Command handlers orchestrate workflows by coordinating domain and infrastructure:

```rust
/// Command handler orchestrating a complete workflow
pub struct CommandHandler {
    domain_service: Arc<dyn DomainService>,
    infrastructure_client: Arc<dyn InfrastructureClient>,
    repository: Arc<dyn Repository>,
}

impl CommandHandler {
    /// Execute the workflow - orchestration only, no business logic
    pub async fn execute(&self, input: Input) -> Result<Output, CommandError> {
        // 1. Load domain entity
        let entity = self.repository.find_by_id(&input.id).await?;
        
        // 2. Delegate business logic to domain
        let updated_entity = entity.perform_business_operation(input.params)?;
        
        // 3. Use infrastructure services
        self.infrastructure_client.execute_external_operation(&updated_entity).await?;
        
        // 4. Persist changes
        self.repository.save(&updated_entity).await?;
        
        Ok(Output::from(updated_entity))
    }
}
```

**Why this is application:**

- Orchestrates domain and infrastructure (no business logic itself)
- Coordinates workflow steps in sequence
- Delegates business rules to domain layer
- Uses infrastructure through interfaces

#### βœ… Pattern: DTO (Data Transfer Object)

DTOs handle deserialization and convert to domain types:

```rust
/// DTO for external input (JSON, TOML, API requests)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigDto {
    /// Raw string - will be validated when converted to domain type
    pub name: String,
    pub port: u16,
    pub enabled: bool,
}

impl ConfigDto {
    /// Convert DTO to validated domain types
    pub fn to_domain(&self) -> Result<DomainConfig, ValidationError> {
        // Validation happens in domain layer
        let name = ValidatedName::new(self.name.clone())?;
        Ok(DomainConfig::new(name, self.port, self.enabled))
    }
}
```

**Why this is application:**

- Data transfer (not a domain entity with business logic)
- Uses raw primitives for deserialization
- Converts to domain types with validation
- Bridge between external format and domain

#### βœ… Pattern: Application Step

Steps are reusable units of work within workflows:

```rust
/// Reusable step in a command handler workflow
pub struct WorkflowStep {
    infrastructure_client: Arc<dyn Client>,
}

impl WorkflowStep {
    pub async fn execute(&self, entity: &Entity) -> Result<StepResult, StepError> {
        // Coordinate infrastructure call - no business logic
        let data = entity.get_required_data();
        self.infrastructure_client.perform_action(data).await?;
        Ok(StepResult::Success)
    }
}
```

**Why this is application:**

- Reusable component in workflows
- Coordinates infrastructure without business logic
- Can be composed into larger workflows

## πŸ”§ Infrastructure Layer (`src/infrastructure/` and `src/adapters/`)

### Purpose

The infrastructure layer provides technical implementations for external integrations, file I/O, persistence, and other external concerns. It implements interfaces defined in the domain layer.

**Note:** The project organizes infrastructure code into two directories:

- `src/adapters/` - Direct wrappers for external tools (OpenTofu, Ansible, SSH, Docker, LXD)
- `src/infrastructure/` - Other infrastructure concerns (persistence, tracing, template rendering)

### What Belongs Here

- βœ… **File I/O implementations** - Reading/writing files
- βœ… **HTTP clients** - External API integrations
- βœ… **Repository implementations** - `JsonEnvironmentRepository`
- βœ… **External tool wrappers** - OpenTofu, Ansible, SSH clients
- βœ… **Template rendering** - Tera template engines
- βœ… **Custom serialization** - When domain can't use derives
- βœ… **Trace writers** - Writing trace files

### What Does NOT Belong Here

- ❌ **Business Rules** - Belongs in domain
- ❌ **Domain Entities** - Reference them, don't define them
- ❌ **Use Cases** - Belongs in application

### Examples and Patterns

#### βœ… Pattern: Repository Implementation

Repositories implement domain persistence interfaces using specific technologies:

```rust
/// File-based repository implementing domain interface
pub struct FileSystemRepository {
    base_dir: PathBuf,
}

#[async_trait]
impl EntityRepository for FileSystemRepository {
    async fn save(&self, entity: &Entity) -> Result<(), RepositoryError> {
        let file_path = self.entity_file_path(entity.id());
        let json = serde_json::to_string_pretty(entity)?;
        tokio::fs::write(file_path, json).await?;
        Ok(())
    }

    async fn find_by_id(&self, id: &EntityId) -> Result<Option<Entity>, RepositoryError> {
        let file_path = self.entity_file_path(id);
        if !file_path.exists() {
            return Ok(None);
        }
        let json = tokio::fs::read_to_string(file_path).await?;
        let entity = serde_json::from_str(&json)?;
        Ok(Some(entity))
    }
}
```

**Why this is infrastructure:**

- Implements domain repository interface
- Contains file I/O operations (tokio::fs)
- Handles serialization/deserialization
- No business logic (just persistence mechanics)

#### βœ… Pattern: External Tool Adapter

Adapters wrap external tools and provide a clean interface:

```rust
/// Adapter for external CLI tool (e.g., Terraform, Ansible, Docker)
pub struct ExternalToolClient {
    command_executor: Arc<dyn CommandExecutor>,
}

impl ExternalToolClient {
    pub async fn initialize(&self, working_dir: &Path) -> Result<(), ToolError> {
        self.command_executor
            .execute("tool", &["init"], working_dir)
            .await?;
        Ok(())
    }

    pub async fn apply_changes(&self, working_dir: &Path) -> Result<(), ToolError> {
        self.command_executor
            .execute("tool", &["apply", "--auto-approve"], working_dir)
            .await?;
        Ok(())
    }
}
```

**Why this is infrastructure:**

- Wraps external tool (CLI, API, SDK)
- Executes system commands or API calls
- No business logic (just integration mechanics)
- Provides clean interface for application layer

#### βœ… Pattern: HTTP Client Adapter

HTTP clients integrate with external APIs:

```rust
/// HTTP client for external API integration
pub struct ApiClient {
    base_url: String,
    http_client: reqwest::Client,
}

impl ApiClient {
    pub async fn fetch_data(&self, id: &str) -> Result<ApiResponse, ApiError> {
        let url = format!("{}/api/data/{}", self.base_url, id);
        let response = self.http_client
            .get(&url)
            .send()
            .await?
            .json::<ApiResponse>()
            .await?;
        Ok(response)
    }
}
```

**Why this is infrastructure:**

- HTTP client for external API
- Handles network I/O
- No business logic (just API integration)
- Returns data for application layer to process

## 🎨 Presentation Layer (`src/presentation/`)

### Purpose

The presentation layer handles user interaction through the command-line interface. It parses user input, dispatches commands, formats output, and displays errors in a user-friendly manner.

### What Belongs Here

- βœ… **Clap command definitions** - CLI argument parsing
- βœ… **User input validation** - Parsing command-line arguments
- βœ… **Output formatting** - User-friendly messages and progress indicators
- βœ… **Command dispatch** - Routing to application layer
- βœ… **Error display** - Formatting errors for users with help systems

### What Does NOT Belong Here

- ❌ **Business Logic** - Belongs in domain
- ❌ **Direct Infrastructure Calls** - Go through application layer

### Examples and Patterns

#### βœ… Pattern: CLI Definition with Clap

CLI structures define the user interface and parse arguments:

```rust
/// Main CLI structure with global args and subcommands
#[derive(Parser, Debug)]
#[command(name = "app-name")]
#[command(about = "Application description")]
#[command(version)]
pub struct Cli {
    /// Global flags available to all subcommands
    #[command(flatten)]
    pub global: GlobalArgs,

    /// Available subcommands
    #[command(subcommand)]
    pub command: Option<Commands>,
}

#[derive(Subcommand, Debug)]
pub enum Commands {
    /// Create a new resource
    Create {
        name: String,
        #[arg(short, long)]
        force: bool,
    },
    /// Delete an existing resource
    Delete {
        name: String,
    },
}
```

**Why this is presentation:**

- Uses clap for argument parsing
- Defines user-facing interface
- No business logic (just data structure)
- Routes to application layer for execution

#### βœ… Pattern: Command Dispatcher

Dispatchers route CLI commands to application layer handlers:

```rust
/// Dispatcher that routes commands to application handlers
pub struct CommandDispatcher {
    command_handler: Arc<CommandHandler>,
}

impl CommandDispatcher {
    pub async fn dispatch(&self, command: Commands) -> Result<(), DispatchError> {
        match command {
            Commands::Create { name, force } => {
                // Parse/validate user input
                let validated_name = ValidatedName::new(name)
                    .map_err(DispatchError::InvalidInput)?;
                
                // Route to application layer
                self.command_handler.create(validated_name, force).await?;
                
                // Format user output
                println!("βœ… Resource created successfully");
                Ok(())
            }
            Commands::Delete { name } => {
                let validated_name = ValidatedName::new(name)
                    .map_err(DispatchError::InvalidInput)?;
                
                self.command_handler.delete(validated_name).await?;
                println!("βœ… Resource deleted successfully");
                Ok(())
            }
        }
    }
}
```

**Why this is presentation:**

- Routes commands to application layer
- Parses and validates user input
- Formats output for users
- No business logic (delegates to application/domain)

## 🧭 Decision Flowchart

Use this flowchart to quickly determine where code belongs:

```text
Does it contain business rules or validation?
β”œβ”€ YES β†’ Domain Layer
β”‚   └─ Does it need persistence?
β”‚       β”œβ”€ YES β†’ Add #[derive(Serialize, Deserialize)]
β”‚       └─ NO β†’ Pure domain type
β”‚
└─ NO β†’ What is its primary responsibility?
    β”‚
    β”œβ”€ Data Transfer (DTO) β†’ Application Layer
    β”‚   └─ Raw primitives (String, i32) for deserialization
    β”‚
    β”œβ”€ Orchestration (Use Case) β†’ Application Layer
    β”‚   └─ Coordinates domain + infrastructure
    β”‚
    β”œβ”€ External Integration β†’ Infrastructure Layer
    β”‚   └─ File I/O, HTTP, SSH, OpenTofu, Ansible
    β”‚
    └─ User Interface β†’ Presentation Layer
        └─ CLI parsing, output formatting, error display
```

## πŸ” Common Mistakes to Avoid

### ❌ DTOs in Domain Layer

```rust
// WRONG: This belongs in application layer
// src/domain/config.rs

#[derive(Serialize, Deserialize)]
pub struct ConfigDto {
    pub name: String,  // Raw primitive, no domain validation
    pub path: String,  // Should be PathBuf or domain type
}
```

**Why wrong:**

- Uses raw primitives without domain semantics
- No business logic or validation
- Purpose is data transfer, not domain modeling

**Correct placement:**

```rust
// src/application/config/environment_config.rs

#[derive(Serialize, Deserialize)]
pub struct EnvironmentCreationConfig {
    pub name: String,  // Will be validated when converted to EnvironmentName
}

impl EnvironmentCreationConfig {
    pub fn to_domain_params(&self) -> Result<EnvironmentName, ConfigError> {
        EnvironmentName::new(self.name.clone())
    }
}
```

### ❌ File I/O in Domain Layer

```rust
// WRONG: This belongs in infrastructure layer
// src/domain/environment/mod.rs

impl Environment {
    pub fn save_to_file(&self) -> Result<(), Error> {
        let json = serde_json::to_string(self)?;
        std::fs::write("environment.json", json)?;  // ❌ File I/O in domain!
        Ok(())
    }
}
```

**Why wrong:**

- Domain should not know about file systems
- Breaks dependency rule (domain β†’ infrastructure)
- Hard to test

**Correct approach:**

```rust
// Domain defines interface
// src/domain/environment/repository.rs

#[async_trait]
pub trait EnvironmentRepository {
    async fn save(&self, environment: &Environment) -> Result<(), RepositoryError>;
}

// Infrastructure implements
// src/infrastructure/persistence/json_repository.rs

pub struct JsonEnvironmentRepository;

#[async_trait]
impl EnvironmentRepository for JsonEnvironmentRepository {
    async fn save(&self, environment: &Environment) -> Result<(), RepositoryError> {
        let json = serde_json::to_string(environment)?;
        tokio::fs::write("environment.json", json).await?;  // βœ… File I/O in infrastructure
        Ok(())
    }
}
```

### ❌ Business Logic in Application Layer

```rust
// WRONG: This belongs in domain layer
// src/application/command_handlers/create.rs

impl CreateCommandHandler {
    pub fn validate_environment_name(&self, name: &str) -> Result<(), Error> {
        // Business rules in application layer - WRONG!
        if name.is_empty() {
            return Err(Error::EmptyName);
        }
        if name.starts_with(char::is_numeric) {
            return Err(Error::StartsWithNumber);
        }
        Ok(())
    }
}
```

**Why wrong:**

- Business rules belong in domain
- Violates single responsibility
- Not reusable across use cases

**Correct approach:**

```rust
// Domain contains business rules
// src/domain/environment/name.rs

impl EnvironmentName {
    pub fn new(name: String) -> Result<Self, EnvironmentNameError> {
        Self::validate(&name)?;  // βœ… Business rules in domain
        Ok(Self(name))
    }

    fn validate(name: &str) -> Result<(), EnvironmentNameError> {
        if name.is_empty() {
            return Err(EnvironmentNameError::Empty);
        }
        if name.starts_with(char::is_numeric) {
            return Err(EnvironmentNameError::StartsWithNumber);
        }
        Ok(())
    }
}

// Application uses domain validation
// src/application/command_handlers/create.rs

impl CreateCommandHandler {
    pub fn execute(&self, name_str: String) -> Result<Environment, CreateError> {
        let name = EnvironmentName::new(name_str)?;  // βœ… Delegates to domain
        // ... rest of use case
    }
}
```

## πŸ“š Related Documentation

- [Codebase Architecture]../codebase-architecture.md - Comprehensive architecture overview
- [Module Organization]./module-organization.md - How to organize code within modules
- [Error Handling]./error-handling.md - Error handling principles and patterns
- [Development Principles]../development-principles.md - Core development principles

## πŸ”— External Resources

- [Herberto GraΓ§a - Explicit Architecture]https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/
- [Microsoft - DDD Microservices Guide]https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/ddd-oriented-microservice
- [DDD Crew - Starter Modelling Process]https://github.com/ddd-crew/ddd-starter-modelling-process
- [Eric Evans - Domain Language]https://www.domainlanguage.com/ddd/
- [Martin Fowler - Domain Driven Design]https://martinfowler.com/bliki/DomainDrivenDesign.html

## πŸ’‘ Summary

When in doubt, ask yourself:

1. **Does it contain business rules?** β†’ Domain
2. **Is it orchestrating a use case?** β†’ Application
3. **Does it integrate with external systems?** β†’ Infrastructure
4. **Does it handle user interaction?** β†’ Presentation

Remember: The goal is **clear separation of concerns**. Each layer should have one job and do it well.