Skip to main content

allsource_core/infrastructure/di/
builder.rs

1//! Container Builder - Fluent API for constructing ServiceContainer
2//!
3//! Provides a builder pattern for wiring up repositories and creating
4//! a fully configured ServiceContainer.
5
6use super::ServiceContainer;
7use crate::{
8    application::services::consumer::ConsumerRegistry,
9    domain::repositories::{
10        AccessTokenRepository, ArticleRepository, AuditEventRepository, CreatorRepository,
11        EventStreamRepository, ForkRepository, TenantRepository, TransactionRepository,
12    },
13    infrastructure::{
14        persistence::{SystemMetadataStore, SystemRepositories},
15        repositories::{
16            EventSourcedConfigRepository, InMemoryAccessTokenRepository, InMemoryArticleRepository,
17            InMemoryCreatorRepository, InMemoryEventStreamRepository, InMemoryForkRepository,
18            InMemoryTransactionRepository,
19        },
20    },
21};
22use std::sync::Arc;
23
24/// Builder for constructing a ServiceContainer with all dependencies.
25///
26/// # Example
27///
28/// ```rust,ignore
29/// // Development with in-memory repositories
30/// let container = ContainerBuilder::new()
31///     .with_in_memory_repositories()
32///     .build();
33///
34/// // Testing with custom mock repositories
35/// let container = ContainerBuilder::new()
36///     .with_creator_repository(Arc::new(mock_creator_repo))
37///     .with_article_repository(Arc::new(mock_article_repo))
38///     .with_transaction_repository(Arc::new(mock_transaction_repo))
39///     .with_access_token_repository(Arc::new(mock_access_repo))
40///     .with_fork_repository(Arc::new(mock_fork_repo))
41///     .with_event_stream_repository(Arc::new(mock_event_stream_repo))
42///     .build();
43/// ```
44#[derive(Default)]
45pub struct ContainerBuilder {
46    creator_repository: Option<Arc<dyn CreatorRepository>>,
47    article_repository: Option<Arc<dyn ArticleRepository>>,
48    transaction_repository: Option<Arc<dyn TransactionRepository>>,
49    access_token_repository: Option<Arc<dyn AccessTokenRepository>>,
50    fork_repository: Option<Arc<dyn ForkRepository>>,
51    event_stream_repository: Option<Arc<dyn EventStreamRepository>>,
52
53    // System metadata (event-sourced)
54    tenant_repository: Option<Arc<dyn TenantRepository>>,
55    audit_repository: Option<Arc<dyn AuditEventRepository>>,
56    config_repository: Option<Arc<EventSourcedConfigRepository>>,
57    system_store: Option<Arc<SystemMetadataStore>>,
58    consumer_registry: Option<Arc<ConsumerRegistry>>,
59}
60
61impl ContainerBuilder {
62    /// Create a new empty builder.
63    pub fn new() -> Self {
64        Self::default()
65    }
66
67    /// Configure all repositories with in-memory implementations.
68    ///
69    /// This is the recommended setup for development and testing.
70    /// For production, use specific repository setters with persistent implementations.
71    pub fn with_in_memory_repositories(mut self) -> Self {
72        self.creator_repository = Some(Arc::new(InMemoryCreatorRepository::new()));
73        self.article_repository = Some(Arc::new(InMemoryArticleRepository::new()));
74        self.transaction_repository = Some(Arc::new(InMemoryTransactionRepository::new()));
75        self.access_token_repository = Some(Arc::new(InMemoryAccessTokenRepository::new()));
76        self.fork_repository = Some(Arc::new(InMemoryForkRepository::new()));
77        self.event_stream_repository = Some(Arc::new(InMemoryEventStreamRepository::new()));
78        self
79    }
80
81    /// Set a custom creator repository.
82    pub fn with_creator_repository(mut self, repository: Arc<dyn CreatorRepository>) -> Self {
83        self.creator_repository = Some(repository);
84        self
85    }
86
87    /// Set a custom article repository.
88    pub fn with_article_repository(mut self, repository: Arc<dyn ArticleRepository>) -> Self {
89        self.article_repository = Some(repository);
90        self
91    }
92
93    /// Set a custom transaction repository.
94    pub fn with_transaction_repository(
95        mut self,
96        repository: Arc<dyn TransactionRepository>,
97    ) -> Self {
98        self.transaction_repository = Some(repository);
99        self
100    }
101
102    /// Set a custom access token repository.
103    pub fn with_access_token_repository(
104        mut self,
105        repository: Arc<dyn AccessTokenRepository>,
106    ) -> Self {
107        self.access_token_repository = Some(repository);
108        self
109    }
110
111    /// Set a custom fork repository.
112    pub fn with_fork_repository(mut self, repository: Arc<dyn ForkRepository>) -> Self {
113        self.fork_repository = Some(repository);
114        self
115    }
116
117    /// Set a custom event stream repository.
118    pub fn with_event_stream_repository(
119        mut self,
120        repository: Arc<dyn EventStreamRepository>,
121    ) -> Self {
122        self.event_stream_repository = Some(repository);
123        self
124    }
125
126    /// Wire event-sourced system repositories from a `SystemRepositories` instance.
127    ///
128    /// This replaces in-memory metadata storage with durable, event-sourced
129    /// repositories backed by AllSource's own WAL. Call this after
130    /// `SystemBootstrap::initialize()` succeeds.
131    ///
132    /// If not called, the container will have no system repositories (backward-compatible).
133    pub fn with_system_repositories(mut self, repos: SystemRepositories) -> Self {
134        self.system_store = Some(repos.system_store);
135        self.tenant_repository = Some(repos.tenant_repository);
136        self.audit_repository = Some(repos.audit_repository);
137        self.config_repository = Some(repos.config_repository);
138        self.consumer_registry = Some(repos.consumer_registry);
139        self
140    }
141
142    /// Set a custom tenant repository.
143    pub fn with_tenant_repository(mut self, repository: Arc<dyn TenantRepository>) -> Self {
144        self.tenant_repository = Some(repository);
145        self
146    }
147
148    /// Set a custom audit repository.
149    pub fn with_audit_repository(mut self, repository: Arc<dyn AuditEventRepository>) -> Self {
150        self.audit_repository = Some(repository);
151        self
152    }
153
154    /// Build the ServiceContainer.
155    ///
156    /// # Panics
157    ///
158    /// Panics if any required repository has not been set.
159    /// Use `with_in_memory_repositories()` to set all repositories at once,
160    /// or set each one individually.
161    pub fn build(self) -> ServiceContainer {
162        let mut container = ServiceContainer::new(
163            self.creator_repository
164                .expect("CreatorRepository not configured. Use with_in_memory_repositories() or with_creator_repository()"),
165            self.article_repository
166                .expect("ArticleRepository not configured. Use with_in_memory_repositories() or with_article_repository()"),
167            self.transaction_repository
168                .expect("TransactionRepository not configured. Use with_in_memory_repositories() or with_transaction_repository()"),
169            self.access_token_repository
170                .expect("AccessTokenRepository not configured. Use with_in_memory_repositories() or with_access_token_repository()"),
171            self.fork_repository
172                .expect("ForkRepository not configured. Use with_in_memory_repositories() or with_fork_repository()"),
173            self.event_stream_repository
174                .expect("EventStreamRepository not configured. Use with_in_memory_repositories() or with_event_stream_repository()"),
175        );
176
177        container.tenant_repository = self.tenant_repository;
178        container.audit_repository = self.audit_repository;
179        container.config_repository = self.config_repository;
180        container.system_store = self.system_store;
181        container.consumer_registry = self.consumer_registry;
182
183        container
184    }
185
186    /// Try to build the ServiceContainer, returning an error if any repository is missing.
187    ///
188    /// This is a safer alternative to `build()` that doesn't panic.
189    pub fn try_build(self) -> Result<ServiceContainer, ContainerBuilderError> {
190        let mut container = ServiceContainer::new(
191            self.creator_repository
192                .ok_or(ContainerBuilderError::MissingRepository(
193                    "CreatorRepository",
194                ))?,
195            self.article_repository
196                .ok_or(ContainerBuilderError::MissingRepository(
197                    "ArticleRepository",
198                ))?,
199            self.transaction_repository
200                .ok_or(ContainerBuilderError::MissingRepository(
201                    "TransactionRepository",
202                ))?,
203            self.access_token_repository
204                .ok_or(ContainerBuilderError::MissingRepository(
205                    "AccessTokenRepository",
206                ))?,
207            self.fork_repository
208                .ok_or(ContainerBuilderError::MissingRepository("ForkRepository"))?,
209            self.event_stream_repository
210                .ok_or(ContainerBuilderError::MissingRepository(
211                    "EventStreamRepository",
212                ))?,
213        );
214
215        container.tenant_repository = self.tenant_repository;
216        container.audit_repository = self.audit_repository;
217        container.config_repository = self.config_repository;
218        container.system_store = self.system_store;
219        container.consumer_registry = self.consumer_registry;
220
221        Ok(container)
222    }
223}
224
225/// Error returned when building a container fails.
226#[derive(Debug, Clone)]
227pub enum ContainerBuilderError {
228    /// A required repository was not configured.
229    MissingRepository(&'static str),
230}
231
232impl std::fmt::Display for ContainerBuilderError {
233    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234        match self {
235            ContainerBuilderError::MissingRepository(name) => {
236                write!(f, "{name} not configured")
237            }
238        }
239    }
240}
241
242impl std::error::Error for ContainerBuilderError {}