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

org.enodeframework.domain.AggregateRoot Maven / Gradle / Ivy

There is a newer version: 1.1.10
Show newest version
package org.enodeframework.domain;

import com.google.common.collect.Lists;
import org.enodeframework.common.container.ObjectContainer;
import org.enodeframework.common.exception.HandlerNotFoundException;
import org.enodeframework.common.function.Action2;
import org.enodeframework.common.utilities.Ensure;
import org.enodeframework.eventing.DomainEventStream;
import org.enodeframework.eventing.IDomainEvent;

import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;

/**
 * Represents an abstract base aggregate root.
 *
 * @param 
 */
public abstract class AggregateRoot implements IAggregateRoot {
    /**
     * dynamic inject through ApplicationContext instance
     */
    private static IAggregateRootInternalHandlerProvider aggregateRootInternalHandlerProvider;
    protected TAggregateRootId id;
    protected int version;
    private List> emptyEvents = new ArrayList<>();
    private Queue> uncommittedEvents;

    protected AggregateRoot() {
        uncommittedEvents = new ConcurrentLinkedDeque>() {
        };
    }

    protected AggregateRoot(TAggregateRootId id) {
        this();
        Ensure.notNull(id, "id");
        this.id = id;
    }

    protected AggregateRoot(TAggregateRootId id, int version) {
        this(id);
        if (version < 0) {
            throw new IllegalArgumentException(String.format("Version cannot small than zero, aggregateRootId: %s, version: %d", id, version));
        }
        this.version = version;
    }

    public TAggregateRootId getId() {
        return this.id;
    }

    protected void applyEvent(IDomainEvent domainEvent) {
        Ensure.notNull(domainEvent, "domainEvent");
        Ensure.notNull(id, "AggregateRootId");
        domainEvent.setAggregateRootId(id);
        domainEvent.setVersion(version + 1);
        handleEvent(domainEvent);
        appendUncommittedEvent(domainEvent);
    }

    protected void applyEvents(List> domainEvents) {
        for (IDomainEvent domainEvent : domainEvents) {
            applyEvent(domainEvent);
        }
    }

    private void handleEvent(IDomainEvent domainEvent) {
        if (aggregateRootInternalHandlerProvider == null) {
            aggregateRootInternalHandlerProvider = ObjectContainer.resolve(IAggregateRootInternalHandlerProvider.class);
        }
        Action2> handler = aggregateRootInternalHandlerProvider.getInternalEventHandler(getClass(), domainEvent.getClass());
        if (handler == null) {
            throw new HandlerNotFoundException(String.format("Could not find event handler for [%s] of [%s]", domainEvent.getClass().getName(), getClass().getName()));
        }
        if (this.id == null && domainEvent.getVersion() == 1) {
            this.id = (TAggregateRootId) domainEvent.getAggregateRootId();
        }
        handler.apply(this, domainEvent);
    }

    private void appendUncommittedEvent(IDomainEvent domainEvent) {
        if (uncommittedEvents == null) {
            uncommittedEvents = new ConcurrentLinkedDeque<>();
        }
        if (uncommittedEvents.stream().anyMatch(x -> x.getClass().equals(domainEvent.getClass()))) {
            throw new UnsupportedOperationException(String.format("Cannot apply duplicated domain event type: %s, current aggregateRoot type: %s, id: %s", domainEvent.getClass(), this.getClass().getName(), id));
        }
        uncommittedEvents.add(domainEvent);
    }

    private void verifyEvent(DomainEventStream eventStream) {
        if (eventStream.getVersion() > 1 && !eventStream.getAggregateRootId().equals(this.getUniqueId())) {
            throw new UnsupportedOperationException(String.format("Invalid domain event stream, aggregateRootId:%s, expected aggregateRootId:%s, type:%s", eventStream.getAggregateRootId(), this.getUniqueId(), this.getClass().getName()));
        }
        if (eventStream.getVersion() != this.getVersion() + 1) {
            throw new UnsupportedOperationException(String.format("Invalid domain event stream, version:%d, expected version:%d, current aggregateRoot type:%s, id:%s", eventStream.getVersion(), this.getVersion(), this.getClass().getName(), this.getUniqueId()));
        }
    }

    @Override
    public String getUniqueId() {
        return id.toString();
    }

    @Override
    public int getVersion() {
        return version;
    }

    @Override
    public List> getChanges() {
        if (uncommittedEvents == null) {
            return emptyEvents;
        }
        return Lists.newArrayList(uncommittedEvents);
    }

    @Override
    public void acceptChanges() {
        if (uncommittedEvents != null && !uncommittedEvents.isEmpty()) {
            version = uncommittedEvents.peek().getVersion();
            uncommittedEvents.clear();
        }
    }

    @Override
    public void replayEvents(List eventStreams) {
        if (eventStreams == null) {
            return;
        }
        eventStreams.forEach(eventStream -> {
            verifyEvent(eventStream);
            eventStream.events().forEach(this::handleEvent);
            this.version = eventStream.getVersion();
        });
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy