All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dk.cloudcreate.essentials.components.eventsourced.aggregates.EventsToPersist Maven / Gradle / Ivy

There is a newer version: 0.40.19
Show newest version
/*
 * Copyright 2021-2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package dk.cloudcreate.essentials.components.eventsourced.aggregates;


import dk.cloudcreate.essentials.components.eventsourced.aggregates.flex.FlexAggregate;
import dk.cloudcreate.essentials.components.eventsourced.aggregates.stateful.modern.AggregateRoot;
import dk.cloudcreate.essentials.components.eventsourced.eventstore.postgresql.eventstream.AggregateEventStream;
import dk.cloudcreate.essentials.components.eventsourced.eventstore.postgresql.types.EventOrder;

import java.util.*;
import java.util.stream.Stream;

import static dk.cloudcreate.essentials.shared.FailFast.requireNonNull;
import static dk.cloudcreate.essentials.shared.MessageFormatter.msg;

/**
 * Wrapper object that captures the results of any command handling (e.g. static constructor method or instance command methods in {@link FlexAggregate}/{@link AggregateRoot} sub classes).
* The purpose of this wrapper is to wrap information about the aggregate-id, event-order of last applied historic event (if any) and any events that were a side effect of the command method invocation * * @param the aggregate id type * @param the type of event */ public class EventsToPersist { /** * The id of the aggregate this relates to */ public final ID aggregateId; /** * (Zero based event order) contains the eventOrder for the last (previous/historic) event applied during {@link AggregateRoot#rehydrate(AggregateEventStream)}/{@link FlexAggregate#rehydrate(AggregateEventStream)}. * See {@link EventOrder#NO_EVENTS_PREVIOUSLY_PERSISTED}
*/ public final EventOrder eventOrderOfLastRehydratedEvent; public final List events; private boolean committed; /** * @param aggregateId the aggregate id this relates to * @param eventOrderOfLastRehydratedEvent (Zero based event order) contains the eventOrder for the last (previous/historic) event applied during {@link AggregateRoot#rehydrate(AggregateEventStream)}/{@link FlexAggregate#rehydrate(AggregateEventStream)}. * See {@link EventOrder#NO_EVENTS_PREVIOUSLY_PERSISTED}
* @param events the events to persist, which will be the result/side-effect of a command method invocation in an {@link FlexAggregate}). */ public EventsToPersist(ID aggregateId, EventOrder eventOrderOfLastRehydratedEvent, List events) { this.aggregateId = requireNonNull(aggregateId, "You must supply an aggregate id"); this.eventOrderOfLastRehydratedEvent = eventOrderOfLastRehydratedEvent; this.events = requireNonNull(events, "No events list provided"); } /** * @param aggregateId the aggregate id this relates to * @param eventOrderOfLastRehydratedEvent (Zero based event order) contains the eventOrder for the last (previous/historic) event applied during {@link AggregateRoot#rehydrate(AggregateEventStream)}/{@link FlexAggregate#rehydrate(AggregateEventStream)}. * See {@link EventOrder#NO_EVENTS_PREVIOUSLY_PERSISTED}
* @param events the events to persist, which will be the result/side-effect of a command method invocation in an {@link FlexAggregate}). */ public EventsToPersist(ID aggregateId, EventOrder eventOrderOfLastRehydratedEvent, EVENT_TYPE... events) { this.aggregateId = requireNonNull(aggregateId, "You must supply an aggregate id"); this.eventOrderOfLastRehydratedEvent = eventOrderOfLastRehydratedEvent; this.events = List.of(events); } /** * Wrap the events that should be persisted for this new aggregate * * @param aggregateId the aggregate id this relates to * @param eventsToPersist the events to persist, which will be the result/side-effect of a command method invocation in an {@link FlexAggregate}). * May be empty if the command method invocation didn't result in any events (e.g. due to idempotency checks) * @param the aggregate id type */ public static EventsToPersist initialAggregateEvents(ID aggregateId, EVENT_TYPE... eventsToPersist) { requireNonNull(aggregateId, "You must supply an aggregateId"); return new EventsToPersist<>(aggregateId, EventOrder.NO_EVENTS_PREVIOUSLY_PERSISTED, List.of(eventsToPersist)); } /** * Wrap the events that should be persisted for this existing aggregate * * @param aggregateId the aggregate id this relates to * @param eventOrderOfLastRehydratedEvent (Zero based event order) contains the eventOrder for the last (previous/historic) event applied during {@link AggregateRoot#rehydrate(AggregateEventStream)}/{@link FlexAggregate#rehydrate(AggregateEventStream)}. * See {@link EventOrder#NO_EVENTS_PREVIOUSLY_PERSISTED}
* @param eventsToPersist the events to persist, which will be the result/side-effect of a command method invocation in an {@link FlexAggregate}). * May be empty if the command method invocation didn't result in any events (e.g. due to idempotency checks) * @param the aggregate id type */ public static EventsToPersist events(ID aggregateId, EventOrder eventOrderOfLastRehydratedEvent, EVENT_TYPE... eventsToPersist) { return new EventsToPersist<>(aggregateId, eventOrderOfLastRehydratedEvent, eventsToPersist); } /** * Wrap the events that should be persisted relates to the given aggregate's command handling * * @param aggregate the easy aggregate instance that the eventsToPersist * @param eventsToPersist the events to persist, which will be the result/side-effect of a command method invocation in an {@link FlexAggregate}). * May be empty if the command method invocation didn't result in any events (e.g. due to idempotency checks) * @param the aggregate id type * @param the aggregate type */ public static > EventsToPersist events(FlexAggregate aggregate, EVENT_TYPE... eventsToPersist) { return new EventsToPersist<>(requireNonNull(aggregate.aggregateId(), "Aggregate doesn't have an aggregateId, please use initialAggregateEvents(id, events)"), aggregate.eventOrderOfLastRehydratedEvent(), eventsToPersist); } /** * Creates an empty {@link EventsToPersist} which is handy for a commanded method didn't have a side-effect (e.g. due to idempotent handling) * * @param aggregateId the aggregate id this relates to * @param the aggregate id type */ public static EventsToPersist noEvents(ID aggregateId) { return new EventsToPersist<>(aggregateId, null); } /** * Creates an empty {@link EventsToPersist} which is handy for a commanded method didn't have a side-effect (e.g. due to idempotent handling) * * @param aggregate the easy aggregate instance * @param the aggregate id type * @param the aggregate type */ public static > EventsToPersist noEvents(FlexAggregate aggregate) { return new EventsToPersist<>(aggregate.aggregateId(), aggregate.eventOrderOfLastRehydratedEvent()); } public void markEventsAsCommitted() { committed = true; } public boolean isCommitted() { return committed; } /** * Immutable method that appends all events in appendEventsToPersist * to the {@link #events} in this instance. The result is returned as a NEW * {@link EventsToPersist} instance that contains the combined list of events * * @param appendEventsToPersist the events to append to this instance * @return The result is returned as a NEW * {@link EventsToPersist} instance that contains the combined list of events. The new {@link EventsToPersist} * retains the original value of the {@link EventsToPersist#eventOrderOfLastRehydratedEvent} */ @SuppressWarnings({"unchecked", "rawtypes"}) public EventsToPersist append(EventsToPersist appendEventsToPersist) { requireNonNull(appendEventsToPersist, "You must supply an appendEventsToPersist instance"); if (!this.aggregateId.equals(appendEventsToPersist.aggregateId)) { throw new IllegalArgumentException(msg("Cannot append appendEventsToPersist since aggregate id's are not the same. " + "this.aggregateId='{}', appendEventsToPersist.aggregateId='{}'", this.aggregateId, appendEventsToPersist.aggregateId)); } if (eventOrderOfLastRehydratedEvent == null && !events.isEmpty()) { throw new IllegalStateException("this.eventOrderOfLastRehydratedEvent is null and therefore doesn't support appending"); } if (appendEventsToPersist.eventOrderOfLastRehydratedEvent == null) { throw new IllegalStateException("appendEventsToPersist.eventOrderOfLastRehydratedEvent is null"); } long expectedEventOrderOfLastRehydratedEvent = eventOrderOfLastRehydratedEvent.longValue() + events.size(); if (expectedEventOrderOfLastRehydratedEvent != appendEventsToPersist.eventOrderOfLastRehydratedEvent.longValue()) { throw new IllegalArgumentException(msg("Cannot append appendEventsToPersist as appendEventsToPersist.eventOrderOfLastRehydratedEvent was " + "{} but it was expected to be {}", appendEventsToPersist.eventOrderOfLastRehydratedEvent, expectedEventOrderOfLastRehydratedEvent)); } var allEventsToPersist = new ArrayList(events); allEventsToPersist.addAll(appendEventsToPersist.events); return new EventsToPersist<>(aggregateId, eventOrderOfLastRehydratedEvent, allEventsToPersist); } @Override public String toString() { return "EventsToPersist{" + "aggregateId=" + aggregateId + ", eventOrderOfLastRehydratedEvent=" + eventOrderOfLastRehydratedEvent + ", eventsToPersist=" + events.size() + ", committed=" + committed + '}'; } /** * Is the {@link #events} empty * * @return Is the {@link #events} empty */ public boolean isEmpty() { return events.isEmpty(); } /** * {@link #events} {@link Stream} * * @return {@link #events} {@link Stream} */ public Stream stream() { return events.stream(); } /** * Number of events. Shorthand for {@link #events#size()} * * @return Shorthand for {@link #events#size()} */ public int size() { return events.size(); } /** * Get event at index. Shorthand for {@link #events#get(int)} * * @param index the index on the {@link #events} list * @return Shorthand for {@link #events#get(int)} */ public Object get(int index) { return events.get(index); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy