content: package bankservice.domain.model;
  
  import static com.google.common.base.Preconditions.checkArgument;
  import static com.google.common.base.Preconditions.checkNotNull;
  import static java.lang.String.format;
  import static java.util.Collections.emptyList;
  
  import com.google.common.base.Throwables;
  import com.google.common.collect.ImmutableList;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.UUID;
  
  public abstract class Aggregate {
  
    private UUID id;
    private int baseVersion;
    private List<Event> newEvents;
  
    protected Aggregate(UUID id) {
      this(id, emptyList());
    }
  
    protected Aggregate(UUID id, List<Event> eventStream) {
      checkNotNull(id);
      checkNotNull(eventStream);
      this.id = id;
      eventStream.forEach(e -> {
        apply(e);
        this.baseVersion = e.getVersion();
      });
      this.newEvents = new ArrayList<>();
    }
  
    protected void applyNewEvent(Event event) {
      checkArgument(event.getVersion() == getNextVersion(),
          "New event version '%s' does not match expected next version '%s'",
          event.getVersion(), getNextVersion());
      apply(event);
      newEvents.add(event);
    }
  
    private void apply(Event event) {
      try {
        Method method = this.getClass().getDeclaredMethod("apply", event.getClass());
        method.setAccessible(true);
        method.invoke(this, event);
      } catch (InvocationTargetException e) {
        Throwables.propagate(e.getCause());
      } catch (NoSuchMethodException | IllegalAccessException e) {
        throw new UnsupportedOperationException(
            format("Aggregate '%s' doesn't apply event type '%s'", this.getClass(), event.getClass()),
            e);
      }
    }
  
    public UUID getId() {
      return id;
    }
  
    public int getBaseVersion() {
      return baseVersion;
    }
  
    public List<Event> getNewEvents() {
      return ImmutableList.copyOf(newEvents);
    }
  
    protected int getNextVersion() {
      return baseVersion + newEvents.size() + 1;
    }
  }
  