Skip to main content

backbone_core/
domain_service.rs

1//! Domain Service Trait
2//!
3//! Base trait for all domain services in the Backbone Framework.
4//! Domain services encapsulate business logic that doesn't naturally
5//! belong to a single entity.
6//!
7//! # Example
8//!
9//! ```ignore
10//! use backbone_core::DomainService;
11//!
12//! pub struct PaymentService {
13//!     // dependencies
14//! }
15//!
16//! #[async_trait::async_trait]
17//! impl DomainService for PaymentService {
18//!     fn service_id(&self) -> &'static str {
19//!         "payment.payment_service"
20//!     }
21//!
22//!     async fn health_check(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
23//!         // Check payment gateway connectivity
24//!         Ok(())
25//!     }
26//! }
27//! ```
28
29use async_trait::async_trait;
30
31/// Base trait for all domain services.
32///
33/// Domain services are stateless operations that coordinate between
34/// multiple entities or handle complex business logic.
35#[async_trait]
36pub trait DomainService: Send + Sync {
37    /// Unique identifier for this service.
38    ///
39    /// Format: `{module}.{service_name}`
40    /// Example: `"corpus.organization_service"`
41    fn service_id(&self) -> &'static str;
42
43    /// Perform a health check for this service.
44    ///
45    /// Returns `Ok(())` if the service is healthy,
46    /// or an error describing the issue.
47    async fn health_check(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
48        Ok(())
49    }
50
51    /// Get the service name (derived from service_id).
52    fn service_name(&self) -> &'static str {
53        self.service_id()
54            .split('.')
55            .next_back()
56            .unwrap_or(self.service_id())
57    }
58
59    /// Get the module name (derived from service_id).
60    fn module_name(&self) -> &'static str {
61        self.service_id()
62            .split('.')
63            .next()
64            .unwrap_or("unknown")
65    }
66}
67
68/// Marker trait for domain services that support transactions.
69#[async_trait]
70pub trait TransactionalDomainService: DomainService {
71    /// Execute an operation within a transaction context.
72    async fn with_transaction<F, R, E>(&self, operation: F) -> Result<R, E>
73    where
74        F: FnOnce() -> Result<R, E> + Send,
75        R: Send,
76        E: Send;
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    struct TestService;
84
85    #[async_trait]
86    impl DomainService for TestService {
87        fn service_id(&self) -> &'static str {
88            "test.test_service"
89        }
90    }
91
92    #[test]
93    fn test_service_name_extraction() {
94        let service = TestService;
95        assert_eq!(service.service_name(), "test_service");
96        assert_eq!(service.module_name(), "test");
97    }
98}