2026-01-01 14:10:13 +01:00
|
|
|
|
//! Presentation layer - API contracts and DTOs
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! This module defines data transfer objects (DTOs) and API request/response types
|
|
|
|
|
|
//! that form the public API contract. It translates between domain types and wire
|
|
|
|
|
|
//! formats (JSON) for HTTP communication.
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! # Current Status
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! **Currently unused** - API handlers are currently in [`crate::route`] module using
|
|
|
|
|
|
//! legacy structure. This module is prepared for future migration to proper hexagonal
|
|
|
|
|
|
//! architecture with clear presentation layer separation.
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! # Architecture Principles
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! - **API-first design**: Define contracts before implementation
|
|
|
|
|
|
//! - **DTO pattern**: Separate domain entities from API representations
|
|
|
|
|
|
//! - **Validation at boundary**: Parse and validate input before domain layer
|
|
|
|
|
|
//! - **OpenAPI integration**: Generate documentation from code
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! # Planned Submodules
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! ## `dto` - Data Transfer Objects
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! - `relay_dto`: RelayResponse, RelayStatusResponse, BulkControlRequest
|
|
|
|
|
|
//! - `label_dto`: UpdateLabelRequest, LabelResponse
|
|
|
|
|
|
//! - `health_dto`: HealthResponse, DeviceStatusResponse
|
|
|
|
|
|
//! - `error_dto`: ApiError, ValidationError (user-facing errors)
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! ## `mapper` - Domain ↔ DTO Conversions
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! - `relay_mapper`: Convert between Relay entity and RelayResponse
|
|
|
|
|
|
//! - `error_mapper`: Translate domain errors to HTTP status codes
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! ## `validator` - Input Validation
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! - Request validation before domain layer
|
|
|
|
|
|
//! - Parse DTOs into domain types (parse, don't validate principle)
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! # DTO Design Pattern
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! DTOs are separate from domain types to:
|
|
|
|
|
|
//! 1. **Prevent domain leakage**: Domain types stay internal
|
|
|
|
|
|
//! 2. **Enable versioning**: API can evolve without changing domain
|
|
|
|
|
|
//! 3. **Control serialization**: Explicit JSON representation
|
|
|
|
|
|
//! 4. **Validate at boundary**: Convert raw input to validated domain types
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! # Example DTO Structure
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! ```rust,ignore
|
|
|
|
|
|
//! /// API response for relay status
|
|
|
|
|
|
//! #[derive(Serialize, Deserialize, Object)]
|
|
|
|
|
|
//! pub struct RelayResponse {
|
|
|
|
|
|
//! /// Relay ID (1-8)
|
|
|
|
|
|
//! pub id: u8,
|
|
|
|
|
|
//! /// Current state ("on" or "off")
|
|
|
|
|
|
//! pub state: String,
|
|
|
|
|
|
//! /// Optional custom label
|
|
|
|
|
|
//! pub label: Option<String>,
|
|
|
|
|
|
//! }
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! impl From<Relay> for RelayResponse {
|
|
|
|
|
|
//! fn from(relay: Relay) -> Self {
|
|
|
|
|
|
//! Self {
|
|
|
|
|
|
//! id: relay.id().value(),
|
|
|
|
|
|
//! state: relay.state().to_string(),
|
|
|
|
|
|
//! label: relay.label().map(|l| l.to_string()),
|
|
|
|
|
|
//! }
|
|
|
|
|
|
//! }
|
|
|
|
|
|
//! }
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! impl TryFrom<RelayResponse> for Relay {
|
|
|
|
|
|
//! type Error = ValidationError;
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! fn try_from(dto: RelayResponse) -> Result<Self, Self::Error> {
|
|
|
|
|
|
//! let id = RelayId::new(dto.id)?;
|
|
|
|
|
|
//! let state = RelayState::from_str(&dto.state)?;
|
|
|
|
|
|
//! let label = dto.label.map(RelayLabel::new).transpose()?;
|
|
|
|
|
|
//! Ok(Relay::new(id, state).with_label(label))
|
|
|
|
|
|
//! }
|
|
|
|
|
|
//! }
|
|
|
|
|
|
//! ```
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! # Migration Plan
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! Current API in [`crate::route`] will be migrated to this layer:
|
|
|
|
|
|
//! 1. Define DTOs for all API endpoints
|
|
|
|
|
|
//! 2. Implement domain <20> DTO mappers
|
|
|
|
|
|
//! 3. Move API handlers to use DTOs
|
|
|
|
|
|
//! 4. Generate OpenAPI specs from DTOs
|
|
|
|
|
|
//! 5. Remove direct domain type exposure
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! # References
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! - Architecture: `specs/constitution.md` - API-First Design principle
|
|
|
|
|
|
//! - API design: `specs/001-modbus-relay-control/plan.md` - Presentation layer tasks
|
|
|
|
|
|
//! - Domain types: [`crate::domain`] - Types to be wrapped in DTOs
|
2026-01-23 20:46:48 +01:00
|
|
|
|
|
|
|
|
|
|
/// Data Transfer Objects (DTOs) for API responses.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// This module contains DTO structures that are used to serialize domain
|
|
|
|
|
|
/// objects for API responses, providing a clean separation between internal
|
|
|
|
|
|
/// domain models and external API contracts.
|
|
|
|
|
|
pub mod dto;
|
|
|
|
|
|
|
|
|
|
|
|
pub mod error;
|