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