allframe_core/
arch.rs

1//! Clean Architecture enforcement
2//!
3//! This module provides compile-time and runtime support for Clean
4//! Architecture. AllFrame enforces architectural boundaries at compile time,
5//! preventing violations like handlers directly accessing repositories.
6//!
7//! ## Architecture Layers
8//!
9//! ```text
10//! ┌─────────────────────────────────────┐
11//! │  Layer 4: Handler                   │  ← HTTP/gRPC/GraphQL endpoints
12//! │  (depends on Use Cases)             │
13//! └─────────────────────────────────────┘
14//!              ↓
15//! ┌─────────────────────────────────────┐
16//! │  Layer 3: Use Case                  │  ← Application logic
17//! │  (depends on Repositories, Domain)  │
18//! └─────────────────────────────────────┘
19//!              ↓
20//! ┌─────────────────────────────────────┐
21//! │  Layer 2: Repository                │  ← Data access
22//! │  (depends on Domain)                │
23//! └─────────────────────────────────────┘
24//!              ↓
25//! ┌─────────────────────────────────────┐
26//! │  Layer 1: Domain                    │  ← Business logic (no deps)
27//! └─────────────────────────────────────┘
28//! ```
29//!
30//! ## Usage
31//!
32//! ```rust,ignore
33//! use allframe_core::arch::{domain, repository, use_case, handler};
34//!
35//! // Domain entities - pure business logic
36//! #[domain]
37//! struct User {
38//!     id: String,
39//!     email: String,
40//! }
41//!
42//! // Repository - data access
43//! #[repository]
44//! trait UserRepository: Send + Sync {
45//!     async fn find(&self, id: &str) -> Option<User>;
46//! }
47//!
48//! // Use case - application logic
49//! #[use_case]
50//! struct GetUserUseCase {
51//!     repo: Arc<dyn UserRepository>,  // ✅ Can depend on repository
52//! }
53//!
54//! // Handler - entry point
55//! #[handler]
56//! struct GetUserHandler {
57//!     use_case: Arc<GetUserUseCase>,  // ✅ Can depend on use case
58//!     // repo: Arc<dyn UserRepository>, // ❌ COMPILE ERROR - cannot skip use case!
59//! }
60//! ```
61
62// Re-export the architecture macros
63#[cfg(feature = "di")]
64pub use allframe_macros::{domain, handler, repository, use_case};
65
66/// Layer metadata for runtime introspection
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub struct LayerMetadata {
69    /// Layer name
70    pub layer_name: &'static str,
71    /// Layer number (1-4)
72    pub layer_number: u8,
73    /// Type name
74    pub type_name: &'static str,
75}
76
77impl LayerMetadata {
78    /// Create new layer metadata
79    pub const fn new(layer_name: &'static str, layer_number: u8, type_name: &'static str) -> Self {
80        Self {
81            layer_name,
82            layer_number,
83            type_name,
84        }
85    }
86
87    /// Get the layer name
88    pub fn layer_name(&self) -> &'static str {
89        self.layer_name
90    }
91
92    /// Get the layer number
93    pub fn layer_number(&self) -> u8 {
94        self.layer_number
95    }
96
97    /// Get the type name
98    pub fn type_name(&self) -> &'static str {
99        self.type_name
100    }
101
102    /// Check if this layer can depend on another layer
103    pub fn can_depend_on(&self, other_layer: &str) -> bool {
104        let other_number = match other_layer {
105            "domain" => 1,
106            "repository" => 2,
107            "use_case" => 3,
108            "handler" => 4,
109            _ => return false,
110        };
111
112        // A layer can only depend on layers with lower numbers
113        self.layer_number > other_number
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn test_layer_metadata_creation() {
123        let metadata = LayerMetadata::new("domain", 1, "User");
124
125        assert_eq!(metadata.layer_name(), "domain");
126        assert_eq!(metadata.layer_number(), 1);
127        assert_eq!(metadata.type_name(), "User");
128    }
129
130    #[test]
131    fn test_can_depend_on() {
132        let domain = LayerMetadata::new("domain", 1, "User");
133        let repository = LayerMetadata::new("repository", 2, "UserRepository");
134        let use_case = LayerMetadata::new("use_case", 3, "GetUserUseCase");
135        let handler = LayerMetadata::new("handler", 4, "GetUserHandler");
136
137        // Domain cannot depend on anything
138        assert!(!domain.can_depend_on("repository"));
139        assert!(!domain.can_depend_on("use_case"));
140        assert!(!domain.can_depend_on("handler"));
141
142        // Repository can only depend on domain
143        assert!(repository.can_depend_on("domain"));
144        assert!(!repository.can_depend_on("use_case"));
145        assert!(!repository.can_depend_on("handler"));
146
147        // Use case can depend on repository and domain
148        assert!(use_case.can_depend_on("domain"));
149        assert!(use_case.can_depend_on("repository"));
150        assert!(!use_case.can_depend_on("handler"));
151
152        // Handler can depend on all lower layers
153        assert!(handler.can_depend_on("domain"));
154        assert!(handler.can_depend_on("repository"));
155        assert!(handler.can_depend_on("use_case"));
156    }
157}