This module contains Configuration Data Transfer Objects (DTOs) for environment creation. These types sit at the boundary between external configuration sources (JSON files, CLI arguments) and the internal domain model.
These types serve multiple purposes:
- JSON Deserialization - Accept user input from configuration files
- Validation Gateway - Convert loose string-based input to strict domain types
- Schema Generation - Derive
JsonSchemafor IDE autocomplete (seeschemas/environment-config.json) - AI Agent Reference - Provide richer type information than JSON schema alone
This module implements the Anti-Corruption Layer pattern from Domain-Driven Design:
User JSON → [Config DTOs] → validate/transform → [Domain Types]
(String-based) (PathBuf, NonZeroU32, etc.)
| Aspect | Config DTOs (this module) | Domain Types |
|---|---|---|
| Layer | Application | Domain |
| Purpose | JSON parsing | Business logic |
| Types | Raw primitives (String, u32) |
Validated newtypes (NonZeroU32, ProfileName) |
| Validation | Deferred to to_*_config() |
Enforced at construction |
| Serde | Heavy (Deserialize, Serialize, JsonSchema) |
Minimal |
config/
├── environment_config.rs # EnvironmentCreationConfig (top-level)
├── ssh_credentials_config.rs # SshCredentialsConfig
├── prometheus.rs # PrometheusSection
├── grafana.rs # GrafanaSection
├── errors.rs # CreateConfigError
├── provider/
│ ├── lxd.rs # LxdProviderSection
│ └── hetzner.rs # HetznerProviderSection
└── tracker/
├── tracker_section.rs # TrackerSection (aggregate)
├── tracker_core_section.rs # TrackerCoreSection, DatabaseSection
├── udp_tracker_section.rs # UdpTrackerSection
├── http_tracker_section.rs # HttpTrackerSection
├── http_api_section.rs # HttpApiSection
└── health_check_api_section.rs # HealthCheckApiSection
Each DTO provides a to_*_config() method that validates and converts to domain types:
// Example: PrometheusSection → PrometheusConfig
impl PrometheusSection {
pub fn to_prometheus_config(&self) -> Result<PrometheusConfig, CreateConfigError> {
// Validates: scrape_interval must be > 0
let interval = NonZeroU32::new(self.scrape_interval_in_secs)
.ok_or_else(|| CreateConfigError::InvalidPrometheusConfig(...))?;
Ok(PrometheusConfig::new(interval))
}
}The Rust types express constraints that JSON Schema cannot fully capture:
| Constraint | Rust Type | JSON Schema Limitation |
|---|---|---|
| Non-zero integers | NonZeroU32 |
Can only specify minimum: 1 |
| Mutually exclusive options | Tagged enums with #[serde(tag = "...")] |
oneOf is complex and error-prone |
| Path validation | PathBuf with existence checks |
No file system awareness |
| Format validation | Newtype constructors (ProfileName::new()) |
Regex patterns are limited |
| Cross-field validation | Custom to_*_config() logic |
No support |
| Secret handling | Password, ApiToken wrappers |
No security semantics |
When generating environment configuration:
- Reference these Rust types for accurate constraint information
- Follow the structure in
EnvironmentCreationConfigas the root type - Check validation logic in
to_*_config()methods for business rules - Use JSON schema (
schemas/environment-config.json) for basic structure, but trust Rust types for constraints
- JSON Schema - Generated schema for IDE autocomplete
- JSON Schema IDE Setup - Configure VS Code/editors
- ADR: Configuration DTO Layer Placement - Why these types are in application layer
- DDD Layer Placement Guide - General layer placement guidance