All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
CheckpointMode::Batchedfor high-throughput scenarios with configurable batch size and max delayCheckpointMode::batched()andCheckpointMode::batched_default()helper constructorsInstanceMode::Coordinatedfor multi-instance coordination using PostgreSQL advisory locksProjectionHandler<P>wrapper type for subscribing projections to the event busSagaHandler<S>wrapper type for subscribing sagas to the event busEvent::to_subset_event_ref()method for reference-based event conversionTryFrom<&D>implementation generated by#[subset_enum]macro for efficient reference-based conversionRefEventStreamtrait for reference-based event streaming (internal use)SliceRefEventStreamfor zero-copy event iteration from slicesProjection::re_hydrate_from_refs()method for reference-based event stream processing- Improved documentation for event ownership model
- BREAKING:
EventBus::publishnow takesArc<Event<T>>instead ofEvent<T> - BREAKING:
EventObserver::on_eventnow takesArc<Event<ED>>instead ofEvent<ED> - BREAKING:
Projection::apply_and_storenow takes&Event<ED>instead ofEvent<ED> - BREAKING:
Saga::handle_eventnow takes&Event<Self::EventType>instead ofEvent<Self::EventType> - BREAKING:
Saga::process_eventnow takes&Event<ED>instead ofEvent<ED> - BREAKING: Projections must now be wrapped in
ProjectionHandlerto subscribe to the event bus - BREAKING: Sagas must now be wrapped in
SagaHandlerto subscribe to the event bus - BREAKING:
Projection::EventTypenow requiresTryFrom<&ED, Error = EnumConversionError>instead ofTryFrom<ED> - BREAKING:
Saga::EventTypenow requiresTryFrom<&ED, Error = EnumConversionError>instead ofTryFrom<ED> InMemoryEventStorenow stores events asArc<Event<D>>internally for efficient sharing- Internal event conversion now uses
to_subset_event_ref()for better performance
- Blanket
EventObserverimplementation forProjection(replaced withProjectionHandler) EventObserversupertrait requirement fromSagatrait
- Eliminated unnecessary event cloning when publishing to multiple subscribers
- Events are now shared via
Arcinstead of cloned for each observer - Saga and projection event handlers receive references, avoiding clones
#[subset_enum]macro generatesTryFrom<&D>which only clones matched variant's fields instead of the entire enumAggregate::handlenow usesSliceRefEventStreamto avoid cloning events during internal re-hydration
// Before
event_bus.subscribe(my_projection).await?;
// After
use epoch::ProjectionHandler;
event_bus.subscribe(ProjectionHandler::new(my_projection)).await?;// Before (with manual EventObserver impl)
event_bus.subscribe(my_saga).await?;
// After
use epoch::SagaHandler;
event_bus.subscribe(SagaHandler::new(my_saga)).await?;// Before
async fn handle_event(
&self,
state: Self::State,
event: Event<Self::EventType>,
) -> Result<Option<Self::State>, Self::SagaError> {
match event.data {
// ...
}
}
// After
async fn handle_event(
&self,
state: Self::State,
event: &Event<Self::EventType>, // Now takes reference
) -> Result<Option<Self::State>, Self::SagaError> {
match &event.data { // Borrow the data
// ...
}
}// Before
async fn on_event(&self, event: Event<ED>) -> Result<(), ...> {
// use event
}
// After
async fn on_event(&self, event: Arc<Event<ED>>) -> Result<(), ...> {
// use &*event or event.field (auto-deref)
}Projection::EventType and Saga::EventType now require TryFrom<&ED, Error = EnumConversionError>
instead of TryFrom<ED>. This enables efficient reference-based conversion internally.
For subset enums generated by #[subset_enum], this is automatic - the macro generates both impls.
For identity conversions (where EventType == ED), add this impl:
impl TryFrom<&MyEvent> for MyEvent {
type Error = EnumConversionError;
fn try_from(value: &MyEvent) -> Result<Self, Self::Error> {
Ok(value.clone())
}
}The #[subset_enum] macro now generates TryFrom<&D> in addition to TryFrom<D>.
The framework internally uses to_subset_event_ref() for efficient conversion that
only clones matched variant fields instead of the entire enum.
You can also use it directly:
// Reference-based conversion (only clones matched variant's fields)
let subset_event = event.to_subset_event_ref::<SubsetEvent>()?;