You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ma...@apache.org on 2018/05/17 07:52:28 UTC
[4/4] james-project git commit: JAMES-2393 Introduce dedicated
modules for event sourcing
JAMES-2393 Introduce dedicated modules for event sourcing
This intend to ease future usage, external usage as well as allow a clear view.
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/189490a4
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/189490a4
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/189490a4
Branch: refs/heads/master
Commit: 189490a4eaabaa9876a0c73b3f5f035ec6565c65
Parents: c2b85a0
Author: benwa <bt...@linagora.com>
Authored: Thu May 10 11:29:21 2018 +0700
Committer: Matthieu Baechler <ma...@apache.org>
Committed: Thu May 17 09:50:00 2018 +0200
----------------------------------------------------------------------
event-sourcing/event-sourcing-core/pom.xml | 90 +++++++
.../james/eventsourcing/CommandDispatcher.java | 110 ++++++++
.../james/eventsourcing/CommandHandler.java | 29 +++
.../apache/james/eventsourcing/EventBus.java | 63 +++++
.../eventsourcing/EventSourcingSystem.java | 37 +++
.../apache/james/eventsourcing/Subscriber.java | 24 ++
.../eventsourcing/DataCollectorSubscriber.java | 46 ++++
.../eventsourcing/EventSourcingSystemTest.java | 252 +++++++++++++++++++
event-sourcing/event-sourcing-pojo/pom.xml | 67 +++++
.../apache/james/eventsourcing/AggregateId.java | 24 ++
.../org/apache/james/eventsourcing/Command.java | 23 ++
.../org/apache/james/eventsourcing/Event.java | 43 ++++
.../org/apache/james/eventsourcing/EventId.java | 86 +++++++
.../apache/james/eventsourcing/EventIdTest.java | 85 +++++++
.../james/eventsourcing/TestAggregateId.java | 68 +++++
.../apache/james/eventsourcing/TestEvent.java | 82 ++++++
event-sourcing/event-store-api/pom.xml | 77 ++++++
.../eventsourcing/eventstore/EventStore.java | 47 ++++
.../eventstore/EventStoreFailedException.java | 23 ++
.../james/eventsourcing/eventstore/History.java | 94 +++++++
.../eventstore/EventStoreTest.java | 88 +++++++
.../eventsourcing/eventstore/HistoryTest.java | 91 +++++++
event-sourcing/event-store-cassandra/pom.xml | 131 ++++++++++
.../cassandra/CassandraEventStore.java | 64 +++++
.../cassandra/CassandraEventStoreModule.java | 62 +++++
.../cassandra/CassandraEventStoreTable.java | 27 ++
.../eventstore/cassandra/EventStoreDao.java | 122 +++++++++
.../cassandra/JsonEventSerializer.java | 98 ++++++++
.../eventstore/cassandra/dto/EventDTO.java | 26 ++
.../cassandra/dto/EventDTOModule.java | 32 +++
.../CassandraEventSourcingSystemTest.java | 28 +++
.../cassandra/CassandraEventStoreExtension.java | 30 +++
.../cassandra/CassandraEventStoreTest.java | 28 +++
.../CassandraGenericEventStoreExtension.java | 87 +++++++
.../cassandra/JsonEventSerializerTest.java | 102 ++++++++
.../eventstore/cassandra/dto/OtherEvent.java | 50 ++++
.../cassandra/dto/OtherTestEventDTO.java | 72 ++++++
.../cassandra/dto/OtherTestEventDTOModule.java | 55 ++++
.../eventstore/cassandra/dto/TestEventDTO.java | 73 ++++++
.../cassandra/dto/TestEventDTOModule.java | 56 +++++
event-sourcing/event-store-memory/pom.xml | 86 +++++++
.../eventstore/memory/InMemoryEventStore.java | 99 ++++++++
.../memory/InMemoryEventSourcingSystemTest.java | 28 +++
.../memory/InMemoryEventStoreExtension.java | 39 +++
.../memory/InMemoryEventStoreTest.java | 28 +++
event-sourcing/pom.xml | 44 ++++
mailbox/plugin/quota-mailing-cassandra/pom.xml | 21 ++
.../cassandra/CassandraEventStore.java | 62 -----
.../cassandra/CassandraEventStoreModule.java | 62 -----
.../cassandra/CassandraEventStoreTable.java | 27 --
.../eventsourcing/cassandra/EventStoreDao.java | 122 ---------
.../cassandra/JsonEventSerializer.java | 98 --------
.../eventsourcing/cassandra/dto/EventDTO.java | 26 --
.../cassandra/dto/EventDTOModule.java | 32 ---
.../dto/QuotaThresholdChangedEventDTO.java | 2 +-
.../QuotaThresholdChangedEventDTOModule.java | 4 +-
.../CassandraEventSourcingSystemTest.java | 28 ---
.../cassandra/CassandraEventStoreExtension.java | 86 -------
.../cassandra/CassandraEventStoreTest.java | 28 ---
.../cassandra/JsonEventSerializerTest.java | 102 --------
.../eventsourcing/cassandra/dto/OtherEvent.java | 50 ----
.../cassandra/dto/OtherTestEventDTO.java | 72 ------
.../cassandra/dto/OtherTestEventDTOModule.java | 55 ----
.../cassandra/dto/TestEventDTO.java | 73 ------
.../cassandra/dto/TestEventDTOModule.java | 56 -----
.../mailbox/quota/cassandra/dto/DTOTest.java | 30 +--
.../listeners/CassandraEventStoreExtension.java | 32 +++
...draQuotaMailingListenersIntegrationTest.java | 1 -
mailbox/plugin/quota-mailing-memory/pom.xml | 21 ++
.../james/eventsource/InMemoryEventStore.java | 97 -------
.../InMemoryEventSourcingSystemTest.java | 27 --
.../InMemoryEventStoreExtension.java | 39 ---
.../eventsourcing/InMemoryEventStoreTest.java | 27 --
...oryQuotaMailingListenersIntegrationTest.java | 2 +-
...yQuotaThresholdConfigurationChangesTest.java | 2 +-
mailbox/plugin/quota-mailing/pom.xml | 14 ++
.../apache/james/eventsourcing/AggregateId.java | 24 --
.../james/eventsourcing/CommandDispatcher.java | 117 ---------
.../org/apache/james/eventsourcing/Event.java | 43 ----
.../apache/james/eventsourcing/EventBus.java | 61 -----
.../org/apache/james/eventsourcing/EventId.java | 86 -------
.../eventsourcing/EventSourcingSystem.java | 36 ---
.../apache/james/eventsourcing/EventStore.java | 114 ---------
.../apache/james/eventsourcing/Subscriber.java | 24 --
.../mailing/aggregates/UserQuotaThresholds.java | 8 +-
.../commands/DetectThresholdCrossing.java | 4 +-
.../DetectThresholdCrossingHandler.java | 9 +-
.../eventsourcing/DataCollectorSubscriber.java | 46 ----
.../apache/james/eventsourcing/EventIdTest.java | 85 -------
.../eventsourcing/EventSourcingSystemTest.java | 250 ------------------
.../james/eventsourcing/EventStoreTest.java | 75 ------
.../apache/james/eventsourcing/HistoryTest.java | 87 -------
.../james/eventsourcing/TestAggregateId.java | 68 -----
.../apache/james/eventsourcing/TestEvent.java | 82 ------
.../QuotaThresholdConfigurationChangesTest.java | 2 +-
.../QuotaThresholdListenersTestSystem.java | 2 +-
.../QuotaThresholdMailingIntegrationTest.java | 2 +-
pom.xml | 61 +++++
98 files changed, 3199 insertions(+), 2401 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-sourcing-core/pom.xml
----------------------------------------------------------------------
diff --git a/event-sourcing/event-sourcing-core/pom.xml b/event-sourcing/event-sourcing-core/pom.xml
new file mode 100644
index 0000000..7b7c6e1
--- /dev/null
+++ b/event-sourcing/event-sourcing-core/pom.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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
+
+ http://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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.james</groupId>
+ <artifactId>event-sourcing</artifactId>
+ <version>3.1.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>event-sourcing-core</artifactId>
+
+ <name>Apache James :: Event sourcing :: core</name>
+ <description>James Event Sourcing system</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>event-sourcing-event-store-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>event-sourcing-pojo</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>event-sourcing-pojo</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.github.steveash.guavate</groupId>
+ <artifactId>guavate</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.inject</groupId>
+ <artifactId>javax.inject</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>nl.jqno.equalsverifier</groupId>
+ <artifactId>equalsverifier</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.platform</groupId>
+ <artifactId>junit-platform-launcher</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/CommandDispatcher.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/CommandDispatcher.java b/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/CommandDispatcher.java
new file mode 100644
index 0000000..15d128c
--- /dev/null
+++ b/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/CommandDispatcher.java
@@ -0,0 +1,110 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Supplier;
+import java.util.stream.IntStream;
+
+import javax.inject.Inject;
+
+import org.apache.james.eventsourcing.eventstore.EventStoreFailedException;
+
+import com.github.steveash.guavate.Guavate;
+
+public class CommandDispatcher {
+
+ private static final int MAX_RETRY = 10;
+
+ public class UnknownCommandException extends RuntimeException {
+ private final Command command;
+
+ public UnknownCommandException(Command command) {
+ super(String.format("Unknown command %s", command));
+ this.command = command;
+ }
+
+ public Command getCommand() {
+ return command;
+ }
+ }
+
+ public class TooManyRetries extends RuntimeException {
+ private final Command command;
+ private final int retries;
+
+
+ public TooManyRetries(Command command, int retries) {
+ super(String.format("Too much retries for command %s. Store failure after %d retries", command, retries));
+ this.command = command;
+ this.retries = retries;
+ }
+
+
+ public Command getCommand() {
+ return command;
+ }
+
+ public int getRetries() {
+ return retries;
+ }
+ }
+
+ private final EventBus eventBus;
+ @SuppressWarnings("rawtypes")
+ private final Map<Class, CommandHandler> handlers;
+
+ @Inject
+ public CommandDispatcher(EventBus eventBus, Set<CommandHandler<?>> handlers) {
+ this.eventBus = eventBus;
+ this.handlers = handlers.stream()
+ .collect(Guavate.toImmutableMap(CommandHandler::handledClass, handler -> handler));
+ }
+
+ public void dispatch(Command c) {
+ trySeveralTimes(() -> tryDispatch(c))
+ .orElseThrow(() -> new TooManyRetries(c, MAX_RETRY));
+ }
+
+ public Optional<Integer> trySeveralTimes(Supplier<Boolean> singleTry) {
+ return IntStream.range(0, MAX_RETRY)
+ .boxed()
+ .filter(any -> singleTry.get())
+ .findFirst();
+ }
+
+ @SuppressWarnings("unchecked")
+ private boolean tryDispatch(Command c) {
+ try {
+ List<Event> events =
+ Optional.ofNullable(handlers.get(c.getClass()))
+ .map(f -> f.handle(c))
+ .orElseThrow(() -> new UnknownCommandException(c));
+
+ eventBus.publish(events);
+ return true;
+ } catch (EventStoreFailedException e) {
+ return false;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/CommandHandler.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/CommandHandler.java b/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/CommandHandler.java
new file mode 100644
index 0000000..6b8aacf
--- /dev/null
+++ b/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/CommandHandler.java
@@ -0,0 +1,29 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing;
+
+import java.util.List;
+
+public interface CommandHandler<C> {
+
+ Class<C> handledClass();
+
+ List<? extends Event> handle(C c);
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/EventBus.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/EventBus.java b/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/EventBus.java
new file mode 100644
index 0000000..289433e
--- /dev/null
+++ b/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/EventBus.java
@@ -0,0 +1,63 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.james.eventsourcing.eventstore.EventStore;
+import org.apache.james.eventsourcing.eventstore.EventStoreFailedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableSet;
+
+public class EventBus {
+
+ public static final Logger LOGGER = LoggerFactory.getLogger(EventBus.class);
+ private final EventStore eventStore;
+ private final Set<Subscriber> subscribers;
+
+ @Inject
+ public EventBus(EventStore eventStore, Set<Subscriber> subscribers) {
+ this.eventStore = eventStore;
+ this.subscribers = ImmutableSet.copyOf(subscribers);
+ }
+
+ public void publish(List<Event> events) throws EventStoreFailedException {
+ eventStore.appendAll(events);
+ events.stream()
+ .flatMap(event -> subscribers.stream().map(subscriber -> Pair.of(event, subscriber)))
+ .forEach(this::handle);
+ }
+
+ public void handle(Pair<Event, Subscriber> pair) {
+ Subscriber subscriber = pair.getRight();
+ Event event = pair.getLeft();
+ try {
+ subscriber.handle(event);
+ } catch (Exception e) {
+ LOGGER.error("Error while calling {} for {}", subscriber, event, e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/EventSourcingSystem.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/EventSourcingSystem.java b/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/EventSourcingSystem.java
new file mode 100644
index 0000000..e077fe6
--- /dev/null
+++ b/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/EventSourcingSystem.java
@@ -0,0 +1,37 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing;
+
+import java.util.Set;
+
+import org.apache.james.eventsourcing.eventstore.EventStore;
+
+public class EventSourcingSystem {
+ private final CommandDispatcher commandDispatcher;
+
+ public EventSourcingSystem(Set<CommandHandler<?>> handlers, Set<Subscriber> subscribers, EventStore eventStore) {
+ EventBus eventBus = new EventBus(eventStore, subscribers);
+ this.commandDispatcher = new CommandDispatcher(eventBus, handlers);
+ }
+
+ public void dispatch(Command c) {
+ commandDispatcher.dispatch(c);
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/Subscriber.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/Subscriber.java b/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/Subscriber.java
new file mode 100644
index 0000000..42a804d
--- /dev/null
+++ b/event-sourcing/event-sourcing-core/src/main/java/org/apache/james/eventsourcing/Subscriber.java
@@ -0,0 +1,24 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing;
+
+public interface Subscriber {
+ void handle(Event event);
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-sourcing-core/src/test/java/org/apache/james/eventsourcing/DataCollectorSubscriber.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-sourcing-core/src/test/java/org/apache/james/eventsourcing/DataCollectorSubscriber.java b/event-sourcing/event-sourcing-core/src/test/java/org/apache/james/eventsourcing/DataCollectorSubscriber.java
new file mode 100644
index 0000000..85fccd1
--- /dev/null
+++ b/event-sourcing/event-sourcing-core/src/test/java/org/apache/james/eventsourcing/DataCollectorSubscriber.java
@@ -0,0 +1,46 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.common.collect.ImmutableList;
+
+public class DataCollectorSubscriber implements Subscriber {
+
+ private final List<String> data;
+
+ public DataCollectorSubscriber() {
+ data = new ArrayList<>();
+ }
+
+ @Override
+ public void handle(Event event) {
+ if (event instanceof TestEvent) {
+ data.add(((TestEvent) event).getData());
+ }
+ }
+
+
+ public List<String> getData() {
+ return ImmutableList.copyOf(data);
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-sourcing-core/src/test/java/org/apache/james/eventsourcing/EventSourcingSystemTest.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-sourcing-core/src/test/java/org/apache/james/eventsourcing/EventSourcingSystemTest.java b/event-sourcing/event-sourcing-core/src/test/java/org/apache/james/eventsourcing/EventSourcingSystemTest.java
new file mode 100644
index 0000000..02c9eec
--- /dev/null
+++ b/event-sourcing/event-sourcing-core/src/test/java/org/apache/james/eventsourcing/EventSourcingSystemTest.java
@@ -0,0 +1,252 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyListOf;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+
+import org.apache.james.eventsourcing.eventstore.EventStore;
+import org.apache.james.eventsourcing.eventstore.History;
+import org.junit.jupiter.api.Test;
+
+import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public interface EventSourcingSystemTest {
+
+ String PAYLOAD_1 = "payload1";
+ String PAYLOAD_2 = "payload2";
+ TestAggregateId AGGREGATE_ID = TestAggregateId.testId(42);
+
+ class MyCommand implements Command {
+ private final String payload;
+
+ public MyCommand(String payload) {
+ this.payload = payload;
+ }
+
+ public String getPayload() {
+ return payload;
+ }
+ }
+
+ @Test
+ default void dispatchShouldApplyCommandHandlerThenCallSubscribers(EventStore eventStore) {
+ DataCollectorSubscriber subscriber = new DataCollectorSubscriber();
+ EventSourcingSystem eventSourcingSystem = new EventSourcingSystem(
+ ImmutableSet.of(simpleDispatcher(eventStore)),
+ ImmutableSet.of(subscriber),
+ eventStore);
+
+ eventSourcingSystem.dispatch(new MyCommand(PAYLOAD_1));
+
+ assertThat(subscriber.getData()).containsExactly(PAYLOAD_1);
+ }
+
+ @Test
+ default void throwingSubscribersShouldNotAbortSubscriberChain(EventStore eventStore) {
+ DataCollectorSubscriber subscriber = new DataCollectorSubscriber();
+ EventSourcingSystem eventSourcingSystem = new EventSourcingSystem(
+ ImmutableSet.of(simpleDispatcher(eventStore)),
+ ImmutableSet.of(
+ events -> {
+ throw new RuntimeException();
+ },
+ subscriber),
+ eventStore);
+
+ eventSourcingSystem.dispatch(new MyCommand(PAYLOAD_1));
+
+ assertThat(subscriber.getData()).containsExactly(PAYLOAD_1);
+ }
+
+ @Test
+ default void throwingStoreShouldNotLeadToPusblishing() {
+ EventStore eventStore = mock(EventStore.class);
+ doThrow(new RuntimeException()).when(eventStore).appendAll(anyListOf(Event.class));
+ when(eventStore.getEventsOfAggregate(any())).thenReturn(History.empty());
+
+ DataCollectorSubscriber subscriber = new DataCollectorSubscriber();
+ EventSourcingSystem eventSourcingSystem = new EventSourcingSystem(
+ ImmutableSet.of(simpleDispatcher(eventStore)),
+ ImmutableSet.of(
+ events -> {
+ throw new RuntimeException();
+ },
+ subscriber),
+ eventStore);
+
+ assertThatThrownBy(() -> eventSourcingSystem.dispatch(new MyCommand(PAYLOAD_1)))
+ .isInstanceOf(RuntimeException.class);
+
+ assertThat(subscriber.getData()).isEmpty();
+ }
+
+ @Test
+ default void dispatchShouldApplyCommandHandlerThenStoreGeneratedEvents(EventStore eventStore) {
+ DataCollectorSubscriber subscriber = new DataCollectorSubscriber();
+ EventSourcingSystem eventSourcingSystem = new EventSourcingSystem(
+ ImmutableSet.of(simpleDispatcher(eventStore)),
+ ImmutableSet.of(subscriber),
+ eventStore);
+
+ eventSourcingSystem.dispatch(new MyCommand(PAYLOAD_1));
+
+ TestEvent expectedEvent = new TestEvent(EventId.first(), AGGREGATE_ID, PAYLOAD_1);
+ assertThat(eventStore.getEventsOfAggregate(AGGREGATE_ID).getEvents())
+ .containsOnly(expectedEvent);
+ }
+
+ @Test
+ default void dispatchShouldCallSubscriberForSubsequentCommands(EventStore eventStore) {
+ DataCollectorSubscriber subscriber = new DataCollectorSubscriber();
+ EventSourcingSystem eventSourcingSystem = new EventSourcingSystem(
+ ImmutableSet.of(simpleDispatcher(eventStore)),
+ ImmutableSet.of(subscriber),
+ eventStore);
+
+ eventSourcingSystem.dispatch(new MyCommand(PAYLOAD_1));
+ eventSourcingSystem.dispatch(new MyCommand(PAYLOAD_2));
+
+ assertThat(subscriber.getData()).containsExactly(PAYLOAD_1, PAYLOAD_2);
+ }
+
+ @Test
+ default void dispatchShouldStoreEventsForSubsequentCommands(EventStore eventStore) {
+ DataCollectorSubscriber subscriber = new DataCollectorSubscriber();
+ EventSourcingSystem eventSourcingSystem = new EventSourcingSystem(
+ ImmutableSet.of(simpleDispatcher(eventStore)),
+ ImmutableSet.of(subscriber),
+ eventStore);
+
+ eventSourcingSystem.dispatch(new MyCommand(PAYLOAD_1));
+ eventSourcingSystem.dispatch(new MyCommand(PAYLOAD_2));
+
+ TestEvent expectedEvent1 = new TestEvent(EventId.first(), AGGREGATE_ID, PAYLOAD_1);
+ TestEvent expectedEvent2 = new TestEvent(expectedEvent1.eventId().next(), AGGREGATE_ID, PAYLOAD_2);
+ assertThat(eventStore.getEventsOfAggregate(AGGREGATE_ID).getEvents())
+ .containsOnly(expectedEvent1, expectedEvent2);
+ }
+
+ @Test
+ default void dispatcherShouldBeAbleToReturnSeveralEvents(EventStore eventStore) {
+ DataCollectorSubscriber subscriber = new DataCollectorSubscriber();
+ EventSourcingSystem eventSourcingSystem = new EventSourcingSystem(
+ ImmutableSet.of(wordCuttingDispatcher(eventStore)),
+ ImmutableSet.of(subscriber),
+ eventStore);
+
+ eventSourcingSystem.dispatch(new MyCommand("This is a test"));
+
+ assertThat(subscriber.getData()).containsExactly("This", "is", "a", "test");
+ }
+
+ @Test
+ default void unknownCommandsShouldBeIgnored(EventStore eventStore) {
+ DataCollectorSubscriber subscriber = new DataCollectorSubscriber();
+ EventSourcingSystem eventSourcingSystem = new EventSourcingSystem(
+ ImmutableSet.of(wordCuttingDispatcher(eventStore)),
+ ImmutableSet.of(subscriber),
+ eventStore);
+
+ assertThatThrownBy(() -> eventSourcingSystem.dispatch(new Command() {}))
+ .isInstanceOf(CommandDispatcher.UnknownCommandException.class);
+ }
+
+ @Test
+ default void constructorShouldThrowWhenSeveralHandlersForTheSameCommand(EventStore eventStore) {
+ DataCollectorSubscriber subscriber = new DataCollectorSubscriber();
+
+ assertThatThrownBy(() ->
+ new EventSourcingSystem(
+ ImmutableSet.of(wordCuttingDispatcher(eventStore),
+ simpleDispatcher(eventStore)),
+ ImmutableSet.of(subscriber),
+ eventStore))
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ default CommandHandler<MyCommand> simpleDispatcher(EventStore eventStore) {
+ return new CommandHandler<MyCommand>() {
+ @Override
+ public Class<MyCommand> handledClass() {
+ return MyCommand.class;
+ }
+
+ @Override
+ public List<? extends Event> handle(MyCommand myCommand) {
+ History history = eventStore.getEventsOfAggregate(AGGREGATE_ID);
+
+ return ImmutableList.of(new TestEvent(
+ history.getNextEventId(),
+ AGGREGATE_ID,
+ myCommand.getPayload()));
+ }
+ };
+ }
+
+ default CommandHandler<MyCommand> wordCuttingDispatcher(EventStore eventStore) {
+ return new CommandHandler<MyCommand>() {
+ @Override
+ public Class<MyCommand> handledClass() {
+ return MyCommand.class;
+ }
+
+ @Override
+ public List<? extends Event> handle(MyCommand myCommand) {
+ History history = eventStore.getEventsOfAggregate(AGGREGATE_ID);
+
+ EventIdIncrementer eventIdIncrementer = new EventIdIncrementer(history.getNextEventId());
+
+ return Splitter.on(" ")
+ .splitToList(myCommand.getPayload())
+ .stream()
+ .map(word -> new TestEvent(
+ eventIdIncrementer.next(),
+ AGGREGATE_ID,
+ word))
+ .collect(Guavate.toImmutableList());
+ }
+ };
+ }
+
+ class EventIdIncrementer {
+ private EventId currentEventId;
+
+ public EventIdIncrementer(EventId base) {
+ this.currentEventId = base;
+ }
+
+ public EventId next() {
+ currentEventId = currentEventId.next();
+ return currentEventId;
+ }
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-sourcing-pojo/pom.xml
----------------------------------------------------------------------
diff --git a/event-sourcing/event-sourcing-pojo/pom.xml b/event-sourcing/event-sourcing-pojo/pom.xml
new file mode 100644
index 0000000..c27130f
--- /dev/null
+++ b/event-sourcing/event-sourcing-pojo/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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
+
+ http://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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.james</groupId>
+ <artifactId>event-sourcing</artifactId>
+ <version>3.1.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>event-sourcing-pojo</artifactId>
+
+ <name>Apache James :: Event sourcing :: pojo</name>
+ <description>James event sourcing types</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.github.steveash.guavate</groupId>
+ <artifactId>guavate</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>nl.jqno.equalsverifier</groupId>
+ <artifactId>equalsverifier</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.platform</groupId>
+ <artifactId>junit-platform-launcher</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-sourcing-pojo/src/main/java/org/apache/james/eventsourcing/AggregateId.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-sourcing-pojo/src/main/java/org/apache/james/eventsourcing/AggregateId.java b/event-sourcing/event-sourcing-pojo/src/main/java/org/apache/james/eventsourcing/AggregateId.java
new file mode 100644
index 0000000..18c6224
--- /dev/null
+++ b/event-sourcing/event-sourcing-pojo/src/main/java/org/apache/james/eventsourcing/AggregateId.java
@@ -0,0 +1,24 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing;
+
+public interface AggregateId {
+ String asAggregateKey();
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-sourcing-pojo/src/main/java/org/apache/james/eventsourcing/Command.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-sourcing-pojo/src/main/java/org/apache/james/eventsourcing/Command.java b/event-sourcing/event-sourcing-pojo/src/main/java/org/apache/james/eventsourcing/Command.java
new file mode 100644
index 0000000..dad8332
--- /dev/null
+++ b/event-sourcing/event-sourcing-pojo/src/main/java/org/apache/james/eventsourcing/Command.java
@@ -0,0 +1,23 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing;
+
+public interface Command {
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-sourcing-pojo/src/main/java/org/apache/james/eventsourcing/Event.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-sourcing-pojo/src/main/java/org/apache/james/eventsourcing/Event.java b/event-sourcing/event-sourcing-pojo/src/main/java/org/apache/james/eventsourcing/Event.java
new file mode 100644
index 0000000..2b31374
--- /dev/null
+++ b/event-sourcing/event-sourcing-pojo/src/main/java/org/apache/james/eventsourcing/Event.java
@@ -0,0 +1,43 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing;
+
+import java.util.List;
+
+public interface Event extends Comparable<Event> {
+
+ static boolean belongsToSameAggregate(List<? extends Event> events) {
+ return events.stream()
+ .map(Event::getAggregateId)
+ .distinct()
+ .limit(2)
+ .count() == 1;
+ }
+
+ EventId eventId();
+
+ AggregateId getAggregateId();
+
+ @Override
+ default int compareTo(Event o) {
+ return eventId().compareTo(o.eventId());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-sourcing-pojo/src/main/java/org/apache/james/eventsourcing/EventId.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-sourcing-pojo/src/main/java/org/apache/james/eventsourcing/EventId.java b/event-sourcing/event-sourcing-pojo/src/main/java/org/apache/james/eventsourcing/EventId.java
new file mode 100644
index 0000000..cb5bd1e
--- /dev/null
+++ b/event-sourcing/event-sourcing-pojo/src/main/java/org/apache/james/eventsourcing/EventId.java
@@ -0,0 +1,86 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing;
+
+import java.util.Objects;
+import java.util.Optional;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+
+public class EventId implements Comparable<EventId> {
+
+ public static EventId fromSerialized(int value) {
+ return new EventId(value);
+ }
+
+ public static EventId first() {
+ return new EventId(0);
+ }
+
+ private final int value;
+
+ private EventId(int value) {
+ Preconditions.checkArgument(value >= 0, "EventId can not be negative");
+ this.value = value;
+ }
+
+ public EventId next() {
+ return new EventId(value + 1);
+ }
+
+ public Optional<EventId> previous() {
+ if (value > 0) {
+ return Optional.of(new EventId(value - 1));
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public int compareTo(EventId o) {
+ return Long.compare(value, o.value);
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (o instanceof EventId) {
+ EventId eventId = (EventId) o;
+
+ return Objects.equals(this.value, eventId.value);
+ }
+ return false;
+ }
+
+ @Override
+ public final int hashCode() {
+ return Objects.hash(value);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("value", value)
+ .toString();
+ }
+
+ public int serialize() {
+ return value;
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-sourcing-pojo/src/test/java/org/apache/james/eventsourcing/EventIdTest.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-sourcing-pojo/src/test/java/org/apache/james/eventsourcing/EventIdTest.java b/event-sourcing/event-sourcing-pojo/src/test/java/org/apache/james/eventsourcing/EventIdTest.java
new file mode 100644
index 0000000..1b46fbf
--- /dev/null
+++ b/event-sourcing/event-sourcing-pojo/src/test/java/org/apache/james/eventsourcing/EventIdTest.java
@@ -0,0 +1,85 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class EventIdTest {
+
+ @Test
+ void shouldMatchBeanContract() {
+ EqualsVerifier.forClass(EventId.class)
+ .allFieldsShouldBeUsed()
+ .verify();
+ }
+
+ @Test
+ void firstShouldReturnAConstant() {
+ assertThat(EventId.first())
+ .isEqualTo(EventId.first());
+ }
+
+ @Test
+ void previousShouldReturnEmptyWhenBeforeFirst() {
+ assertThat(EventId.first().previous())
+ .isEmpty();
+ }
+
+ @Test
+ void compareToShouldReturnNegativeWhenComparedToNext() {
+ assertThat(EventId.first())
+ .isLessThan(EventId.first().next());
+ }
+
+ @Test
+ void compareToShouldReturnNegativeWhenComparedToPrevious() {
+ assertThat(EventId.first().next())
+ .isGreaterThan(EventId.first());
+ }
+
+ @Test
+ void nextShouldAlwaysHaveTheSameIncrement() {
+ assertThat(EventId.first().next())
+ .isEqualTo(EventId.first().next());
+ }
+
+ @Test
+ void previousShouldRevertNext() {
+ assertThat(EventId.first().next().previous())
+ .contains(EventId.first());
+ }
+
+ @Test
+ void compareToShouldReturnNegativeWhenComparedToNextWithPreviousCall() {
+ assertThat(EventId.first().next().previous().get())
+ .isLessThan(EventId.first().next());
+ }
+
+ @Test
+ void compareToShouldReturnNegativeWhenComparedToPreviousWithPreviousCall() {
+ assertThat(EventId.first().next())
+ .isGreaterThan(EventId.first().next().previous().get());
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-sourcing-pojo/src/test/java/org/apache/james/eventsourcing/TestAggregateId.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-sourcing-pojo/src/test/java/org/apache/james/eventsourcing/TestAggregateId.java b/event-sourcing/event-sourcing-pojo/src/test/java/org/apache/james/eventsourcing/TestAggregateId.java
new file mode 100644
index 0000000..b3ae78c
--- /dev/null
+++ b/event-sourcing/event-sourcing-pojo/src/test/java/org/apache/james/eventsourcing/TestAggregateId.java
@@ -0,0 +1,68 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing;
+
+import java.util.Objects;
+
+import com.google.common.base.MoreObjects;
+
+public class TestAggregateId implements AggregateId {
+
+ public static TestAggregateId testId(int id) {
+ return new TestAggregateId(id);
+ }
+
+ private final int id;
+
+ private TestAggregateId(int id) {
+ this.id = id;
+ }
+
+ @Override
+ public String asAggregateKey() {
+ return "TestAggregateId-" + id;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (o instanceof TestAggregateId) {
+ TestAggregateId that = (TestAggregateId) o;
+
+ return Objects.equals(this.id, that.id);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("id", id)
+ .toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-sourcing-pojo/src/test/java/org/apache/james/eventsourcing/TestEvent.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-sourcing-pojo/src/test/java/org/apache/james/eventsourcing/TestEvent.java b/event-sourcing/event-sourcing-pojo/src/test/java/org/apache/james/eventsourcing/TestEvent.java
new file mode 100644
index 0000000..c46f804
--- /dev/null
+++ b/event-sourcing/event-sourcing-pojo/src/test/java/org/apache/james/eventsourcing/TestEvent.java
@@ -0,0 +1,82 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing;
+
+import java.util.Comparator;
+import java.util.Objects;
+
+import com.google.common.base.MoreObjects;
+
+public class TestEvent implements Event {
+ private final EventId id;
+ private final TestAggregateId aggregateId;
+ private final String data;
+
+ public TestEvent(EventId id, TestAggregateId aggregateId, String data) {
+ this.id = id;
+ this.aggregateId = aggregateId;
+ this.data = data;
+ }
+
+ @Override
+ public EventId eventId() {
+ return id;
+ }
+
+ @Override
+ public TestAggregateId getAggregateId() {
+ return aggregateId;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ @Override
+ public int compareTo(Event o) {
+ return Comparator.<EventId>naturalOrder().compare(id, o.eventId());
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (o instanceof TestEvent) {
+ TestEvent testEvent = (TestEvent) o;
+
+ return Objects.equals(this.id, testEvent.id)
+ && Objects.equals(this.aggregateId, testEvent.aggregateId)
+ && Objects.equals(this.data, testEvent.data);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, aggregateId, data);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("id", id)
+ .add("aggregateId", aggregateId)
+ .add("data", data)
+ .toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-api/pom.xml
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-api/pom.xml b/event-sourcing/event-store-api/pom.xml
new file mode 100644
index 0000000..2cfd710
--- /dev/null
+++ b/event-sourcing/event-store-api/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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
+
+ http://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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.james</groupId>
+ <artifactId>event-sourcing</artifactId>
+ <version>3.1.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>event-sourcing-event-store-api</artifactId>
+
+ <name>Apache James :: Event Sourcing :: Event Store :: API</name>
+ <description>James Event Sourcing interface for Event Store implementations</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>event-sourcing-pojo</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>event-sourcing-pojo</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.github.steveash.guavate</groupId>
+ <artifactId>guavate</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>nl.jqno.equalsverifier</groupId>
+ <artifactId>equalsverifier</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.platform</groupId>
+ <artifactId>junit-platform-launcher</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-api/src/main/java/org/apache/james/eventsourcing/eventstore/EventStore.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-api/src/main/java/org/apache/james/eventsourcing/eventstore/EventStore.java b/event-sourcing/event-store-api/src/main/java/org/apache/james/eventsourcing/eventstore/EventStore.java
new file mode 100644
index 0000000..3b3a95e
--- /dev/null
+++ b/event-sourcing/event-store-api/src/main/java/org/apache/james/eventsourcing/eventstore/EventStore.java
@@ -0,0 +1,47 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing.eventstore;
+
+import java.util.List;
+
+import org.apache.james.eventsourcing.AggregateId;
+import org.apache.james.eventsourcing.Event;
+
+import com.google.common.collect.ImmutableList;
+
+public interface EventStore {
+
+ default void append(Event event) {
+ appendAll(ImmutableList.of(event));
+ }
+
+ default void appendAll(Event... events) {
+ appendAll(ImmutableList.copyOf(events));
+ }
+
+ /**
+ * This method should check that no input event has an id already stored and throw otherwise
+ * It should also check that all events belong to the same aggregate
+ */
+ void appendAll(List<Event> events);
+
+ History getEventsOfAggregate(AggregateId aggregateId);
+
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-api/src/main/java/org/apache/james/eventsourcing/eventstore/EventStoreFailedException.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-api/src/main/java/org/apache/james/eventsourcing/eventstore/EventStoreFailedException.java b/event-sourcing/event-store-api/src/main/java/org/apache/james/eventsourcing/eventstore/EventStoreFailedException.java
new file mode 100644
index 0000000..93bcb2f
--- /dev/null
+++ b/event-sourcing/event-store-api/src/main/java/org/apache/james/eventsourcing/eventstore/EventStoreFailedException.java
@@ -0,0 +1,23 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing.eventstore;
+
+public class EventStoreFailedException extends RuntimeException {
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-api/src/main/java/org/apache/james/eventsourcing/eventstore/History.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-api/src/main/java/org/apache/james/eventsourcing/eventstore/History.java b/event-sourcing/event-store-api/src/main/java/org/apache/james/eventsourcing/eventstore/History.java
new file mode 100644
index 0000000..5a91306
--- /dev/null
+++ b/event-sourcing/event-store-api/src/main/java/org/apache/james/eventsourcing/eventstore/History.java
@@ -0,0 +1,94 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing.eventstore;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.EventId;
+
+import com.github.steveash.guavate.Guavate;
+import com.google.common.collect.ImmutableList;
+
+public class History {
+ public static History empty() {
+ return new History(ImmutableList.of());
+ }
+
+ public static History of(List<Event> events) {
+ return new History(ImmutableList.copyOf(events));
+ }
+
+ public static History of(Event... events) {
+ return of(ImmutableList.copyOf(events));
+ }
+
+ private final List<Event> events;
+
+ private History(List<Event> events) {
+ if (hasEventIdDuplicates(events)) {
+ throw new EventStoreFailedException();
+ }
+ this.events = events;
+ }
+
+ public boolean hasEventIdDuplicates(List<Event> events) {
+ Set<EventId> eventIds = events.stream()
+ .map(Event::eventId)
+ .collect(Guavate.toImmutableSet());
+
+ return eventIds.size() != events.size();
+ }
+
+ public Optional<EventId> getVersion() {
+ return events.stream()
+ .map(Event::eventId)
+ .max(Comparator.naturalOrder());
+ }
+
+ public List<Event> getEvents() {
+ return events;
+ }
+
+ public EventId getNextEventId() {
+ return getVersion()
+ .map(EventId::next)
+ .orElse(EventId.first());
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (o instanceof History) {
+ History history = (History) o;
+
+ return Objects.equals(this.events, history.events);
+ }
+ return false;
+ }
+
+ @Override
+ public final int hashCode() {
+ return Objects.hash(events);
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-api/src/test/java/org/apache/james/eventsourcing/eventstore/EventStoreTest.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-api/src/test/java/org/apache/james/eventsourcing/eventstore/EventStoreTest.java b/event-sourcing/event-store-api/src/test/java/org/apache/james/eventsourcing/eventstore/EventStoreTest.java
new file mode 100644
index 0000000..dc312d9
--- /dev/null
+++ b/event-sourcing/event-store-api/src/test/java/org/apache/james/eventsourcing/eventstore/EventStoreTest.java
@@ -0,0 +1,88 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing.eventstore;
+
+import static org.apache.james.eventsourcing.TestAggregateId.testId;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.apache.james.eventsourcing.EventId;
+import org.apache.james.eventsourcing.TestAggregateId;
+import org.apache.james.eventsourcing.TestEvent;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public interface EventStoreTest {
+
+ TestAggregateId AGGREGATE_1 = testId(1);
+ TestAggregateId AGGREGATE_2 = testId(2);
+
+ @Test
+ default void getEventsOfAggregateShouldThrowOnNullAggregateId(EventStore testee) {
+ assertThatThrownBy(() -> testee.getEventsOfAggregate(null))
+ .isInstanceOf(NullPointerException.class);
+ }
+
+ @Test
+ default void appendShouldThrowWhenEventFromSeveralAggregates(EventStore testee) {
+ TestEvent event1 = new TestEvent(EventId.first(), AGGREGATE_1, "first");
+ TestEvent event2 = new TestEvent(event1.eventId().next(), AGGREGATE_2, "second");
+ assertThatThrownBy(() -> testee.appendAll(event1, event2)).isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ default void appendShouldDoNothingOnEmptyEventList(EventStore testee) {
+ assertThatCode(testee::appendAll).doesNotThrowAnyException();
+ }
+
+ @Test
+ default void appendShouldThrowWhenTryingToRewriteHistory(EventStore testee) {
+ TestEvent event1 = new TestEvent(EventId.first(), AGGREGATE_1, "first");
+ testee.append(event1);
+ TestEvent event2 = new TestEvent(EventId.first(), AGGREGATE_1, "second");
+ assertThatThrownBy(() -> testee.append(event2)).isInstanceOf(EventStoreFailedException.class);
+ }
+
+ @Test
+ default void getEventsOfAggregateShouldReturnEmptyHistoryWhenUnknown(EventStore testee) {
+ assertThat(testee.getEventsOfAggregate(AGGREGATE_1)).isEqualTo(History.empty());
+ }
+
+ @Test
+ default void getEventsOfAggregateShouldReturnAppendedEvent(EventStore testee) {
+ TestEvent event = new TestEvent(EventId.first(), AGGREGATE_1, "first");
+ testee.append(event);
+ assertThat(testee.getEventsOfAggregate(AGGREGATE_1))
+ .isEqualTo(History.of(ImmutableList.of(event)));
+ }
+
+ @Test
+ default void getEventsOfAggregateShouldReturnAppendedEvents(EventStore testee) {
+ TestEvent event1 = new TestEvent(EventId.first(), AGGREGATE_1, "first");
+ TestEvent event2 = new TestEvent(event1.eventId().next(), AGGREGATE_1, "second");
+ testee.append(event1);
+ testee.append(event2);
+ assertThat(testee.getEventsOfAggregate(AGGREGATE_1))
+ .isEqualTo(History.of(ImmutableList.of(event1, event2)));
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-api/src/test/java/org/apache/james/eventsourcing/eventstore/HistoryTest.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-api/src/test/java/org/apache/james/eventsourcing/eventstore/HistoryTest.java b/event-sourcing/event-store-api/src/test/java/org/apache/james/eventsourcing/eventstore/HistoryTest.java
new file mode 100644
index 0000000..2945b92
--- /dev/null
+++ b/event-sourcing/event-store-api/src/test/java/org/apache/james/eventsourcing/eventstore/HistoryTest.java
@@ -0,0 +1,91 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 *
+ * *
+ * http://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 org.apache.james.eventsourcing.eventstore;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.apache.james.eventsourcing.EventId;
+import org.apache.james.eventsourcing.TestAggregateId;
+import org.apache.james.eventsourcing.TestEvent;
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class HistoryTest {
+
+ @Test
+ void shouldMatchBeanContract() {
+ EqualsVerifier.forClass(History.class)
+ .allFieldsShouldBeUsed()
+ .verify();
+ }
+
+ @Test
+ void emptyShouldGenerateAnEmptyHistory() {
+ assertThat(History.empty())
+ .isEqualTo(History.of());
+ }
+
+ @Test
+ void getVersionShouldReturnEmptyWhenEmpty() {
+ assertThat(History.empty()
+ .getVersion())
+ .isEmpty();
+ }
+
+ @Test
+ void getVersionShouldReturnSingleEventIdWhenSingleEvent() {
+ assertThat(History
+ .of(new TestEvent(EventId.first(),
+ TestAggregateId.testId(42),
+ "any"))
+ .getVersion())
+ .contains(EventId.first());
+ }
+
+ @Test
+ void getVersionShouldReturnHighestEventId() {
+ TestEvent event1 = new TestEvent(EventId.first(),
+ TestAggregateId.testId(42),
+ "any");
+ TestEvent event2 = new TestEvent(event1.eventId().next(),
+ TestAggregateId.testId(42),
+ "any");
+
+ assertThat(History.of(event1, event2)
+ .getVersion())
+ .contains(event2.eventId());
+ }
+
+ @Test
+ void duplicateHistoryShouldThrow() {
+ TestEvent event1 = new TestEvent(EventId.first(),
+ TestAggregateId.testId(42),
+ "any");
+ TestEvent event2 = new TestEvent(EventId.first(),
+ TestAggregateId.testId(42),
+ "any");
+
+ assertThatThrownBy(() -> History.of(event1, event2))
+ .isInstanceOf(EventStoreFailedException.class);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/pom.xml
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/pom.xml b/event-sourcing/event-store-cassandra/pom.xml
new file mode 100644
index 0000000..cf2e6dc
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/pom.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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
+
+ http://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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.james</groupId>
+ <artifactId>event-sourcing</artifactId>
+ <version>3.1.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>event-sourcing-event-store-cassandra</artifactId>
+
+ <name>Apache James :: Event sourcing :: Event Store :: Cassandra</name>
+ <description>Cassandra implementation for James Event Store</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>apache-james-backends-cassandra</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>apache-james-backends-cassandra</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>event-sourcing-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>event-sourcing-event-store-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>event-sourcing-event-store-api</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>event-sourcing-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>event-sourcing-pojo</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.datatype</groupId>
+ <artifactId>jackson-datatype-jdk8</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>net.javacrumbs.json-unit</groupId>
+ <artifactId>json-unit</artifactId>
+ <version>1.5.5</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>net.javacrumbs.json-unit</groupId>
+ <artifactId>json-unit-fluent</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>nl.jqno.equalsverifier</groupId>
+ <artifactId>equalsverifier</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.platform</groupId>
+ <artifactId>junit-platform-launcher</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+
+</project>
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org