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

com.github.rahulsom.grooves.api.EventsDsl Maven / Gradle / Ivy

package com.github.rahulsom.grooves.api;

import com.github.rahulsom.grooves.api.events.BaseEvent;
import com.github.rahulsom.grooves.api.snapshots.Snapshot;
import com.github.rahulsom.grooves.queries.QuerySupport;

import java.util.Date;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * DSL to simplify writing code with Events.
 *
 * @param  The Aggregate on which this operates
 * @param    The Type of Event's id field
 * @param      The type of event on which this operates
 *
 * @author Rahul Somasunderam
 */
public class EventsDsl<
        AggregateT extends AggregateType,
        EventIdT,
        EventT extends BaseEvent> {

    private static AtomicLong defaultPositionSupplier = new AtomicLong();

    protected static AtomicLong getDefaultPositionSupplier() {
        return defaultPositionSupplier;
    }

    /**
     * Allows executing a consumer with some context to setup events.
     *
     * @param aggregate         The aggregate on which the consumer must operate
     * @param entityConsumer    A Consumer that decides what happens when apply is called on an
     *                          entity
     * @param positionSupplier  A supplier which offers the default position number for an event
     *                          when it is not provided
     * @param userSupplier      A supplier that provides the User who created the event if it isn't
     *                          set
     * @param timestampSupplier A supplier that provides the date for an event if it isn't set
     * @param closure           The block of code to execute with the aggregate
     *
     * @return The aggregate after all the code has been executed
     */
    public AggregateT on(
            AggregateT aggregate, Consumer entityConsumer, Supplier positionSupplier,
            Supplier userSupplier, Supplier timestampSupplier,
            Consumer closure) {

        OnSpec spec = new OnSpec();
        spec.setAggregate(aggregate);
        spec.setEntityConsumer(entityConsumer);
        spec.setUserSupplier(userSupplier);
        spec.setTimestampSupplier(timestampSupplier);
        spec.setPositionSupplier(positionSupplier);
        closure.accept(spec);
        return aggregate;
    }

    public AggregateT on(
            AggregateT aggregate, Consumer entityConsumer, Supplier positionSupplier,
            Supplier userSupplier, Consumer closure) {
        return on(aggregate, entityConsumer, positionSupplier, userSupplier, Date::new, closure);
    }

    public AggregateT on(
            AggregateT aggregate, Consumer entityConsumer, Supplier positionSupplier,
            Consumer closure) {
        return on(aggregate, entityConsumer, positionSupplier, () -> "anonymous", Date::new,
                closure);
    }

    public AggregateT on(AggregateT aggregate, Consumer entityConsumer, Consumer closure) {
        return on(aggregate, entityConsumer, () -> defaultPositionSupplier.incrementAndGet(),
                () -> "anonymous", Date::new, closure);
    }

    public class OnSpec<
            SnapshotIdT,
            SnapshotT extends Snapshot> {
        private AggregateT aggregate;
        private Consumer entityConsumer;
        private Supplier timestampSupplier;
        private Supplier userSupplier;
        private Supplier positionSupplier;

        /**
         * Applies an event to an aggregate. This involves checking if any important fields are
         * missing and populating them based on the suppliers.
         *
         * @param event The event to be applied
         *
         * @return The event after persisting
         */
        public EventT apply(EventT event) {
            event.setAggregate(aggregate);

            if (event.getCreatedBy() == null) {
                event.setCreatedBy(userSupplier.get());
            }
            if (event.getPosition() == null) {
                event.setPosition(positionSupplier.get());
            }
            if (event.getTimestamp() == null) {
                event.setTimestamp(timestampSupplier.get());
            }

            entityConsumer.accept(event);

            return event;
        }

        /**
         * Computes and persists a snapshot based on a QueryUtil on the aggregate that this
         * OnSpec applies on
         *
         * @param queryUtil     The Query Util to compute the snapshot
         * @param beforePersist Code to execute before persisting the snapshot.
         *
         * @return The snapshot after persisting
         */
        public SnapshotT snapshotWith(
                QuerySupport queryUtil,
                Consumer beforePersist) {


            SnapshotT snapshotT = queryUtil
                    .computeSnapshot(aggregate, Long.MAX_VALUE)
                    .toBlocking()
                    .single();

            beforePersist.accept(snapshotT);
            entityConsumer.accept(snapshotT);

            return snapshotT;
        }

        /**
         * Computes and persists a snapshot based on a QueryUtil on the aggregate that this
         * OnSpec applies on.
         *
         * @param queryUtil The Query Util to compute the snapshot
         *
         * @return The snapshot after persisting
         */
        public SnapshotT snapshotWith(
                QuerySupport queryUtil) {
            return snapshotWith(queryUtil, snapshotT -> { });
        }

        public AggregateT getAggregate() {
            return aggregate;
        }

        public void setAggregate(AggregateT aggregate) {
            this.aggregate = aggregate;
        }

        public void setEntityConsumer(Consumer entityConsumer) {
            this.entityConsumer = entityConsumer;
        }

        public void setTimestampSupplier(Supplier timestampSupplier) {
            this.timestampSupplier = timestampSupplier;
        }

        public void setUserSupplier(Supplier userSupplier) {
            this.userSupplier = userSupplier;
        }

        public void setPositionSupplier(Supplier positionSupplier) {
            this.positionSupplier = positionSupplier;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy