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}