Skip to main content

allsource_core/domain/value_objects/
system_stream.rs

1use crate::domain::value_objects::{EntityId, EventType, TenantId};
2
3/// System stream namespace prefix.
4///
5/// All system streams use event types starting with `_system.` and
6/// a reserved tenant ID of `_system`. This isolates operational metadata
7/// from user event data while leveraging the same durable storage engine.
8///
9/// # Naming Convention
10///
11/// - Event types: `_system.<domain>.<action>` (e.g., `_system.tenant.created`)
12/// - Entity IDs: `_system:<domain>:<id>` (e.g., `_system:tenant:acme-corp`)
13/// - Tenant ID: `_system` (reserved, rejected for user tenants)
14///
15/// # Inspiration
16///
17/// - Kafka KRaft: `__consumer_offsets`, `__transaction_state`
18/// - CockroachDB: `system.*` tables
19/// - FoundationDB: `\xff` system keyspace
20/// - EventStoreDB: `$` prefix for system streams
21///
22/// The reserved event type prefix for system streams.
23pub const SYSTEM_EVENT_TYPE_PREFIX: &str = "_system.";
24
25/// The reserved tenant ID for system metadata.
26pub const SYSTEM_TENANT_ID: &str = "_system";
27
28/// The reserved entity ID prefix for system streams.
29pub const SYSTEM_ENTITY_ID_PREFIX: &str = "_system:";
30
31/// Known system stream domains.
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
33pub enum SystemDomain {
34    /// Tenant lifecycle: create, update, suspend, reactivate, delete
35    Tenant,
36    /// Audit log: immutable append-only security/compliance log
37    Audit,
38    /// Configuration: key-value settings (log-compacted)
39    Config,
40    /// Schema registry: schema definitions and versions
41    Schema,
42    /// Policies: access policies and retention rules
43    Policy,
44}
45
46impl SystemDomain {
47    /// Get the domain name as used in event types and entity IDs.
48    pub fn as_str(&self) -> &'static str {
49        match self {
50            Self::Tenant => "tenant",
51            Self::Audit => "audit",
52            Self::Config => "config",
53            Self::Schema => "schema",
54            Self::Policy => "policy",
55        }
56    }
57
58    /// All known system domains.
59    pub fn all() -> &'static [SystemDomain] {
60        &[
61            Self::Tenant,
62            Self::Audit,
63            Self::Config,
64            Self::Schema,
65            Self::Policy,
66        ]
67    }
68}
69
70/// System event types for tenant lifecycle.
71pub mod tenant_events {
72    pub const CREATED: &str = "_system.tenant.created";
73    pub const UPDATED: &str = "_system.tenant.updated";
74    pub const SUSPENDED: &str = "_system.tenant.suspended";
75    pub const REACTIVATED: &str = "_system.tenant.reactivated";
76    pub const DELETED: &str = "_system.tenant.deleted";
77    pub const QUOTA_UPDATED: &str = "_system.tenant.quota_updated";
78    pub const USAGE_UPDATED: &str = "_system.tenant.usage_updated";
79}
80
81/// System event types for audit log.
82pub mod audit_events {
83    pub const RECORDED: &str = "_system.audit.recorded";
84}
85
86/// System event types for configuration.
87pub mod config_events {
88    pub const SET: &str = "_system.config.set";
89    pub const DELETED: &str = "_system.config.deleted";
90}
91
92/// System event types for schema registry.
93pub mod schema_events {
94    pub const REGISTERED: &str = "_system.schema.registered";
95    pub const UPDATED: &str = "_system.schema.updated";
96    pub const DELETED: &str = "_system.schema.deleted";
97}
98
99/// System event types for policies.
100pub mod policy_events {
101    pub const CREATED: &str = "_system.policy.created";
102    pub const UPDATED: &str = "_system.policy.updated";
103    pub const DELETED: &str = "_system.policy.deleted";
104}
105
106/// Check whether an event type string belongs to the system namespace.
107pub fn is_system_event_type(event_type: &str) -> bool {
108    event_type.starts_with(SYSTEM_EVENT_TYPE_PREFIX)
109}
110
111/// Check whether an entity ID string belongs to the system namespace.
112pub fn is_system_entity_id(entity_id: &str) -> bool {
113    entity_id.starts_with(SYSTEM_ENTITY_ID_PREFIX)
114}
115
116/// Check whether a tenant ID is the reserved system tenant.
117pub fn is_system_tenant_id(tenant_id: &str) -> bool {
118    tenant_id == SYSTEM_TENANT_ID
119}
120
121/// Build a system entity ID for a given domain and resource ID.
122///
123/// Example: `system_entity_id(SystemDomain::Tenant, "acme-corp")` → `_system:tenant:acme-corp`
124pub fn system_entity_id(domain: SystemDomain, resource_id: &str) -> String {
125    format!(
126        "{}{}:{}",
127        SYSTEM_ENTITY_ID_PREFIX,
128        domain.as_str(),
129        resource_id
130    )
131}
132
133/// Get the system tenant ID as a `TenantId` value object.
134pub fn system_tenant_id() -> TenantId {
135    TenantId::new_unchecked(SYSTEM_TENANT_ID.to_string())
136}
137
138/// Create a system event type from a raw string constant.
139///
140/// Uses `new_unchecked` because system event type constants are compile-time validated.
141pub fn system_event_type(event_type_str: &str) -> EventType {
142    debug_assert!(
143        event_type_str.starts_with(SYSTEM_EVENT_TYPE_PREFIX),
144        "System event type must start with '{}'",
145        SYSTEM_EVENT_TYPE_PREFIX
146    );
147    EventType::new_unchecked(event_type_str.to_string())
148}
149
150/// Create a system entity ID value object.
151pub fn system_entity_id_value(domain: SystemDomain, resource_id: &str) -> EntityId {
152    EntityId::new_unchecked(system_entity_id(domain, resource_id))
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158
159    #[test]
160    fn test_system_event_type_prefix() {
161        assert!(is_system_event_type("_system.tenant.created"));
162        assert!(is_system_event_type("_system.config.set"));
163        assert!(!is_system_event_type("order.placed"));
164        assert!(!is_system_event_type("system.tenant.created"));
165    }
166
167    #[test]
168    fn test_system_entity_id_prefix() {
169        assert!(is_system_entity_id("_system:tenant:acme"));
170        assert!(is_system_entity_id("_system:config:max_conn"));
171        assert!(!is_system_entity_id("user-123"));
172        assert!(!is_system_entity_id("system:tenant:acme"));
173    }
174
175    #[test]
176    fn test_system_tenant_id() {
177        assert!(is_system_tenant_id("_system"));
178        assert!(!is_system_tenant_id("default"));
179        assert!(!is_system_tenant_id("acme-corp"));
180    }
181
182    #[test]
183    fn test_system_entity_id_construction() {
184        let id = system_entity_id(SystemDomain::Tenant, "acme-corp");
185        assert_eq!(id, "_system:tenant:acme-corp");
186
187        let id = system_entity_id(SystemDomain::Config, "max_connections");
188        assert_eq!(id, "_system:config:max_connections");
189
190        let id = system_entity_id(SystemDomain::Audit, "tenant-1");
191        assert_eq!(id, "_system:audit:tenant-1");
192    }
193
194    #[test]
195    fn test_system_domain_all() {
196        let domains = SystemDomain::all();
197        assert_eq!(domains.len(), 5);
198    }
199
200    #[test]
201    fn test_system_event_type_constants_are_valid() {
202        // All system event type constants should pass EventType validation
203        let constants = [
204            tenant_events::CREATED,
205            tenant_events::UPDATED,
206            tenant_events::SUSPENDED,
207            tenant_events::REACTIVATED,
208            tenant_events::DELETED,
209            tenant_events::QUOTA_UPDATED,
210            tenant_events::USAGE_UPDATED,
211            audit_events::RECORDED,
212            config_events::SET,
213            config_events::DELETED,
214            schema_events::REGISTERED,
215            schema_events::UPDATED,
216            schema_events::DELETED,
217            policy_events::CREATED,
218            policy_events::UPDATED,
219            policy_events::DELETED,
220        ];
221
222        for constant in constants {
223            let result = EventType::new(constant.to_string());
224            assert!(
225                result.is_ok(),
226                "System event type '{}' failed validation: {:?}",
227                constant,
228                result.err()
229            );
230        }
231    }
232
233    #[test]
234    fn test_system_tenant_id_is_valid() {
235        let result = TenantId::new(SYSTEM_TENANT_ID.to_string());
236        assert!(result.is_ok(), "System tenant ID should be valid");
237    }
238
239    #[test]
240    fn test_system_entity_ids_are_valid() {
241        for domain in SystemDomain::all() {
242            let id_str = system_entity_id(*domain, "test-resource");
243            let result = EntityId::new(id_str.clone());
244            assert!(
245                result.is_ok(),
246                "System entity ID '{}' failed validation: {:?}",
247                id_str,
248                result.err()
249            );
250        }
251    }
252
253    #[test]
254    fn test_system_event_type_helper() {
255        let et = system_event_type(tenant_events::CREATED);
256        assert_eq!(et.as_str(), "_system.tenant.created");
257    }
258
259    #[test]
260    fn test_system_entity_id_value_helper() {
261        let eid = system_entity_id_value(SystemDomain::Tenant, "acme");
262        assert_eq!(eid.as_str(), "_system:tenant:acme");
263    }
264}