modkit/domain/mod.rs
1//! Domain Layer Marker Traits
2//!
3//! This module provides marker traits for enforcing domain-driven design boundaries
4//! at compile time. Types marked with these traits are guaranteed to be free of
5//! infrastructure dependencies (`sqlx`, `sea_orm`, `http`, `axum`, etc.).
6//!
7//! # Usage
8//!
9//! Use the `#[domain_model]` attribute macro to mark domain types:
10//!
11//! ```rust,ignore
12//! use modkit_macros::domain_model;
13//!
14//! #[domain_model]
15//! pub struct User {
16//! pub id: Uuid,
17//! pub email: String,
18//! pub created_at: DateTime<Utc>,
19//! }
20//! ```
21//!
22//! The macro will:
23//! - Implement `DomainModel` for the type
24//! - Validate at macro-expansion time that all fields are free of infrastructure types
25//! - Generate clear error messages if forbidden types are detected
26//!
27//! # Enforcement
28//!
29//! Domain services can use trait bounds to ensure they only work with domain types:
30//!
31//! ```rust,ignore
32//! pub trait UserRepository: Send + Sync {
33//! type Model: DomainModel;
34//!
35//! async fn find(&self, id: Uuid) -> Result<Option<Self::Model>>;
36//! }
37//! ```
38//!
39//! # Validation Strategy
40//!
41//! The `#[domain_model]` macro performs validation by checking field type names against
42//! a list of forbidden patterns (e.g., `sqlx::`, `http::`, `sea_orm::`). This provides
43//! clear error messages at macro expansion time, similar to how `#[api_dto]` validates
44//! its arguments.
45//!
46//! Additional enforcement is provided by Dylint lints:
47//! - `DE0301`: Prohibits infrastructure imports in domain layer
48//! - `DE0308`: Prohibits HTTP types in domain layer
49
50/// Marker trait for domain models (business entities).
51///
52/// Domain models represent core business concepts and should:
53/// - Contain only business-relevant data
54/// - Be independent of persistence mechanisms
55/// - Be free of infrastructure dependencies
56///
57/// # Usage
58///
59/// Use the `#[domain_model]` attribute macro to implement this trait:
60///
61/// ```rust,ignore
62/// #[domain_model]
63/// pub struct Order {
64/// pub id: Uuid,
65/// pub customer_id: Uuid,
66/// pub total: Decimal,
67/// pub status: OrderStatus,
68/// }
69/// ```
70///
71/// The macro validates field types at expansion time and generates clear error
72/// messages if forbidden types (e.g., `sqlx::PgPool`, `http::StatusCode`) are detected.
73///
74/// # Thread Safety
75///
76/// This trait does not require `Send + Sync`. If you need thread-safe domain models,
77/// wrap them in `Arc<T>` at the point of use.
78pub trait DomainModel {}
79
80/// Marker trait for domain errors.
81///
82/// Domain errors represent business rule violations and should not
83/// contain infrastructure-specific error types.
84pub trait DomainErrorMarker: std::error::Error + Send + Sync {}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 // DomainModel is a simple marker trait - no trait bounds to test
91 #[allow(dead_code)]
92 fn assert_domain_model<T: DomainModel>() {}
93
94 #[test]
95 fn test_domain_model_trait_exists() {
96 // Test that the trait is defined and can be used as a bound
97 // Actual validation is done by the #[domain_model] macro
98 fn _accepts_domain_model<T: DomainModel>(_: T) {}
99 }
100}