code
docs
tests
The Event Sourcing Library supports generating, storing and replaying two types of events: application-events and domain-events.
Application events are bound to Usecase and are produced by execution of specific methods (ones with ApplicationEvent
as their first parameter).
Each application event holds information about Usecase, method name and JSON serialized values of method parameters.
Domain events are bound to entity instances and are produced by execution of annotated (see @DomainEvent
) methods that belongs to EntityComposite
.
Each domain event (see DomainEventValue
) holds information about entity type, identity, method name and JSON serialized values of method parameters.
Both application and domain events are captured during UnitOfWork
lifetime and are stored in EventStore
after successfully completed UnitOfWork
as collection together (see UnitOfWorkDomainEventsValue
and TransactionApplicationEvents
).
There is support for replaying events. When events are replayed the same code is executed but no new events are generated.
There are helper classes that enables a service to easily track events feed, and for domain events there is EventRouter
that allow to specify specification→receiver routes.
JDBM backed store
EventStore supports indexed and streamed access to events feed. There is in-memory and JDBM backed implementations.
code
docs
tests
REST access
For remote access to feed there is eventsourcing-rest
library that exposes events as Atom feeds.
code
docs
tests
Assembly is done as follows:
new EventsourcingAssembler() .withApplicationEvents() .withCurrentUserFromUOWPrincipal() .assemble(module);
Configure application events store:
module.services( MemoryApplicationEventStoreService.class );
Actual method on composite which execution emits application event.
First parameter is null
on "normal" execution.
If it is not null
, then the method call is a replay of previously created events.
@Mixins( Users.Mixin.class ) public interface Users extends TransientComposite { void signup( @Optional ApplicationEvent evt, String username, List<String> mailinglists );
To enable execution capturing, you have to configure composite with concern:
module.transients( Users.class ).withConcerns( ApplicationEventCreationConcern.class );
Assembly:
new EventsourcingAssembler() .withDomainEvents() .withCurrentUserFromUOWPrincipal() .assemble(module);
Configure domain events store:
module.services( MemoryEventStoreService.class );
Annotate your entity state changing methods. Event methods may only change state. They may not fail or thrown exceptions:
@Mixins( TestEntity.Mixin.class ) public interface TestEntity extends EntityComposite { @UseDefaults Property<String> description(); @DomainEvent void changedDescription( String newName ); abstract class Mixin implements TestEntity { public void changedDescription( String newName ) { description().set( newName ); } } }
To enable method execution capturing, you have to configure entity with concern:
module.entities( TestEntity.class ).withConcerns(DomainEventCreationConcern.class);