Class FlexAggregate<ID,AGGREGATE_TYPE extends FlexAggregate<ID,AGGREGATE_TYPE>>
- java.lang.Object
-
- dk.cloudcreate.essentials.components.eventsourced.aggregates.flex.FlexAggregate<ID,AGGREGATE_TYPE>
-
- Type Parameters:
ID- The id type for the aggregate idAGGREGATE_TYPE- the type of the aggregate
- All Implemented Interfaces:
Aggregate<ID,AGGREGATE_TYPE>
public abstract class FlexAggregate<ID,AGGREGATE_TYPE extends FlexAggregate<ID,AGGREGATE_TYPE>> extends Object implements Aggregate<ID,AGGREGATE_TYPE>
Simple, easy and opinionated aggregate that allows you to apply events that don't have any requirements with regards to inheritance (i.e. you can use Records) in combination with anFlexAggregateRepository
Events
New Aggregate instancepublic record OrderAdded(OrderId orderId, CustomerId orderingCustomerId, long orderNumber) { } public static class OrderAccepted { public final OrderId orderId; public OrderAccepted(OrderId orderId) { this.orderId = orderId; } }
The method that creates a new aggregate instance may be a static method that returns aEventsToPersistinstance:
Where the createNewOrder static methods looks like:unitOfWorkFactory.usingUnitOfWork(unitOfWork -> { var eventsToPersist = Order.createNewOrder(orderId, CustomerId.random(), 123); repository.persist(eventsToPersist); });
Aggregate command methodspublic static EventsToPersist<OrderId> createNewOrder(OrderId orderId, CustomerId orderingCustomerId, int orderNumber) { FailFast.requireNonNull(orderId, "You must provide an orderId"); FailFast.requireNonNull(orderingCustomerId, "You must provide an orderingCustomerId"); return initialAggregateEvents(orderId, new OrderAdded(orderId, orderingCustomerId, orderNumber)); }
Each individual command method MUST also return aEventsToPersistinstance that can will usually be use in in a call toFlexAggregateRepository.persist(EventsToPersist)
And where the accept() command method looks like this:FlexAggregateRepository<OrderId, Order> repository = FlexAggregateRepository.from( eventStores, standardSingleTenantConfigurationUsingJackson( AggregateType.of("Orders"), createObjectMapper(), AggregateIdSerializer.serializerFor(OrderId.class), IdentifierColumnType.UUID, JSONColumnType.JSONB), unitOfWorkFactory, OrderId.class, Order.class ); unitOfWorkFactory.usingUnitOfWork(unitOfWork -> { var order = repository.load(orderId); var eventsToPersist = order.accept(); repository.persist(eventsToPersist); });public EventsToPersist<OrderId> accept() { if (accepted) { return noEvents(); } return events(new OrderAccepted(aggregateId())); }
Command method chaining
Command methods can be chained like this:
Aggregate event handling methodsunitOfWorkFactory.usingUnitOfWork(unitOfWork -> { var eventsToPersist = Order.createNewOrder(orderId, CustomerId.random(), 123); eventsToPersist = eventsToPersist.append(new Order().rehydrate(eventsToPersist).addProduct(productId, 10)); repository.persist(eventsToPersist); });When an aggregate is loaded from the
EventStoreusing theFlexAggregateRepository, e.g. usingFlexAggregateRepository.load(Object)orFlexAggregateRepository.tryLoad(Object), the repository will return anFlexAggregateinstance that has had all events returned from theEventStoreapplied to it.
What happens internally is that theFlexAggregateRepository.load(Object)method will callrehydrate(AggregateEventStream), which in-order will callapplyRehydratedEventToTheAggregate(Object)for each event returned from the EventStore.
You can either choose to implement the event matching using instanceof:{@code- See Also:
FlexAggregateRepository
-
-
Constructor Summary
Constructors Constructor Description FlexAggregate()
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description IDaggregateId()The id of the aggregate (aka.protected voidapplyRehydratedEventToTheAggregate(Object event)Apply the previously recorded/persisted event (aka historic event) to the aggregate instance to reflect the event as a state change to the aggregate
The default implementation will automatically call any (private) methods annotated withEventHandlerdk.cloudcreate.essentials.components.eventsourced.eventstore.postgresql.types.EventOrdereventOrderOfLastRehydratedEvent()Get the eventOrder of the last event that was applied to theFlexAggregateUsing:rehydrate(AggregateEventStream)rehydrate(EventsToPersist)rehydrate(Object, Stream)rehydrate(Object, List)protected EventsToPersist<ID,Object>events(Object... eventsToPersist)Wrap the events that should be persisted as a side effect of a command methodbooleanhasBeenRehydrated()Has the aggregate been initialized using previously recorded/persisted events?protected voidinitialize()static <ID> EventsToPersist<ID,Object>newAggregateEvents(ID aggregateId, Object... eventsToPersist)Wrap the events that should be persisted for this new aggregateprotected EventsToPersist<ID,Object>noEvents()Creates an emptyEventsToPersistwhich is handy for a commanded method didn't have a side-effect (e.g.AGGREGATE_TYPErehydrate(EventsToPersist<ID,Object> eventsToPersist)Initialize an aggregate instance fromEventsToPersist
Effectively performs a leftFold over all the previous events related to this aggregate instanceAGGREGATE_TYPErehydrate(dk.cloudcreate.essentials.components.eventsourced.eventstore.postgresql.eventstream.AggregateEventStream<ID> persistedEvents)Initialize an aggregate instance from historic events.
Effectively performs a leftFold over all the previous persisted events related to this aggregate instanceAGGREGATE_TYPErehydrate(ID aggregateId, List<Object> historicEvents)AGGREGATE_TYPErehydrate(ID aggregateId, Stream<Object> persistedEvents)Effectively performs a leftFold over all the previous events related to this aggregate instance
-
-
-
Method Detail
-
initialize
protected void initialize()
-
rehydrate
public AGGREGATE_TYPE rehydrate(dk.cloudcreate.essentials.components.eventsourced.eventstore.postgresql.eventstream.AggregateEventStream<ID> persistedEvents)
Initialize an aggregate instance from historic events.
Effectively performs a leftFold over all the previous persisted events related to this aggregate instance- Specified by:
rehydratein interfaceAggregate<ID,AGGREGATE_TYPE extends FlexAggregate<ID,AGGREGATE_TYPE>>- Parameters:
persistedEvents- the previous recorded/persisted events related to this aggregate instance, aka. the aggregates history- Returns:
- the aggregate instance
-
rehydrate
public AGGREGATE_TYPE rehydrate(EventsToPersist<ID,Object> eventsToPersist)
Initialize an aggregate instance fromEventsToPersist
Effectively performs a leftFold over all the previous events related to this aggregate instance- Parameters:
eventsToPersist-- Returns:
- the aggregate instance
-
rehydrate
public AGGREGATE_TYPE rehydrate(ID aggregateId, List<Object> historicEvents)
-
rehydrate
public AGGREGATE_TYPE rehydrate(ID aggregateId, Stream<Object> persistedEvents)
Effectively performs a leftFold over all the previous events related to this aggregate instance- Parameters:
persistedEvents- the previous recorded/persisted events related to this aggregate instance, aka. the aggregates history- Returns:
- the aggregate instance
-
newAggregateEvents
public static <ID> EventsToPersist<ID,Object> newAggregateEvents(ID aggregateId, Object... eventsToPersist)
Wrap the events that should be persisted for this new aggregate- Type Parameters:
ID- the aggregate id type- Parameters:
aggregateId- the aggregate id this relates toeventsToPersist- the events to persist, which will be the result/side-effect of a command method invocation in anFlexAggregate). May be empty if the command method invocation didn't result in any events (e.g. due to idempotency checks)
-
events
protected EventsToPersist<ID,Object> events(Object... eventsToPersist)
Wrap the events that should be persisted as a side effect of a command method- Parameters:
eventsToPersist- the events to persist, which will be the result/side-effect of a command method invocation in anFlexAggregate). May be empty if the command method invocation didn't result in any events (e.g. due to idempotency checks)- See Also:
EventsToPersist.events(FlexAggregate, Object...)
-
noEvents
protected EventsToPersist<ID,Object> noEvents()
Creates an emptyEventsToPersistwhich is handy for a commanded method didn't have a side-effect (e.g. due to idempotent handling)- See Also:
EventsToPersist.noEvents(FlexAggregate)
-
applyRehydratedEventToTheAggregate
protected void applyRehydratedEventToTheAggregate(Object event)
Apply the previously recorded/persisted event (aka historic event) to the aggregate instance to reflect the event as a state change to the aggregate
The default implementation will automatically call any (private) methods annotated withEventHandler- Parameters:
event- the event to apply to the aggregate
-
aggregateId
public ID aggregateId()
Description copied from interface:AggregateThe id of the aggregate (aka. the stream-id)- Specified by:
aggregateIdin interfaceAggregate<ID,AGGREGATE_TYPE extends FlexAggregate<ID,AGGREGATE_TYPE>>
-
hasBeenRehydrated
public boolean hasBeenRehydrated()
Has the aggregate been initialized using previously recorded/persisted events?- Specified by:
hasBeenRehydratedin interfaceAggregate<ID,AGGREGATE_TYPE extends FlexAggregate<ID,AGGREGATE_TYPE>>- See Also:
rehydrate(AggregateEventStream),rehydrate(EventsToPersist),rehydrate(Object, List),rehydrate(Object, Stream)
-
eventOrderOfLastRehydratedEvent
public final dk.cloudcreate.essentials.components.eventsourced.eventstore.postgresql.types.EventOrder eventOrderOfLastRehydratedEvent()
Get the eventOrder of the last event that was applied to theFlexAggregateUsing:- Specified by:
eventOrderOfLastRehydratedEventin interfaceAggregate<ID,AGGREGATE_TYPE extends FlexAggregate<ID,AGGREGATE_TYPE>>- Returns:
- the event order of the last applied
EventorEventOrder.NO_EVENTS_PREVIOUSLY_PERSISTEDin case no events has ever been applied to the aggregate
-
-