evidentsource_core/lib.rs
1//! EvidentSource Core Library
2//!
3//! This crate provides the core domain types and traits for the EvidentSource
4//! event sourcing platform.
5
6pub mod domain;
7
8use std::future::Future;
9
10use chrono::{DateTime, Utc};
11use futures::Stream;
12use nonempty::NonEmpty;
13
14// Re-export key event types from the domain
15pub use domain::{Event, EventData, ExtensionValue, ProspectiveEvent};
16
17use domain::{
18 AppendCondition, CommandRequest, DatabaseError, DatabaseName, EventAttribute, EventSelector,
19 QueryOptions, Revision, StateChangeError, StateChangeName, StateChangeVersion, StateView,
20 StateViewError, StateViewName, StateViewVersion, Transaction, TransactionSummary,
21};
22
23/// Basic database identity information.
24pub trait DatabaseIdentity: Send + Sync {
25 /// Get the database name.
26 fn name(&self) -> &DatabaseName;
27
28 /// Get the timestamp when this database was created.
29 fn created_at(&self) -> DateTime<Utc>;
30}
31
32/// Catalog of available databases.
33pub trait DatabaseCatalog {
34 /// The type returned when creating a database.
35 type Identity: DatabaseIdentity;
36
37 /// List all databases in the catalog.
38 fn list_databases(&self) -> impl Stream<Item = DatabaseName>;
39
40 /// Create a new database with the given name.
41 fn create_database(
42 &self,
43 name: DatabaseName,
44 ) -> impl Future<Output = Result<Self::Identity, DatabaseError>>;
45
46 /// Delete a database by name.
47 fn delete_database(
48 &self,
49 name: DatabaseName,
50 ) -> impl Future<Output = Result<(), DatabaseError>>;
51}
52
53/// Provides access to database views at various revisions.
54pub trait DatabaseProvider: DatabaseIdentity {
55 /// The type of database view at a specific revision.
56 type AtRevision: DatabaseAtRevision;
57
58 /// Get the latest local database
59 fn local_database(&self) -> Self::AtRevision;
60
61 /// Get the latest revision on the server committed to storage.
62 fn latest_database(&self) -> impl Future<Output = Result<Self::AtRevision, DatabaseError>>;
63
64 /// Get the database at a specific revision, awaiting if necessary.
65 fn database_at_revision(
66 &self,
67 revision: Revision,
68 ) -> impl Future<Output = Result<Self::AtRevision, DatabaseError>>;
69
70 /// Get the database at the revision effective at a specific timestamp.
71 fn database_at_timestamp(
72 &self,
73 revision_timestamp: DateTime<Utc>,
74 ) -> impl Future<Output = Result<Self::AtRevision, DatabaseError>>;
75}
76
77/// A database connection capable of performing transactions.
78pub trait DatabaseConnection: DatabaseProvider {
79 /// Transact events with append conditions.
80 ///
81 /// The transaction must contain at least one event. Conditions are checked
82 /// against the current database state before committing.
83 ///
84 /// See: <https://dcb.events/specification/> for the DCB append condition spec.
85 fn transact(
86 &self,
87 events: NonEmpty<ProspectiveEvent>,
88 conditions: Vec<AppendCondition>,
89 ) -> impl Future<Output = Result<Self::AtRevision, DatabaseError>>;
90
91 /// Transact events with a custom transaction ID.
92 ///
93 /// This is useful for idempotency - if a transaction with the same ID has
94 /// already been committed, the existing result is returned.
95 fn transact_with_id(
96 &self,
97 transaction_id: &str,
98 events: NonEmpty<ProspectiveEvent>,
99 conditions: Vec<AppendCondition>,
100 ) -> impl Future<Output = Result<Self::AtRevision, DatabaseError>>;
101
102 /// Execute a state change.
103 ///
104 /// This invokes the named state change component with the given request
105 /// and commits any resulting events.
106 fn execute_state_change(
107 &self,
108 name: &StateChangeName,
109 version: StateChangeVersion,
110 request: CommandRequest,
111 ) -> impl Future<Output = Result<Self::AtRevision, StateChangeError>>;
112
113 /// Get the transaction log (summary only).
114 fn log(&self) -> impl Stream<Item = TransactionSummary>;
115
116 /// Get the transaction log with full event details.
117 fn log_detail(&self) -> impl Stream<Item = Transaction>;
118
119 /// Get the transaction log starting from a specific revision.
120 ///
121 /// This returns transaction summaries for all transactions committed at or
122 /// after the specified revision.
123 fn log_from(&self, from_revision: Revision) -> impl Stream<Item = TransactionSummary>;
124
125 /// Get the transaction log with full event details, starting from a specific revision.
126 ///
127 /// This returns full transaction details (including events) for all transactions
128 /// committed at or after the specified revision.
129 fn log_detail_from(&self, from_revision: Revision) -> impl Stream<Item = Transaction>;
130}
131
132/// A database view at a specific revision.
133pub trait DatabaseAtRevision: DatabaseIdentity + Clone {
134 /// The type returned when scoping to an effective timestamp.
135 type EffectiveTimestampView: DatabaseAtRevisionAndEffectiveTimestamp;
136
137 /// The type returned for speculative queries.
138 type Speculative: SpeculativeDatabase;
139
140 /// Get this view's revision number.
141 fn revision(&self) -> Revision;
142
143 /// Get the timestamp when this revision was committed.
144 fn revision_timestamp(&self) -> DateTime<Utc>;
145
146 /// Get a view scoped to a specific effective timestamp (for bi-temporal queries).
147 fn at_effective_timestamp(
148 &self,
149 effective_timestamp: DateTime<Utc>,
150 ) -> Self::EffectiveTimestampView;
151
152 /// Get a view at a different revision.
153 ///
154 /// This allows navigating to an earlier or later revision of the database.
155 fn at_revision(&self, revision: Revision) -> impl Future<Output = Self>;
156
157 /// Create a speculative view with additional uncommitted events.
158 fn speculate_with_transaction(&self, events: NonEmpty<ProspectiveEvent>) -> Self::Speculative;
159
160 /// Query events matching a selector.
161 fn query_events(&self, selector: &EventSelector) -> impl Stream<Item = Event>;
162
163 /// Query events matching a selector with options.
164 ///
165 /// This allows controlling the query direction and limiting results.
166 ///
167 /// # Example
168 ///
169 /// ```ignore
170 /// use evidentsource_core::domain::{EventSelector, QueryOptions, QueryDirection};
171 ///
172 /// // Get the 10 most recent events
173 /// let selector = EventSelector::stream_equals("my-stream").unwrap();
174 /// let options = QueryOptions::new().reverse().limit(10);
175 /// let events = db.query_events_with_options(&selector, options);
176 /// ```
177 fn query_events_with_options(
178 &self,
179 selector: &EventSelector,
180 options: QueryOptions,
181 ) -> impl Stream<Item = Event>;
182
183 /// Fetch a state view.
184 fn view_state(
185 &self,
186 name: &StateViewName,
187 version: StateViewVersion,
188 ) -> impl Future<Output = Result<StateView, StateViewError>>;
189
190 /// Fetch a state view with parameter bindings.
191 ///
192 /// Parameters are used to filter state views by specific attributes.
193 /// Common parameter patterns:
194 /// - `("subject.account_id", EventAttribute::Subject(Some(...)))` - filter by subject
195 /// - `("stream.processing_day_stream", EventAttribute::Stream(...))` - filter by stream
196 fn view_state_with_params(
197 &self,
198 name: &StateViewName,
199 version: StateViewVersion,
200 params: &[(String, EventAttribute)],
201 ) -> impl Future<Output = Result<StateView, StateViewError>>;
202}
203
204/// A database view at both a revision and effective timestamp.
205///
206/// This enables bi-temporal queries where you want to see the database
207/// state as it was understood at a particular point in effective time.
208pub trait DatabaseAtRevisionAndEffectiveTimestamp: DatabaseAtRevision {
209 /// The type of the base revision view.
210 type Basis: DatabaseAtRevision;
211
212 /// Get the base database view (without effective timestamp scope).
213 fn basis(&self) -> &Self::Basis;
214
215 /// Get the effective timestamp this view is scoped to.
216 fn effective_timestamp(&self) -> DateTime<Utc>;
217
218 /// Get a view at a different revision while keeping the same effective timestamp.
219 ///
220 /// This is an async operation because fetching a different revision may require
221 /// a remote call (e.g., gRPC) to retrieve the database state at that revision.
222 fn at_revision_with_effective_timestamp(
223 &self,
224 revision: Revision,
225 ) -> impl Future<Output = Result<Self, DatabaseError>>;
226}
227
228/// A speculative database view with uncommitted events.
229///
230/// This allows querying the database as if certain events had been committed,
231/// useful for validation and preview scenarios.
232pub trait SpeculativeDatabase: DatabaseAtRevision {
233 /// The type of the committed view this speculation is based on.
234 type Basis: DatabaseAtRevision;
235
236 /// Get the committed view this speculation is based on.
237 fn basis(&self) -> &Self::Basis;
238
239 /// Get the speculated (uncommitted) event transactions.
240 fn speculated_transactions(&self) -> &NonEmpty<NonEmpty<ProspectiveEvent>>;
241}