Skip to content

Latest commit

 

History

History
145 lines (111 loc) · 5.03 KB

File metadata and controls

145 lines (111 loc) · 5.03 KB

Changelog

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.

[Unreleased]

Added

  • CheckpointMode::Batched for high-throughput scenarios with configurable batch size and max delay
  • CheckpointMode::batched() and CheckpointMode::batched_default() helper constructors
  • InstanceMode::Coordinated for multi-instance coordination using PostgreSQL advisory locks
  • ProjectionHandler<P> wrapper type for subscribing projections to the event bus
  • SagaHandler<S> wrapper type for subscribing sagas to the event bus
  • Event::to_subset_event_ref() method for reference-based event conversion
  • TryFrom<&D> implementation generated by #[subset_enum] macro for efficient reference-based conversion
  • RefEventStream trait for reference-based event streaming (internal use)
  • SliceRefEventStream for zero-copy event iteration from slices
  • Projection::re_hydrate_from_refs() method for reference-based event stream processing
  • Improved documentation for event ownership model

Changed

  • BREAKING: EventBus::publish now takes Arc<Event<T>> instead of Event<T>
  • BREAKING: EventObserver::on_event now takes Arc<Event<ED>> instead of Event<ED>
  • BREAKING: Projection::apply_and_store now takes &Event<ED> instead of Event<ED>
  • BREAKING: Saga::handle_event now takes &Event<Self::EventType> instead of Event<Self::EventType>
  • BREAKING: Saga::process_event now takes &Event<ED> instead of Event<ED>
  • BREAKING: Projections must now be wrapped in ProjectionHandler to subscribe to the event bus
  • BREAKING: Sagas must now be wrapped in SagaHandler to subscribe to the event bus
  • BREAKING: Projection::EventType now requires TryFrom<&ED, Error = EnumConversionError> instead of TryFrom<ED>
  • BREAKING: Saga::EventType now requires TryFrom<&ED, Error = EnumConversionError> instead of TryFrom<ED>
  • InMemoryEventStore now stores events as Arc<Event<D>> internally for efficient sharing
  • Internal event conversion now uses to_subset_event_ref() for better performance

Removed

  • Blanket EventObserver implementation for Projection (replaced with ProjectionHandler)
  • EventObserver supertrait requirement from Saga trait

Performance

  • Eliminated unnecessary event cloning when publishing to multiple subscribers
  • Events are now shared via Arc instead of cloned for each observer
  • Saga and projection event handlers receive references, avoiding clones
  • #[subset_enum] macro generates TryFrom<&D> which only clones matched variant's fields instead of the entire enum
  • Aggregate::handle now uses SliceRefEventStream to avoid cloning events during internal re-hydration

Migration Guide

Subscribing Projections

// Before
event_bus.subscribe(my_projection).await?;

// After
use epoch::ProjectionHandler;
event_bus.subscribe(ProjectionHandler::new(my_projection)).await?;

Subscribing Sagas

// Before (with manual EventObserver impl)
event_bus.subscribe(my_saga).await?;

// After
use epoch::SagaHandler;
event_bus.subscribe(SagaHandler::new(my_saga)).await?;

Implementing Saga::handle_event

// 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
        // ...
    }
}

Custom EventObserver Implementations

// 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)
}

EventType Trait Bound Change

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())
    }
}

Using Reference-Based Event Conversion

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>()?;