Skip to main content

EventStore

Struct EventStore 

Source
pub struct EventStore<S, C, A, M = ()> { /* private fields */ }
Expand description

Event store over a single Encode + Decode codec — one terminal for both owning and borrowing codecs.

The owning-vs-borrowing distinction is inferred from the codec’s Decode::Output GAT, not restated at the call site: an owning codec (Output<'a> = E, e.g. serde — one allocation per decoded event) and a borrowing codec (Output<'a> = &'a E, e.g. a #[repr(C)] POD reinterpret — zero allocation) are unified on the load path by the bound Output<'a>: Borrow<E> (std’s Borrow<T> for T and Borrow<T> for &T cover both), and the decoded value is fed to replay via out.borrow() in either case.

§Construction

Created via Store::repository::<A>(), which names the aggregate A once:

let store = Store::new(backend);
let orders = store.repository::<Order>().codec(OrderCodec).build();
let order = orders.load(id).await?;        // AggregateRoot<Order> — inferred
orders.save(&mut order, &events).await?;   // inferred

§Aggregate binding

The aggregate A is a phantom type parameter (carried as PhantomData<fn() -> A>, so the facade is Send + Sync + 'static regardless of A and stays covariant in it). It exists solely so the facade implements Repository<A> for exactly one A: with A fixed on the type, load(id) / save(..) infer the aggregate from the receiver, with no per-call annotation (the blanket-over-A impl that previously defeated inference is gone). A is named once, at repository::<A>(). The substrate Store<S> remains multi-aggregate; mint one cheap per-aggregate facade per aggregate type.

§Schema evolution

The plain load / save path performs no upcasting. For schema evolution, call load_with with the macro-generated function (e.g. OrderTransforms::upcast) on the read path, and save_with with OrderTransforms::current_version on the write path:

// Read path:
let root = es.load_with(id, OrderTransforms::upcast).await?;

// Write path:
es.save_with(&mut root, &events, OrderTransforms::current_version).await?;

§Internal ownership

Owns the codec as Arc<C> and the metadata provider as Arc<M> so async load paths can clone both handles into combinator closures and capture them by value. Per Rust 2024’s stricter capture rules (RFC 3498, rustc issue 133529), a closure that borrows from &self and is then handed to a try_fold-style combinator whose returned future is + Send cannot satisfy the bound — the future-Send check effectively requires the borrow to be 'static. Owning the components via Arc and cloning per call sidesteps the borrow entirely. Cost: one heap allocation at facade construction, one pointer bump per load.

The M = () default keeps every existing call site compiling unchanged; the inert provider always returns None metadata.

Implementations§

Source§

impl<S, C, A, M> EventStore<S, C, A, M>

Source

pub async fn load_with<F, E>( &self, id: A::Id, upcast: F, ) -> Result<AggregateRoot<A>, LoadWithError<S::Error, <C as Encode<EventOf<A>>>::Error, <C as Decode<EventOf<A>>>::Error, E>>
where A: Aggregate, S: RawEventStore + 'static, for<'a> C: Encode<EventOf<A>> + Decode<EventOf<A>, Output<'a>: Borrow<EventOf<A>>> + 'static, F: for<'a> Fn(EventMorsel<'a>) -> Result<EventMorsel<'a>, E> + Send + Sync + 'static, E: Error + Send + Sync + 'static, EventOf<A>: DomainEvent, S::Stream: Send, M: Send + Sync + 'static,

Load an aggregate, running upcast over each persisted event before decoding it.

upcast is the schema-evolution function — typically the associated function the #[mnesis::transforms] macro emits (e.g. OrderTransforms::upcast). Pass it directly as a function pointer; the 'static bound on F and the + Send + Sync bounds are required by the try_fold combinator chain (see the doc comment on EventStore for the full Rust 2024 capture-rules rationale).

§Errors

Returns LoadWithError::Store for any non-upcast error (adapter, codec, kernel) and LoadWithError::Upcast for any error returned by the upcast function.

Source

pub async fn save_with<F, const N: usize>( &self, aggregate: &mut AggregateRoot<A>, events: &Events<EventOf<A>, N>, current_version: F, ) -> Result<S::AllPosition, StoreError<S::Error, <C as Encode<EventOf<A>>>::Error, <C as Decode<EventOf<A>>>::Error>>
where A: Aggregate, S: RawEventStore + 'static, C: Encode<EventOf<A>> + Decode<EventOf<A>> + 'static, F: Fn(&str) -> Option<Version>, EventOf<A>: DomainEvent, M: MetadataProvider<EventOf<A>>,

Persist decided events, stamping the schema version on each via current_version.

current_version is typically the associated function the #[mnesis::transforms] macro emits (e.g. OrderTransforms::current_version). For event types it doesn’t know about, it returns None and the schema version falls back to Version::INITIAL (the same default as the no-upcaster save).

Returns the Position the last event landed at, exactly as save does (#330).

§Errors

The same set of errors save can produce — the schema-version lookup itself is infallible.

Trait Implementations§

Source§

impl<S, C, A, M> Repository<A> for EventStore<S, C, A, M>
where A: Aggregate, S: RawEventStore + 'static, for<'a> C: Encode<EventOf<A>> + Decode<EventOf<A>, Output<'a>: Borrow<EventOf<A>>> + 'static, EventOf<A>: DomainEvent, S::Stream: Send, M: MetadataProvider<EventOf<A>>,

Source§

type Error = StoreError<<S as RawEventStore>::Error, <C as Encode<<<A as Aggregate>::State as AggregateState>::Event>>::Error, <C as Decode<<<A as Aggregate>::State as AggregateState>::Event>>::Error>

The error type for repository operations.
Source§

type Position = <S as RawEventStore>::AllPosition

The $all position save returns — the adapter’s AllPosition, surfaced up from RawEventStore::append (#330). Read more
Source§

async fn load(&self, id: A::Id) -> Result<AggregateRoot<A>, Self::Error>

Load an aggregate by replaying its event stream. Read more
Source§

async fn save<const N: usize>( &self, aggregate: &mut AggregateRoot<A>, events: &Events<EventOf<A>, N>, ) -> Result<Self::Position, Self::Error>

Persist decided events and advance the aggregate’s in-memory state. Read more

Auto Trait Implementations§

§

impl<S, C, A, M> Freeze for EventStore<S, C, A, M>

§

impl<S, C, A, M> RefUnwindSafe for EventStore<S, C, A, M>

§

impl<S, C, A, M> Send for EventStore<S, C, A, M>
where C: Sync + Send, M: Sync + Send, S: Sync + Send,

§

impl<S, C, A, M> Sync for EventStore<S, C, A, M>
where C: Sync + Send, M: Sync + Send, S: Sync + Send,

§

impl<S, C, A, M> Unpin for EventStore<S, C, A, M>

§

impl<S, C, A, M> UnsafeUnpin for EventStore<S, C, A, M>

§

impl<S, C, A, M> UnwindSafe for EventStore<S, C, A, M>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> ArchivePointee for T

Source§

type ArchivedMetadata = ()

The archived version of the pointer metadata for this type.
Source§

fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata

Converts some archived metadata to the pointer metadata for itself.
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<A, R> CommandRepository<A> for R
where A: Aggregate, R: Repository<A>,

Source§

fn execute<C, const N: usize>( &self, root: &mut AggregateRoot<A>, command: C, ) -> impl Future<Output = Result<Execution<A, Self::Position, N>, ExecuteError<A::Error, Self::Error>>> + Send
where A: Handle<C, N>, C: Send,

Decide command against root, persist the decided events atomically, advance root, and return an Execution carrying the read-your-writes position and the decided events. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> LayoutRaw for T

Source§

fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>

Returns the layout of the type.
Source§

impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
where T: SharedNiching<N1, N2>, N1: Niching<T>, N2: Niching<T>,

Source§

unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool

Returns whether the given value has been niched. Read more
Source§

fn resolve_niched(out: Place<NichedOption<T, N1>>)

Writes data to out indicating that a T is niched.
Source§

impl<T> Pointee for T

Source§

type Metadata = ()

The metadata type for pointers and references to this type.
Source§

impl<S, R> SagaRepository<S> for R
where S: Saga, R: Repository<S>,

Source§

fn react_and_save<E, const N: usize>( &self, root: &mut AggregateRoot<S>, event: &E, ) -> impl Future<Output = Result<Reaction<S, Self::Position, N>, SagaError<S::Error, Self::Error>>> + Send
where S: React<E, N>, E: DomainEvent,

Core (single-writer / world A and the base for world B). React to one upstream event against a saga root already in hand, persist any produced own-events atomically, and return their intents pinned to the versions save just assigned. No load — the caller supplies the root. Read more
Source§

fn dispatch<E, const N: usize>( &self, id: S::Id, event: &E, ) -> impl Future<Output = Result<Reaction<S, Self::Position, N>, SagaError<S::Error, Self::Error>>> + Send
where S: React<E, N>, E: DomainEvent,

Convenience (stateless concurrent reactors / world B). load the instance then react_and_save. One call per upstream event; a concurrent writer may cause save to surface Err(SagaError::Store) with is_conflict — the caller reloads and retries. load is whichever Repository<S>::load is in play, so snapshot hydration composes for free. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more