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:25 UTC

[1/4] james-project git commit: JAMES-2393 Introduce dedicated modules for event sourcing

Repository: james-project
Updated Branches:
  refs/heads/master c2b85a004 -> 189490a4e


http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/EventSourcingSystem.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/EventSourcingSystem.java b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/EventSourcingSystem.java
deleted file mode 100644
index 22a2788..0000000
--- a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/EventSourcingSystem.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/****************************************************************
- * 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;
-
-public class EventSourcingSystem {
-    private final EventBus eventBus;
-    private final CommandDispatcher commandDispatcher;
-
-    public EventSourcingSystem(Set<CommandDispatcher.CommandHandler<?>> handlers, Set<Subscriber> subscribers, EventStore eventStore) {
-        this.eventBus = new EventBus(eventStore, subscribers);
-        this.commandDispatcher = new CommandDispatcher(eventBus, handlers);
-    }
-
-    public void dispatch(CommandDispatcher.Command c) {
-        commandDispatcher.dispatch(c);
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/EventStore.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/EventStore.java b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/EventStore.java
deleted file mode 100644
index 1ba8028..0000000
--- a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/EventStore.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/****************************************************************
- * 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.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-
-import com.github.steveash.guavate.Guavate;
-import com.google.common.collect.ImmutableList;
-
-public interface EventStore {
-
-    class EventStoreFailedException extends RuntimeException {
-
-    }
-
-    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);
-        }
-    }
-
-    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/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/Subscriber.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/Subscriber.java b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/Subscriber.java
deleted file mode 100644
index 42a804d..0000000
--- a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/Subscriber.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/****************************************************************
- * 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/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/aggregates/UserQuotaThresholds.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/aggregates/UserQuotaThresholds.java b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/aggregates/UserQuotaThresholds.java
index 8290baf..ae67d30 100644
--- a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/aggregates/UserQuotaThresholds.java
+++ b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/aggregates/UserQuotaThresholds.java
@@ -27,7 +27,7 @@ import java.util.stream.Collectors;
 
 import org.apache.james.core.User;
 import org.apache.james.eventsourcing.AggregateId;
-import org.apache.james.eventsourcing.EventStore;
+import org.apache.james.eventsourcing.eventstore.History;
 import org.apache.james.mailbox.model.Quota;
 import org.apache.james.mailbox.quota.QuotaCount;
 import org.apache.james.mailbox.quota.QuotaSize;
@@ -82,15 +82,15 @@ public class UserQuotaThresholds {
         }
     }
 
-    public static UserQuotaThresholds fromEvents(Id aggregateId, EventStore.History history) {
+    public static UserQuotaThresholds fromEvents(Id aggregateId, History history) {
         return new UserQuotaThresholds(aggregateId, history);
     }
 
     private final Id aggregateId;
-    private final EventStore.History history;
+    private final History history;
     private final List<QuotaThresholdChangedEvent> events;
 
-    private UserQuotaThresholds(Id aggregateId, EventStore.History history) {
+    private UserQuotaThresholds(Id aggregateId, History history) {
         this.aggregateId = aggregateId;
         this.history = history;
         this.events = history.getEvents().stream()

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/commands/DetectThresholdCrossing.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/commands/DetectThresholdCrossing.java b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/commands/DetectThresholdCrossing.java
index 0de0023..1128c0b 100644
--- a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/commands/DetectThresholdCrossing.java
+++ b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/commands/DetectThresholdCrossing.java
@@ -23,12 +23,12 @@ import java.time.Instant;
 import java.util.Objects;
 
 import org.apache.james.core.User;
-import org.apache.james.eventsourcing.CommandDispatcher;
+import org.apache.james.eventsourcing.Command;
 import org.apache.james.mailbox.model.Quota;
 import org.apache.james.mailbox.quota.QuotaCount;
 import org.apache.james.mailbox.quota.QuotaSize;
 
-public class DetectThresholdCrossing implements CommandDispatcher.Command {
+public class DetectThresholdCrossing implements Command {
 
     private final User user;
     private final Quota<QuotaCount> countQuota;

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/commands/DetectThresholdCrossingHandler.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/commands/DetectThresholdCrossingHandler.java b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/commands/DetectThresholdCrossingHandler.java
index 39b2013..bbf480e 100644
--- a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/commands/DetectThresholdCrossingHandler.java
+++ b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/commands/DetectThresholdCrossingHandler.java
@@ -23,13 +23,14 @@ import java.util.List;
 
 import javax.inject.Inject;
 
-import org.apache.james.eventsourcing.CommandDispatcher;
+import org.apache.james.eventsourcing.CommandHandler;
 import org.apache.james.eventsourcing.Event;
-import org.apache.james.eventsourcing.EventStore;
+import org.apache.james.eventsourcing.eventstore.EventStore;
+import org.apache.james.eventsourcing.eventstore.History;
 import org.apache.james.mailbox.quota.mailing.QuotaMailingListenerConfiguration;
 import org.apache.james.mailbox.quota.mailing.aggregates.UserQuotaThresholds;
 
-public class DetectThresholdCrossingHandler implements CommandDispatcher.CommandHandler<DetectThresholdCrossing> {
+public class DetectThresholdCrossingHandler implements CommandHandler<DetectThresholdCrossing> {
 
     private final EventStore eventStore;
     private final QuotaMailingListenerConfiguration quotaMailingListenerConfiguration;
@@ -48,7 +49,7 @@ public class DetectThresholdCrossingHandler implements CommandDispatcher.Command
 
     private UserQuotaThresholds loadAggregate(DetectThresholdCrossing command) {
         UserQuotaThresholds.Id aggregateId = UserQuotaThresholds.Id.from(command.getUser());
-        EventStore.History history = eventStore.getEventsOfAggregate(aggregateId);
+        History history = eventStore.getEventsOfAggregate(aggregateId);
         return UserQuotaThresholds.fromEvents(aggregateId, history);
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/DataCollectorSubscriber.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/DataCollectorSubscriber.java b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/DataCollectorSubscriber.java
deleted file mode 100644
index 85fccd1..0000000
--- a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/DataCollectorSubscriber.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/****************************************************************
- * 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/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventIdTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventIdTest.java b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventIdTest.java
deleted file mode 100644
index ffdc5f2..0000000
--- a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventIdTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/****************************************************************
- * 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;
-
-public class EventIdTest {
-
-    @Test
-    public void shouldMatchBeanContract() {
-        EqualsVerifier.forClass(EventStore.History.class)
-            .allFieldsShouldBeUsed()
-            .verify();
-    }
-
-    @Test
-    public void firstShouldReturnAConstant() {
-        assertThat(EventId.first())
-            .isEqualTo(EventId.first());
-    }
-
-    @Test
-    public void previousShouldReturnEmptyWhenBeforeFirst() {
-        assertThat(EventId.first().previous())
-            .isEmpty();
-    }
-
-    @Test
-    public void compareToShouldReturnNegativeWhenComparedToNext() {
-        assertThat(EventId.first())
-            .isLessThan(EventId.first().next());
-    }
-
-    @Test
-    public void compareToShouldReturnNegativeWhenComparedToPrevious() {
-        assertThat(EventId.first().next())
-            .isGreaterThan(EventId.first());
-    }
-
-    @Test
-    public void nextShouldAlwaysHaveTheSameIncrement() {
-        assertThat(EventId.first().next())
-            .isEqualTo(EventId.first().next());
-    }
-
-    @Test
-    public void previousShouldRevertNext() {
-        assertThat(EventId.first().next().previous())
-            .contains(EventId.first());
-    }
-
-    @Test
-    public void compareToShouldReturnNegativeWhenComparedToNextWithPreviousCall() {
-        assertThat(EventId.first().next().previous().get())
-            .isLessThan(EventId.first().next());
-    }
-
-    @Test
-    public 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/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventSourcingSystemTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventSourcingSystemTest.java b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventSourcingSystemTest.java
deleted file mode 100644
index 19c3925..0000000
--- a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventSourcingSystemTest.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/****************************************************************
- * 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.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 CommandDispatcher.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(EventStore.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 CommandDispatcher.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 CommandDispatcher.CommandHandler<MyCommand> simpleDispatcher(EventStore eventStore) {
-        return new CommandDispatcher.CommandHandler<MyCommand>() {
-            @Override
-            public Class<MyCommand> handledClass() {
-                return MyCommand.class;
-            }
-
-            @Override
-            public List<? extends Event> handle(MyCommand myCommand) {
-                EventStore.History history = eventStore.getEventsOfAggregate(AGGREGATE_ID);
-
-                return ImmutableList.of(new TestEvent(
-                    history.getNextEventId(),
-                    AGGREGATE_ID,
-                    myCommand.getPayload()));
-            }
-        };
-    }
-
-    default CommandDispatcher.CommandHandler<MyCommand> wordCuttingDispatcher(EventStore eventStore) {
-        return new CommandDispatcher.CommandHandler<MyCommand>() {
-            @Override
-            public Class<MyCommand> handledClass() {
-                return MyCommand.class;
-            }
-
-            @Override
-            public List<? extends Event> handle(MyCommand myCommand) {
-                EventStore.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/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventStoreTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventStoreTest.java b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventStoreTest.java
deleted file mode 100644
index aad8daa..0000000
--- a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventStoreTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package org.apache.james.eventsourcing;
-
-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.junit.jupiter.api.Test;
-
-import com.google.common.collect.ImmutableList;
-
-import nl.jqno.equalsverifier.EqualsVerifier;
-
-public interface EventStoreTest {
-
-    TestAggregateId AGGREGATE_1 = testId(1);
-    TestAggregateId AGGREGATE_2 = testId(2);
-
-    @Test
-    default void historyShouldMatchBeanContract() {
-        EqualsVerifier.forClass(EventStore.History.class)
-            .allFieldsShouldBeUsed()
-            .verify();
-    }
-
-    @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(EventStore.EventStoreFailedException.class);
-    }
-
-    @Test
-    default void getEventsOfAggregateShouldReturnEmptyHistoryWhenUnknown(EventStore testee) {
-        assertThat(testee.getEventsOfAggregate(AGGREGATE_1)).isEqualTo(EventStore.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(EventStore.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(EventStore.History.of(ImmutableList.of(event1, event2)));
-    }
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/HistoryTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/HistoryTest.java b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/HistoryTest.java
deleted file mode 100644
index 9419087..0000000
--- a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/HistoryTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/****************************************************************
- * 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 org.junit.jupiter.api.Test;
-
-import nl.jqno.equalsverifier.EqualsVerifier;
-
-public class HistoryTest {
-
-    @Test
-    public void shouldMatchBeanContract() {
-        EqualsVerifier.forClass(EventStore.History.class)
-            .verify();
-    }
-
-    @Test
-    public void emptyShouldGenerateAnEmptyHistory() {
-        assertThat(EventStore.History.empty())
-            .isEqualTo(EventStore.History.of());
-    }
-
-    @Test
-    public void getVersionShouldReturnEmptyWhenEmpty() {
-        assertThat(EventStore.History.empty()
-            .getVersion())
-            .isEmpty();
-    }
-
-    @Test
-    public void getVersionShouldReturnSingleEventIdWhenSingleEvent() {
-        assertThat(EventStore.History
-            .of(new TestEvent(EventId.first(),
-                TestAggregateId.testId(42),
-                "any"))
-            .getVersion())
-            .contains(EventId.first());
-    }
-
-    @Test
-    public void getVersionShouldReturnHighestEventId() {
-        TestEvent event1 = new TestEvent(EventId.first(),
-            TestAggregateId.testId(42),
-            "any");
-        TestEvent event2 = new TestEvent(event1.eventId().next(),
-            TestAggregateId.testId(42),
-            "any");
-
-        assertThat(EventStore.History.of(event1, event2)
-            .getVersion())
-            .contains(event2.eventId());
-    }
-
-    @Test
-    public void duplicateHistoryShouldThrow() {
-        TestEvent event1 = new TestEvent(EventId.first(),
-            TestAggregateId.testId(42),
-            "any");
-        TestEvent event2 = new TestEvent(EventId.first(),
-            TestAggregateId.testId(42),
-            "any");
-
-        assertThatThrownBy(() -> EventStore.History.of(event1, event2))
-            .isInstanceOf(EventStore.EventStoreFailedException.class);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/TestAggregateId.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/TestAggregateId.java b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/TestAggregateId.java
deleted file mode 100644
index b3ae78c..0000000
--- a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/TestAggregateId.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/****************************************************************
- * 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/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/TestEvent.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/TestEvent.java b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/TestEvent.java
deleted file mode 100644
index c46f804..0000000
--- a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/TestEvent.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/****************************************************************
- * 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/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/listeners/QuotaThresholdConfigurationChangesTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/listeners/QuotaThresholdConfigurationChangesTest.java b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/listeners/QuotaThresholdConfigurationChangesTest.java
index fe57929..166d86c 100644
--- a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/listeners/QuotaThresholdConfigurationChangesTest.java
+++ b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/listeners/QuotaThresholdConfigurationChangesTest.java
@@ -28,7 +28,7 @@ import static org.apache.james.mailbox.quota.model.QuotaThresholdFixture._75;
 import static org.apache.james.mailbox.quota.model.QuotaThresholdFixture.mailetContext;
 import static org.assertj.core.api.Assertions.assertThat;
 
-import org.apache.james.eventsourcing.EventStore;
+import org.apache.james.eventsourcing.eventstore.EventStore;
 import org.apache.james.mailbox.MailboxListener.QuotaUsageUpdatedEvent;
 import org.apache.james.mailbox.quota.mailing.QuotaMailingListenerConfiguration;
 import org.apache.james.mailbox.quota.model.QuotaThresholdFixture.Quotas.Counts;

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/listeners/QuotaThresholdListenersTestSystem.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/listeners/QuotaThresholdListenersTestSystem.java b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/listeners/QuotaThresholdListenersTestSystem.java
index bb1dce8..0b279dd 100644
--- a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/listeners/QuotaThresholdListenersTestSystem.java
+++ b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/listeners/QuotaThresholdListenersTestSystem.java
@@ -20,7 +20,7 @@
 package org.apache.james.mailbox.quota.mailing.listeners;
 
 import org.apache.james.eventsourcing.EventSourcingSystem;
-import org.apache.james.eventsourcing.EventStore;
+import org.apache.james.eventsourcing.eventstore.EventStore;
 import org.apache.james.filesystem.api.FileSystem;
 import org.apache.james.mailbox.Event;
 import org.apache.james.mailbox.exception.MailboxException;

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/listeners/QuotaThresholdMailingIntegrationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/listeners/QuotaThresholdMailingIntegrationTest.java b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/listeners/QuotaThresholdMailingIntegrationTest.java
index 604d891..362246f 100644
--- a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/listeners/QuotaThresholdMailingIntegrationTest.java
+++ b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/listeners/QuotaThresholdMailingIntegrationTest.java
@@ -37,7 +37,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 import java.util.concurrent.TimeUnit;
 
-import org.apache.james.eventsourcing.EventStore;
+import org.apache.james.eventsourcing.eventstore.EventStore;
 import org.apache.james.mailbox.MailboxListener.QuotaUsageUpdatedEvent;
 import org.apache.james.mailbox.quota.mailing.QuotaMailingListenerConfiguration;
 import org.apache.james.mailbox.quota.model.QuotaThresholdFixture.Quotas.Counts;

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 4a41df0..16beb29 100644
--- a/pom.xml
+++ b/pom.xml
@@ -526,6 +526,7 @@
     <modules>
         <module>backends-common</module>
         <module>core</module>
+        <module>event-sourcing</module>
         <module>javax-mail-extension</module>
         <module>mailbox</module>
         <module>mailet</module>
@@ -1001,6 +1002,66 @@
                 <artifactId>blob-cassandra</artifactId>
                 <version>${project.version}</version>
             </dependency>
+             <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>event-sourcing-core</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>event-sourcing-core</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>event-sourcing-event-store-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>event-sourcing-event-store-api</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>event-sourcing-event-store-cassandra</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>event-sourcing-event-store-cassandra</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>event-sourcing-event-store-memory</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>event-sourcing-event-store-memory</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>event-sourcing-memory</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>event-sourcing-pojo</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>event-sourcing-pojo</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+            </dependency>
             <dependency>
                 <groupId>${project.groupId}</groupId>
                 <artifactId>james-server-cassandra-guice</artifactId>


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[4/4] james-project git commit: JAMES-2393 Introduce dedicated modules for event sourcing

Posted by ma...@apache.org.
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


[3/4] james-project git commit: JAMES-2393 Introduce dedicated modules for event sourcing

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStore.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStore.java b/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStore.java
new file mode 100644
index 0000000..0e8fd5f
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStore.java
@@ -0,0 +1,64 @@
+/****************************************************************
+ * 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.cassandra;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.james.eventsourcing.AggregateId;
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.eventstore.EventStore;
+import org.apache.james.eventsourcing.eventstore.EventStoreFailedException;
+import org.apache.james.eventsourcing.eventstore.History;
+
+import com.google.common.base.Preconditions;
+
+public class CassandraEventStore implements EventStore {
+
+    private final EventStoreDao eventStoreDao;
+
+    @Inject
+    public CassandraEventStore(EventStoreDao eventStoreDao) {
+        this.eventStoreDao = eventStoreDao;
+    }
+
+    @Override
+    public void appendAll(List<Event> events) {
+        if (events.isEmpty()) {
+            return;
+        }
+        doAppendAll(events);
+    }
+
+    public void doAppendAll(List<Event> events) {
+        Preconditions.checkArgument(Event.belongsToSameAggregate(events));
+
+        boolean success = eventStoreDao.appendAll(events).join();
+        if (!success) {
+            throw new EventStoreFailedException();
+        }
+    }
+
+    @Override
+    public History getEventsOfAggregate(AggregateId aggregateId) {
+        return eventStoreDao.getEventsOfAggregate(aggregateId);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStoreModule.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStoreModule.java b/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStoreModule.java
new file mode 100644
index 0000000..85dab3c
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStoreModule.java
@@ -0,0 +1,62 @@
+/****************************************************************
+ * 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.cassandra;
+
+import java.util.List;
+
+import org.apache.james.backends.cassandra.components.CassandraModule;
+import org.apache.james.backends.cassandra.components.CassandraTable;
+import org.apache.james.backends.cassandra.components.CassandraType;
+import org.apache.james.backends.cassandra.utils.CassandraConstants;
+
+import com.datastax.driver.core.DataType;
+import com.datastax.driver.core.schemabuilder.SchemaBuilder;
+import com.google.common.collect.ImmutableList;
+
+public class CassandraEventStoreModule implements CassandraModule {
+    private final List<CassandraTable> tables;
+    private final List<CassandraType> types;
+
+    public CassandraEventStoreModule() {
+        tables = ImmutableList.of(
+            new CassandraTable(CassandraEventStoreTable.EVENTS_TABLE,
+                SchemaBuilder.createTable(CassandraEventStoreTable.EVENTS_TABLE)
+                    .ifNotExists()
+                    .addPartitionKey(CassandraEventStoreTable.AGGREGATE_ID, DataType.varchar())
+                    .addClusteringColumn(CassandraEventStoreTable.EVENT_ID, DataType.cint())
+                    .addColumn(CassandraEventStoreTable.EVENT, DataType.text())
+                    .withOptions()
+                    .comment("Store events of a EventSourcing aggregate")
+                    .caching(SchemaBuilder.KeyCaching.ALL,
+                            SchemaBuilder.rows(CassandraConstants.DEFAULT_CACHED_ROW_PER_PARTITION))));
+        types = ImmutableList.of();
+    }
+
+    @Override
+    public List<CassandraTable> moduleTables() {
+        return tables;
+    }
+
+    @Override
+    public List<CassandraType> moduleTypes() {
+        return types;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStoreTable.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStoreTable.java b/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStoreTable.java
new file mode 100644
index 0000000..fb4575b
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStoreTable.java
@@ -0,0 +1,27 @@
+/****************************************************************
+ * 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.cassandra;
+
+public interface CassandraEventStoreTable {
+    String EVENTS_TABLE = "eventStore";
+    String AGGREGATE_ID = "aggregateId";
+    String EVENT = "event";
+    String EVENT_ID = "eventId";
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/EventStoreDao.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/EventStoreDao.java b/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/EventStoreDao.java
new file mode 100644
index 0000000..feb6ef7
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/EventStoreDao.java
@@ -0,0 +1,122 @@
+/****************************************************************
+ * 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.cassandra;
+
+import static com.datastax.driver.core.querybuilder.QueryBuilder.bindMarker;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
+import static org.apache.james.eventsourcing.eventstore.cassandra.CassandraEventStoreTable.AGGREGATE_ID;
+import static org.apache.james.eventsourcing.eventstore.cassandra.CassandraEventStoreTable.EVENT;
+import static org.apache.james.eventsourcing.eventstore.cassandra.CassandraEventStoreTable.EVENTS_TABLE;
+import static org.apache.james.eventsourcing.eventstore.cassandra.CassandraEventStoreTable.EVENT_ID;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import javax.inject.Inject;
+
+import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
+import org.apache.james.backends.cassandra.utils.CassandraUtils;
+import org.apache.james.eventsourcing.AggregateId;
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.eventstore.History;
+
+import com.datastax.driver.core.BatchStatement;
+import com.datastax.driver.core.BoundStatement;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Row;
+import com.datastax.driver.core.Session;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.github.steveash.guavate.Guavate;
+
+public class EventStoreDao {
+    private final CassandraUtils cassandraUtils;
+    private final CassandraAsyncExecutor cassandraAsyncExecutor;
+    private final PreparedStatement insert;
+    private final PreparedStatement select;
+    private final JsonEventSerializer jsonEventSerializer;
+
+    @Inject
+    public EventStoreDao(Session session, CassandraUtils cassandraUtils, JsonEventSerializer jsonEventSerializer) {
+        this.cassandraUtils = cassandraUtils;
+        this.cassandraAsyncExecutor = new CassandraAsyncExecutor(session);
+        this.jsonEventSerializer = jsonEventSerializer;
+        this.insert = prepareInsert(session);
+        this.select = prepareSelect(session);
+    }
+
+    private PreparedStatement prepareInsert(Session session) {
+        return session.prepare(insertInto(EVENTS_TABLE)
+            .value(AGGREGATE_ID, bindMarker(AGGREGATE_ID))
+            .value(EVENT_ID, bindMarker(EVENT_ID))
+            .value(EVENT, bindMarker(EVENT))
+            .ifNotExists());
+    }
+
+    private PreparedStatement prepareSelect(Session session) {
+        return session.prepare(select()
+            .from(EVENTS_TABLE)
+            .where(eq(AGGREGATE_ID, bindMarker(AGGREGATE_ID))));
+    }
+
+    public CompletableFuture<Boolean> appendAll(List<Event> events) {
+        BatchStatement batch = new BatchStatement();
+        events.forEach(event -> batch.add(insertEvent(event)));
+        return cassandraAsyncExecutor.executeReturnApplied(batch);
+    }
+
+    private BoundStatement insertEvent(Event event) {
+        try {
+            return insert
+                .bind()
+                .setString(AGGREGATE_ID, event.getAggregateId().asAggregateKey())
+                .setInt(EVENT_ID, event.eventId().serialize())
+                .setString(EVENT, jsonEventSerializer.serialize(event));
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public History getEventsOfAggregate(AggregateId aggregateId) {
+        return toHistory(
+            cassandraAsyncExecutor.execute(
+                select.bind()
+                    .setString(AGGREGATE_ID, aggregateId.asAggregateKey()))
+                .join());
+    }
+
+    private History toHistory(ResultSet resultSet) {
+        List<Event> events = cassandraUtils.convertToStream(resultSet)
+            .map(this::toEvent)
+            .collect(Guavate.toImmutableList());
+        return History.of(events);
+    }
+
+    private Event toEvent(Row row) {
+        try {
+            return jsonEventSerializer.deserialize(row.getString(EVENT));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/JsonEventSerializer.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/JsonEventSerializer.java b/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/JsonEventSerializer.java
new file mode 100644
index 0000000..dea4a5e
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/JsonEventSerializer.java
@@ -0,0 +1,98 @@
+/****************************************************************
+ * 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.cassandra;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+
+import javax.inject.Inject;
+
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.eventstore.cassandra.dto.EventDTO;
+import org.apache.james.eventsourcing.eventstore.cassandra.dto.EventDTOModule;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
+import com.github.steveash.guavate.Guavate;
+import com.google.common.collect.ImmutableSet;
+
+public class JsonEventSerializer {
+    public static class UnknownEventException extends RuntimeException {
+        public UnknownEventException(String message) {
+            super(message);
+        }
+    }
+
+    private final Map<Class<? extends Event>, EventDTOModule> eventClassToModule;
+    private final Map<String, EventDTOModule> typeToModule;
+    private final ObjectMapper objectMapper;
+
+    @Inject
+    public JsonEventSerializer(Set<EventDTOModule> modules) {
+        objectMapper = new ObjectMapper();
+        objectMapper.registerModule(new Jdk8Module());
+        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_ABSENT);
+
+        typeToModule = modules.stream()
+            .collect(Guavate.toImmutableMap(
+                EventDTOModule::getType,
+                Function.identity()));
+
+        eventClassToModule = modules.stream()
+            .collect(Guavate.toImmutableMap(
+                EventDTOModule::getEventClass,
+                Function.identity()));
+    }
+    
+    public JsonEventSerializer(EventDTOModule... modules) {
+        this(ImmutableSet.copyOf(modules));
+    }
+
+    public String serialize(Event event) throws JsonProcessingException {
+        Object dto = Optional.ofNullable(eventClassToModule.get(event.getClass()))
+            .orElseThrow(() -> new UnknownEventException("unknown event class " + event.getClass()))
+            .toDTO(event);
+        return objectMapper.writeValueAsString(dto);
+    }
+
+    public Event deserialize(String value) throws IOException {
+        JsonNode jsonNode = objectMapper.readTree(value);
+
+        String type = jsonNode.path("type").asText();
+
+        EventDTO dto = objectMapper.readValue(
+            objectMapper.treeAsTokens(jsonNode),
+            retrieveDTOClass(type));
+        return dto.toEvent();
+    }
+
+    public Class<? extends EventDTO> retrieveDTOClass(String type) {
+        return Optional.ofNullable(typeToModule.get(type))
+            .map(EventDTOModule::getDTOClass)
+            .orElseThrow(() -> new UnknownEventException("unknown event type " + type));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/EventDTO.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/EventDTO.java b/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/EventDTO.java
new file mode 100644
index 0000000..461212a
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/EventDTO.java
@@ -0,0 +1,26 @@
+/****************************************************************
+ * 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.cassandra.dto;
+
+import org.apache.james.eventsourcing.Event;
+
+public interface EventDTO {
+    Event toEvent();
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/EventDTOModule.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/EventDTOModule.java b/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/EventDTOModule.java
new file mode 100644
index 0000000..feaa5ef
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/EventDTOModule.java
@@ -0,0 +1,32 @@
+/****************************************************************
+ * 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.cassandra.dto;
+
+import org.apache.james.eventsourcing.Event;
+
+public interface EventDTOModule {
+    String getType();
+
+    Class<? extends EventDTO> getDTOClass();
+
+    Class<? extends Event> getEventClass();
+
+    EventDTO toDTO(Event event);
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventSourcingSystemTest.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventSourcingSystemTest.java b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventSourcingSystemTest.java
new file mode 100644
index 0000000..e538b93
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventSourcingSystemTest.java
@@ -0,0 +1,28 @@
+/****************************************************************
+ * 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.cassandra;
+
+import org.apache.james.eventsourcing.EventSourcingSystemTest;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(CassandraEventStoreExtension.class)
+public class CassandraEventSourcingSystemTest implements EventSourcingSystemTest {
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStoreExtension.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStoreExtension.java b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStoreExtension.java
new file mode 100644
index 0000000..73754a9
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStoreExtension.java
@@ -0,0 +1,30 @@
+/****************************************************************
+ * 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.cassandra;
+
+import org.apache.james.eventsourcing.eventstore.cassandra.dto.TestEventDTOModule;
+
+import com.google.common.collect.ImmutableSet;
+
+public class CassandraEventStoreExtension extends CassandraGenericEventStoreExtension {
+    public CassandraEventStoreExtension() {
+        super(ImmutableSet.of(new TestEventDTOModule()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStoreTest.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStoreTest.java b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStoreTest.java
new file mode 100644
index 0000000..6d240c3
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraEventStoreTest.java
@@ -0,0 +1,28 @@
+/****************************************************************
+ * 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.cassandra;
+
+import org.apache.james.eventsourcing.eventstore.EventStoreTest;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(CassandraEventStoreExtension.class)
+class CassandraEventStoreTest implements EventStoreTest {
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraGenericEventStoreExtension.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraGenericEventStoreExtension.java b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraGenericEventStoreExtension.java
new file mode 100644
index 0000000..699153d
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/CassandraGenericEventStoreExtension.java
@@ -0,0 +1,87 @@
+/****************************************************************
+ * 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.cassandra;
+
+import java.util.Set;
+
+import org.apache.james.backends.cassandra.CassandraCluster;
+import org.apache.james.backends.cassandra.DockerCassandraExtension;
+import org.apache.james.backends.cassandra.DockerCassandraExtension.DockerCassandra;
+import org.apache.james.backends.cassandra.utils.CassandraUtils;
+import org.apache.james.eventsourcing.eventstore.EventStore;
+import org.apache.james.eventsourcing.eventstore.cassandra.dto.EventDTOModule;
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.ParameterResolver;
+
+public class CassandraGenericEventStoreExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, ParameterResolver {
+    private final DockerCassandraExtension dockerCassandraExtension;
+    private final Set<EventDTOModule> modules;
+    private CassandraCluster cassandra;
+    private DockerCassandra dockerCassandra;
+    private EventStoreDao eventStoreDao;
+
+    public CassandraGenericEventStoreExtension(Set<EventDTOModule> modules) {
+        this.modules = modules;
+        dockerCassandraExtension = new DockerCassandraExtension();
+    }
+
+    @Override
+    public void beforeAll(ExtensionContext context) throws Exception {
+        dockerCassandraExtension.beforeAll(context);
+        dockerCassandra = dockerCassandraExtension.getDockerCassandra();
+    }
+
+    @Override
+    public void afterAll(ExtensionContext context) throws Exception {
+        dockerCassandraExtension.afterAll(context);
+    }
+
+    @Override
+    public void beforeEach(ExtensionContext context) {
+        cassandra = CassandraCluster.create(
+                new CassandraEventStoreModule(), dockerCassandra.getIp(), dockerCassandra.getBindingPort());
+
+        JsonEventSerializer jsonEventSerializer = new JsonEventSerializer(modules);
+
+        eventStoreDao = new EventStoreDao(cassandra.getConf(), CassandraUtils.WITH_DEFAULT_CONFIGURATION,
+            jsonEventSerializer);
+    }
+
+    @Override
+    public void afterEach(ExtensionContext context) {
+        cassandra.close();
+    }
+
+    @Override
+    public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
+        return (parameterContext.getParameter().getType() == EventStore.class);
+    }
+
+    @Override
+    public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
+        return new CassandraEventStore(eventStoreDao);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/JsonEventSerializerTest.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/JsonEventSerializerTest.java b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/JsonEventSerializerTest.java
new file mode 100644
index 0000000..1664ab1
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/JsonEventSerializerTest.java
@@ -0,0 +1,102 @@
+/****************************************************************
+ * 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.cassandra;
+
+import static net.javacrumbs.jsonunit.fluent.JsonFluentAssert.assertThatJson;
+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.apache.james.eventsourcing.eventstore.cassandra.dto.OtherEvent;
+import org.apache.james.eventsourcing.eventstore.cassandra.dto.OtherTestEventDTOModule;
+import org.apache.james.eventsourcing.eventstore.cassandra.dto.TestEventDTOModule;
+import org.junit.jupiter.api.Test;
+
+class JsonEventSerializerTest {
+    public static final EventId EVENT_ID = EventId.fromSerialized(0);
+    public static final TestAggregateId AGGREGATE_ID = TestAggregateId.testId(1);
+
+    public static final OtherEvent OTHER_EVENT = new OtherEvent(EVENT_ID, AGGREGATE_ID, 1);
+    public static final TestEvent TEST_EVENT = new TestEvent(EVENT_ID, AGGREGATE_ID, "first");
+
+    public static final String TEST_EVENT_JSON = "{\"type\":\"TestType\",\"data\":\"first\",\"eventId\":0,\"aggregate\":1}";
+    public static final String OTHER_EVENT_JSON = "{\"type\":\"other-type\",\"data\":1,\"eventId\":0,\"aggregate\":1}";
+
+    @Test
+    void shouldDeserializeKnownEvent() throws Exception {
+        assertThat(new JsonEventSerializer(new TestEventDTOModule())
+            .deserialize(TEST_EVENT_JSON))
+            .isEqualTo(TEST_EVENT);
+    }
+
+    @Test
+    void shouldThrowWhenDeserializeUnknownEvent() {
+        assertThatThrownBy(() -> new JsonEventSerializer()
+            .deserialize(TEST_EVENT_JSON))
+            .isInstanceOf(JsonEventSerializer.UnknownEventException.class);
+    }
+
+    @Test
+    void serializeShouldHandleAllKnownEvents() throws Exception {
+        JsonEventSerializer jsonEventSerializer = new JsonEventSerializer(
+            new TestEventDTOModule(),
+            new OtherTestEventDTOModule());
+
+        assertThatJson(
+            jsonEventSerializer.serialize(OTHER_EVENT))
+            .isEqualTo(OTHER_EVENT_JSON);
+
+        assertThatJson(
+            jsonEventSerializer.serialize(TEST_EVENT))
+            .isEqualTo(TEST_EVENT_JSON);
+    }
+
+    @Test
+    void deserializeShouldHandleAllKnownEvents() throws Exception {
+        JsonEventSerializer jsonEventSerializer = new JsonEventSerializer(
+            new TestEventDTOModule(),
+            new OtherTestEventDTOModule());
+
+        assertThatJson(
+            jsonEventSerializer.deserialize(OTHER_EVENT_JSON))
+            .isEqualTo(OTHER_EVENT);
+
+        assertThatJson(
+            jsonEventSerializer.deserialize(TEST_EVENT_JSON))
+            .isEqualTo(TEST_EVENT);
+    }
+
+    @Test
+    void shouldSerializeKnownEvent() throws Exception {
+        assertThatJson(new JsonEventSerializer(new TestEventDTOModule())
+            .serialize(TEST_EVENT))
+            .isEqualTo(TEST_EVENT_JSON);
+    }
+
+    @Test
+    void shouldThrowWhenSerializeUnknownEvent() {
+        assertThatThrownBy(() -> new JsonEventSerializer()
+            .serialize(TEST_EVENT))
+            .isInstanceOf(JsonEventSerializer.UnknownEventException.class);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/OtherEvent.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/OtherEvent.java b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/OtherEvent.java
new file mode 100644
index 0000000..1b21a36
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/OtherEvent.java
@@ -0,0 +1,50 @@
+/****************************************************************
+ * 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.cassandra.dto;
+
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.EventId;
+import org.apache.james.eventsourcing.TestAggregateId;
+
+public class OtherEvent implements Event {
+    private final EventId eventId;
+    private final TestAggregateId aggregateId;
+    private final long payload;
+
+    public OtherEvent(EventId eventId, TestAggregateId aggregateId, long payload) {
+        this.eventId = eventId;
+        this.aggregateId = aggregateId;
+        this.payload = payload;
+    }
+
+    @Override
+    public EventId eventId() {
+        return eventId;
+    }
+
+    @Override
+    public TestAggregateId getAggregateId() {
+        return aggregateId;
+    }
+
+    public long getPayload() {
+        return payload;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/OtherTestEventDTO.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/OtherTestEventDTO.java b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/OtherTestEventDTO.java
new file mode 100644
index 0000000..d4739f8
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/OtherTestEventDTO.java
@@ -0,0 +1,72 @@
+/****************************************************************
+ * 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.cassandra.dto;
+
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.EventId;
+import org.apache.james.eventsourcing.TestAggregateId;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class OtherTestEventDTO implements EventDTO {
+    private final String type;
+    private final long data;
+    private final int eventId;
+    private final int aggregate;
+
+    @JsonCreator
+    public OtherTestEventDTO(
+            @JsonProperty("type") String type,
+            @JsonProperty("data") long data,
+            @JsonProperty("eventId") int eventId,
+            @JsonProperty("aggregate") int aggregate) {
+        this.type = type;
+        this.data = data;
+        this.eventId = eventId;
+        this.aggregate = aggregate;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public long getData() {
+        return data;
+    }
+
+    public long getEventId() {
+        return eventId;
+    }
+
+    public int getAggregate() {
+        return aggregate;
+    }
+
+    @JsonIgnore
+    @Override
+    public Event toEvent() {
+        return new OtherEvent(
+            EventId.fromSerialized(eventId),
+            TestAggregateId.testId(aggregate),
+            data);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/OtherTestEventDTOModule.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/OtherTestEventDTOModule.java b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/OtherTestEventDTOModule.java
new file mode 100644
index 0000000..d025add
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/OtherTestEventDTOModule.java
@@ -0,0 +1,55 @@
+/****************************************************************
+ * 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.cassandra.dto;
+
+import org.apache.james.eventsourcing.Event;
+import org.testcontainers.shaded.com.google.common.base.Preconditions;
+
+public class OtherTestEventDTOModule implements EventDTOModule {
+
+    public static final String OTHER_TYPE = "other-type";
+
+    @Override
+    public String getType() {
+        return OTHER_TYPE;
+    }
+
+    @Override
+    public Class<? extends EventDTO> getDTOClass() {
+        return OtherTestEventDTO.class;
+    }
+
+    @Override
+    public Class<? extends Event> getEventClass() {
+        return OtherEvent.class;
+    }
+
+    @Override
+    public EventDTO toDTO(Event event) {
+        Preconditions.checkArgument(event instanceof OtherEvent);
+        OtherEvent otherEvent = (OtherEvent) event;
+
+        return new OtherTestEventDTO(
+            OTHER_TYPE,
+            otherEvent.getPayload(),
+            otherEvent.eventId().serialize(),
+            otherEvent.getAggregateId().getId());
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/TestEventDTO.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/TestEventDTO.java b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/TestEventDTO.java
new file mode 100644
index 0000000..709c709
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/TestEventDTO.java
@@ -0,0 +1,73 @@
+/****************************************************************
+ * 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.cassandra.dto;
+
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.EventId;
+import org.apache.james.eventsourcing.TestAggregateId;
+import org.apache.james.eventsourcing.TestEvent;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class TestEventDTO implements EventDTO {
+    private final String type;
+    private final String data;
+    private final int eventId;
+    private final int aggregate;
+
+    @JsonCreator
+    public TestEventDTO(
+            @JsonProperty("type") String type,
+            @JsonProperty("data") String data,
+            @JsonProperty("eventId") int eventId,
+            @JsonProperty("aggregate") int aggregate) {
+        this.type = type;
+        this.data = data;
+        this.eventId = eventId;
+        this.aggregate = aggregate;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public long getEventId() {
+        return eventId;
+    }
+
+    public int getAggregate() {
+        return aggregate;
+    }
+
+    @JsonIgnore
+    @Override
+    public Event toEvent() {
+        return new TestEvent(
+            EventId.fromSerialized(eventId),
+            TestAggregateId.testId(aggregate),
+            data);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/TestEventDTOModule.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/TestEventDTOModule.java b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/TestEventDTOModule.java
new file mode 100644
index 0000000..f0613ee
--- /dev/null
+++ b/event-sourcing/event-store-cassandra/src/test/java/org/apache/james/eventsourcing/eventstore/cassandra/dto/TestEventDTOModule.java
@@ -0,0 +1,56 @@
+/****************************************************************
+ * 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.cassandra.dto;
+
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.TestEvent;
+import org.testcontainers.shaded.com.google.common.base.Preconditions;
+
+public class TestEventDTOModule implements EventDTOModule {
+
+    public static final String TEST_TYPE = "TestType";
+
+    @Override
+    public String getType() {
+        return TEST_TYPE;
+    }
+
+    @Override
+    public Class<? extends EventDTO> getDTOClass() {
+        return TestEventDTO.class;
+    }
+
+    @Override
+    public Class<? extends Event> getEventClass() {
+        return TestEvent.class;
+    }
+
+    @Override
+    public EventDTO toDTO(Event event) {
+        Preconditions.checkArgument(event instanceof TestEvent);
+
+        TestEvent testEvent = (TestEvent) event;
+        return new TestEventDTO(
+            TEST_TYPE,
+            testEvent.getData(),
+            testEvent.eventId().serialize(),
+            testEvent.getAggregateId().getId());
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-memory/pom.xml
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-memory/pom.xml b/event-sourcing/event-store-memory/pom.xml
new file mode 100644
index 0000000..c23c93a
--- /dev/null
+++ b/event-sourcing/event-store-memory/pom.xml
@@ -0,0 +1,86 @@
+<?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-memory</artifactId>
+
+    <name>Apache James :: Event sourcing :: Event Store :: Memory</name>
+    <description>Memory implementation for James Event Store</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>event-sourcing-core</artifactId>
+            <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-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-pojo</artifactId>
+            <type>test-jar</type>
+            <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>
+    </dependencies>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-memory/src/main/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStore.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-memory/src/main/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStore.java b/event-sourcing/event-store-memory/src/main/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStore.java
new file mode 100644
index 0000000..69ccf5f
--- /dev/null
+++ b/event-sourcing/event-store-memory/src/main/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStore.java
@@ -0,0 +1,99 @@
+/****************************************************************
+ * 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.memory;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.james.eventsourcing.AggregateId;
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.eventstore.EventStore;
+import org.apache.james.eventsourcing.eventstore.EventStoreFailedException;
+import org.apache.james.eventsourcing.eventstore.History;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+public class InMemoryEventStore implements EventStore {
+
+    private final ConcurrentHashMap<AggregateId, History> store;
+
+    public InMemoryEventStore() {
+        this.store = new ConcurrentHashMap<>();
+    }
+
+    @Override
+    public void appendAll(List<Event> events) {
+        if (events.isEmpty()) {
+            return;
+        }
+        AggregateId aggregateId = getAggregateId(events);
+
+        if (!store.containsKey(aggregateId)) {
+            appendToEmptyHistory(aggregateId, events);
+        } else {
+            appendToExistingHistory(aggregateId, events);
+        }
+    }
+
+    private AggregateId getAggregateId(List<? extends Event> events) {
+        Preconditions.checkArgument(!events.isEmpty());
+        Preconditions.checkArgument(Event.belongsToSameAggregate(events));
+
+        return events.stream()
+            .map(Event::getAggregateId)
+            .findFirst()
+            .get();
+    }
+
+    private void appendToEmptyHistory(AggregateId aggregateId, List<Event> events) {
+        History newHistory = History.of(events);
+
+        History previousHistory = store.putIfAbsent(aggregateId, newHistory);
+        if (previousHistory != null) {
+            throw new EventStoreFailedException();
+        }
+    }
+
+    private void appendToExistingHistory(AggregateId aggregateId, List<? extends Event> events) {
+        History currentHistory = store.get(aggregateId);
+        List<Event> updatedEvents = updatedEvents(currentHistory, events);
+        History updatedHistory = History.of(updatedEvents);
+
+        boolean isReplaced = store.replace(aggregateId, currentHistory, updatedHistory);
+        if (!isReplaced) {
+            throw new EventStoreFailedException();
+        }
+    }
+
+    private List<Event> updatedEvents(History currentHistory, List<? extends Event> newEvents) {
+        return ImmutableList.<Event>builder()
+            .addAll(currentHistory.getEvents())
+            .addAll(newEvents)
+            .build();
+    }
+
+    @Override
+    public History getEventsOfAggregate(AggregateId aggregateId) {
+        return Optional.ofNullable(store.get(aggregateId))
+            .orElse(History.empty());
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-memory/src/test/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventSourcingSystemTest.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-memory/src/test/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventSourcingSystemTest.java b/event-sourcing/event-store-memory/src/test/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventSourcingSystemTest.java
new file mode 100644
index 0000000..4a07c40
--- /dev/null
+++ b/event-sourcing/event-store-memory/src/test/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventSourcingSystemTest.java
@@ -0,0 +1,28 @@
+/****************************************************************
+ * 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.memory;
+
+import org.apache.james.eventsourcing.EventSourcingSystemTest;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(InMemoryEventStoreExtension.class)
+public class InMemoryEventSourcingSystemTest implements EventSourcingSystemTest {
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-memory/src/test/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStoreExtension.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-memory/src/test/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStoreExtension.java b/event-sourcing/event-store-memory/src/test/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStoreExtension.java
new file mode 100644
index 0000000..03f61bc
--- /dev/null
+++ b/event-sourcing/event-store-memory/src/test/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStoreExtension.java
@@ -0,0 +1,39 @@
+/****************************************************************
+ * 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.memory;
+
+import org.apache.james.eventsourcing.eventstore.EventStore;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.ParameterResolver;
+
+public class InMemoryEventStoreExtension implements ParameterResolver {
+
+    @Override
+    public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
+        return (parameterContext.getParameter().getType() == EventStore.class);
+    }
+
+    @Override
+    public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
+        return new InMemoryEventStore();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/event-store-memory/src/test/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStoreTest.java
----------------------------------------------------------------------
diff --git a/event-sourcing/event-store-memory/src/test/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStoreTest.java b/event-sourcing/event-store-memory/src/test/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStoreTest.java
new file mode 100644
index 0000000..11cd197
--- /dev/null
+++ b/event-sourcing/event-store-memory/src/test/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStoreTest.java
@@ -0,0 +1,28 @@
+/****************************************************************
+ * 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.memory;
+
+import org.apache.james.eventsourcing.eventstore.EventStoreTest;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(InMemoryEventStoreExtension.class)
+public class InMemoryEventStoreTest implements EventStoreTest {
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/event-sourcing/pom.xml
----------------------------------------------------------------------
diff --git a/event-sourcing/pom.xml b/event-sourcing/pom.xml
new file mode 100644
index 0000000..920700b
--- /dev/null
+++ b/event-sourcing/pom.xml
@@ -0,0 +1,44 @@
+<?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>james-project</artifactId>
+        <version>3.1.0-SNAPSHOT</version>
+    </parent>
+
+    <packaging>pom</packaging>
+    <artifactId>event-sourcing</artifactId>
+
+    <name>Apache James :: Event Sourcing</name>
+
+    <modules>
+        <module>event-sourcing-core</module>
+        <module>event-sourcing-pojo</module>
+        <module>event-store-api</module>
+        <module>event-store-cassandra</module>
+        <module>event-store-memory</module>
+    </modules>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/pom.xml
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/pom.xml b/mailbox/plugin/quota-mailing-cassandra/pom.xml
index b6b3b2c..bc3b954 100644
--- a/mailbox/plugin/quota-mailing-cassandra/pom.xml
+++ b/mailbox/plugin/quota-mailing-cassandra/pom.xml
@@ -36,6 +36,27 @@
     <dependencies>
         <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>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>event-sourcing-event-store-cassandra</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>event-sourcing-event-store-cassandra</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
             <artifactId>apache-james-backends-cassandra</artifactId>
         </dependency>
         <dependency>

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStore.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStore.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStore.java
deleted file mode 100644
index 61c5a06..0000000
--- a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStore.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/****************************************************************
- * 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.cassandra;
-
-import java.util.List;
-
-import javax.inject.Inject;
-
-import org.apache.james.eventsourcing.AggregateId;
-import org.apache.james.eventsourcing.Event;
-import org.apache.james.eventsourcing.EventStore;
-
-import com.google.common.base.Preconditions;
-
-public class CassandraEventStore implements EventStore {
-
-    private final EventStoreDao eventStoreDao;
-
-    @Inject
-    public CassandraEventStore(EventStoreDao eventStoreDao) {
-        this.eventStoreDao = eventStoreDao;
-    }
-
-    @Override
-    public void appendAll(List<Event> events) {
-        if (events.isEmpty()) {
-            return;
-        }
-        doAppendAll(events);
-    }
-
-    public void doAppendAll(List<Event> events) {
-        Preconditions.checkArgument(Event.belongsToSameAggregate(events));
-
-        boolean success = eventStoreDao.appendAll(events).join();
-        if (!success) {
-            throw new EventStoreFailedException();
-        }
-    }
-
-    @Override
-    public History getEventsOfAggregate(AggregateId aggregateId) {
-        return eventStoreDao.getEventsOfAggregate(aggregateId);
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreModule.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreModule.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreModule.java
deleted file mode 100644
index ed15cb2..0000000
--- a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreModule.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/****************************************************************
- * 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.cassandra;
-
-import java.util.List;
-
-import org.apache.james.backends.cassandra.components.CassandraModule;
-import org.apache.james.backends.cassandra.components.CassandraTable;
-import org.apache.james.backends.cassandra.components.CassandraType;
-import org.apache.james.backends.cassandra.utils.CassandraConstants;
-
-import com.datastax.driver.core.DataType;
-import com.datastax.driver.core.schemabuilder.SchemaBuilder;
-import com.google.common.collect.ImmutableList;
-
-public class CassandraEventStoreModule implements CassandraModule {
-    private final List<CassandraTable> tables;
-    private final List<CassandraType> types;
-
-    public CassandraEventStoreModule() {
-        tables = ImmutableList.of(
-            new CassandraTable(CassandraEventStoreTable.EVENTS_TABLE,
-                SchemaBuilder.createTable(CassandraEventStoreTable.EVENTS_TABLE)
-                    .ifNotExists()
-                    .addPartitionKey(CassandraEventStoreTable.AGGREGATE_ID, DataType.varchar())
-                    .addClusteringColumn(CassandraEventStoreTable.EVENT_ID, DataType.cint())
-                    .addColumn(CassandraEventStoreTable.EVENT, DataType.text())
-                    .withOptions()
-                    .comment("Store events of a EventSourcing aggregate")
-                    .caching(SchemaBuilder.KeyCaching.ALL,
-                            SchemaBuilder.rows(CassandraConstants.DEFAULT_CACHED_ROW_PER_PARTITION))));
-        types = ImmutableList.of();
-    }
-
-    @Override
-    public List<CassandraTable> moduleTables() {
-        return tables;
-    }
-
-    @Override
-    public List<CassandraType> moduleTypes() {
-        return types;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTable.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTable.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTable.java
deleted file mode 100644
index c90b81e..0000000
--- a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTable.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/****************************************************************
- * 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.cassandra;
-
-public interface CassandraEventStoreTable {
-    String EVENTS_TABLE = "eventStore";
-    String AGGREGATE_ID = "aggregateId";
-    String EVENT = "event";
-    String EVENT_ID = "eventId";
-}


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[2/4] james-project git commit: JAMES-2393 Introduce dedicated modules for event sourcing

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/EventStoreDao.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/EventStoreDao.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/EventStoreDao.java
deleted file mode 100644
index 2518f02..0000000
--- a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/EventStoreDao.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/****************************************************************
- * 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.cassandra;
-
-import static com.datastax.driver.core.querybuilder.QueryBuilder.bindMarker;
-import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
-import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto;
-import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
-import static org.apache.james.eventsourcing.cassandra.CassandraEventStoreTable.AGGREGATE_ID;
-import static org.apache.james.eventsourcing.cassandra.CassandraEventStoreTable.EVENT;
-import static org.apache.james.eventsourcing.cassandra.CassandraEventStoreTable.EVENTS_TABLE;
-import static org.apache.james.eventsourcing.cassandra.CassandraEventStoreTable.EVENT_ID;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.concurrent.CompletableFuture;
-
-import javax.inject.Inject;
-
-import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
-import org.apache.james.backends.cassandra.utils.CassandraUtils;
-import org.apache.james.eventsourcing.AggregateId;
-import org.apache.james.eventsourcing.Event;
-import org.apache.james.eventsourcing.EventStore;
-
-import com.datastax.driver.core.BatchStatement;
-import com.datastax.driver.core.BoundStatement;
-import com.datastax.driver.core.PreparedStatement;
-import com.datastax.driver.core.ResultSet;
-import com.datastax.driver.core.Row;
-import com.datastax.driver.core.Session;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.github.steveash.guavate.Guavate;
-
-public class EventStoreDao {
-    private final CassandraUtils cassandraUtils;
-    private final CassandraAsyncExecutor cassandraAsyncExecutor;
-    private final PreparedStatement insert;
-    private final PreparedStatement select;
-    private final JsonEventSerializer jsonEventSerializer;
-
-    @Inject
-    public EventStoreDao(Session session, CassandraUtils cassandraUtils, JsonEventSerializer jsonEventSerializer) {
-        this.cassandraUtils = cassandraUtils;
-        this.cassandraAsyncExecutor = new CassandraAsyncExecutor(session);
-        this.jsonEventSerializer = jsonEventSerializer;
-        this.insert = prepareInsert(session);
-        this.select = prepareSelect(session);
-    }
-
-    private PreparedStatement prepareInsert(Session session) {
-        return session.prepare(insertInto(EVENTS_TABLE)
-            .value(AGGREGATE_ID, bindMarker(AGGREGATE_ID))
-            .value(EVENT_ID, bindMarker(EVENT_ID))
-            .value(EVENT, bindMarker(EVENT))
-            .ifNotExists());
-    }
-
-    private PreparedStatement prepareSelect(Session session) {
-        return session.prepare(select()
-            .from(EVENTS_TABLE)
-            .where(eq(AGGREGATE_ID, bindMarker(AGGREGATE_ID))));
-    }
-
-    public CompletableFuture<Boolean> appendAll(List<Event> events) {
-        BatchStatement batch = new BatchStatement();
-        events.forEach(event -> batch.add(insertEvent(event)));
-        return cassandraAsyncExecutor.executeReturnApplied(batch);
-    }
-
-    private BoundStatement insertEvent(Event event) {
-        try {
-            return insert
-                .bind()
-                .setString(AGGREGATE_ID, event.getAggregateId().asAggregateKey())
-                .setInt(EVENT_ID, event.eventId().serialize())
-                .setString(EVENT, jsonEventSerializer.serialize(event));
-        } catch (JsonProcessingException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    public EventStore.History getEventsOfAggregate(AggregateId aggregateId) {
-        return toHistory(
-            cassandraAsyncExecutor.execute(
-                select.bind()
-                    .setString(AGGREGATE_ID, aggregateId.asAggregateKey()))
-                .join());
-    }
-
-    private EventStore.History toHistory(ResultSet resultSet) {
-        List<Event> events = cassandraUtils.convertToStream(resultSet)
-            .map(this::toEvent)
-            .collect(Guavate.toImmutableList());
-        return EventStore.History.of(events);
-    }
-
-    private Event toEvent(Row row) {
-        try {
-            return jsonEventSerializer.deserialize(row.getString(EVENT));
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializer.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializer.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializer.java
deleted file mode 100644
index bf3a5dc..0000000
--- a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializer.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/****************************************************************
- * 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.cassandra;
-
-import java.io.IOException;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.function.Function;
-
-import javax.inject.Inject;
-
-import org.apache.james.eventsourcing.Event;
-import org.apache.james.eventsourcing.cassandra.dto.EventDTO;
-import org.apache.james.eventsourcing.cassandra.dto.EventDTOModule;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
-import com.github.steveash.guavate.Guavate;
-import com.google.common.collect.ImmutableSet;
-
-public class JsonEventSerializer {
-    public static class UnknownEventException extends RuntimeException {
-        public UnknownEventException(String message) {
-            super(message);
-        }
-    }
-
-    private final Map<Class<? extends Event>, EventDTOModule> eventClassToModule;
-    private final Map<String, EventDTOModule> typeToModule;
-    private final ObjectMapper objectMapper;
-
-    @Inject
-    public JsonEventSerializer(Set<EventDTOModule> modules) {
-        objectMapper = new ObjectMapper();
-        objectMapper.registerModule(new Jdk8Module());
-        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_ABSENT);
-
-        typeToModule = modules.stream()
-            .collect(Guavate.toImmutableMap(
-                EventDTOModule::getType,
-                Function.identity()));
-
-        eventClassToModule = modules.stream()
-            .collect(Guavate.toImmutableMap(
-                EventDTOModule::getEventClass,
-                Function.identity()));
-    }
-    
-    public JsonEventSerializer(EventDTOModule... modules) {
-        this(ImmutableSet.copyOf(modules));
-    }
-
-    public String serialize(Event event) throws JsonProcessingException {
-        Object dto = Optional.ofNullable(eventClassToModule.get(event.getClass()))
-            .orElseThrow(() -> new UnknownEventException("unknown event class " + event.getClass()))
-            .toDTO(event);
-        return objectMapper.writeValueAsString(dto);
-    }
-
-    public Event deserialize(String value) throws IOException {
-        JsonNode jsonNode = objectMapper.readTree(value);
-
-        String type = jsonNode.path("type").asText();
-
-        EventDTO dto = objectMapper.readValue(
-            objectMapper.treeAsTokens(jsonNode),
-            retrieveDTOClass(type));
-        return dto.toEvent();
-    }
-
-    public Class<? extends EventDTO> retrieveDTOClass(String type) {
-        return Optional.ofNullable(typeToModule.get(type))
-            .map(EventDTOModule::getDTOClass)
-            .orElseThrow(() -> new UnknownEventException("unknown event type " + type));
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTO.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTO.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTO.java
deleted file mode 100644
index 4616248..0000000
--- a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTO.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/****************************************************************
- * 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.cassandra.dto;
-
-import org.apache.james.eventsourcing.Event;
-
-public interface EventDTO {
-    Event toEvent();
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTOModule.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTOModule.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTOModule.java
deleted file mode 100644
index d86d1d7..0000000
--- a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTOModule.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/****************************************************************
- * 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.cassandra.dto;
-
-import org.apache.james.eventsourcing.Event;
-
-public interface EventDTOModule {
-    String getType();
-
-    Class<? extends EventDTO> getDTOClass();
-
-    Class<? extends Event> getEventClass();
-
-    EventDTO toDTO(Event event);
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTO.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTO.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTO.java
index 80eae8d..f229e2c 100644
--- a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTO.java
+++ b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTO.java
@@ -21,7 +21,7 @@ package org.apache.james.mailbox.quota.cassandra.dto;
 
 import org.apache.james.core.User;
 import org.apache.james.eventsourcing.EventId;
-import org.apache.james.eventsourcing.cassandra.dto.EventDTO;
+import org.apache.james.eventsourcing.eventstore.cassandra.dto.EventDTO;
 import org.apache.james.mailbox.quota.mailing.aggregates.UserQuotaThresholds;
 import org.apache.james.mailbox.quota.mailing.events.QuotaThresholdChangedEvent;
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTOModule.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTOModule.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTOModule.java
index 328cb09..7c37b7f 100644
--- a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTOModule.java
+++ b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTOModule.java
@@ -20,8 +20,8 @@
 package org.apache.james.mailbox.quota.cassandra.dto;
 
 import org.apache.james.eventsourcing.Event;
-import org.apache.james.eventsourcing.cassandra.dto.EventDTO;
-import org.apache.james.eventsourcing.cassandra.dto.EventDTOModule;
+import org.apache.james.eventsourcing.eventstore.cassandra.dto.EventDTO;
+import org.apache.james.eventsourcing.eventstore.cassandra.dto.EventDTOModule;
 import org.apache.james.mailbox.quota.mailing.events.QuotaThresholdChangedEvent;
 
 import com.google.common.base.Preconditions;

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventSourcingSystemTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventSourcingSystemTest.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventSourcingSystemTest.java
deleted file mode 100644
index bee6e88..0000000
--- a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventSourcingSystemTest.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/****************************************************************
- * 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.cassandra;
-
-import org.apache.james.eventsourcing.EventSourcingSystemTest;
-import org.junit.jupiter.api.extension.ExtendWith;
-
-@ExtendWith(CassandraEventStoreExtension.class)
-public class CassandraEventSourcingSystemTest implements EventSourcingSystemTest {
-
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreExtension.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreExtension.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreExtension.java
deleted file mode 100644
index 1b0066f..0000000
--- a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreExtension.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/****************************************************************
- * 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.cassandra;
-
-import org.apache.james.backends.cassandra.CassandraCluster;
-import org.apache.james.backends.cassandra.DockerCassandraExtension;
-import org.apache.james.backends.cassandra.DockerCassandraExtension.DockerCassandra;
-import org.apache.james.backends.cassandra.utils.CassandraUtils;
-import org.apache.james.eventsourcing.EventStore;
-import org.apache.james.eventsourcing.cassandra.dto.TestEventDTOModule;
-import org.apache.james.mailbox.quota.cassandra.dto.QuotaThresholdChangedEventDTOModule;
-import org.junit.jupiter.api.extension.AfterAllCallback;
-import org.junit.jupiter.api.extension.AfterEachCallback;
-import org.junit.jupiter.api.extension.BeforeAllCallback;
-import org.junit.jupiter.api.extension.BeforeEachCallback;
-import org.junit.jupiter.api.extension.ExtensionContext;
-import org.junit.jupiter.api.extension.ParameterContext;
-import org.junit.jupiter.api.extension.ParameterResolutionException;
-import org.junit.jupiter.api.extension.ParameterResolver;
-
-public class CassandraEventStoreExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, ParameterResolver {
-    private final DockerCassandraExtension dockerCassandraExtension;
-    private CassandraCluster cassandra;
-    private DockerCassandra dockerCassandra;
-    private EventStoreDao eventStoreDao;
-
-    public CassandraEventStoreExtension() {
-        dockerCassandraExtension = new DockerCassandraExtension();
-    }
-
-    @Override
-    public void beforeAll(ExtensionContext context) throws Exception {
-        dockerCassandraExtension.beforeAll(context);
-        dockerCassandra = dockerCassandraExtension.getDockerCassandra();
-    }
-
-    @Override
-    public void afterAll(ExtensionContext context) throws Exception {
-        dockerCassandraExtension.afterAll(context);
-    }
-
-    @Override
-    public void beforeEach(ExtensionContext context) {
-        cassandra = CassandraCluster.create(
-                new CassandraEventStoreModule(), dockerCassandra.getIp(), dockerCassandra.getBindingPort());
-
-        JsonEventSerializer jsonEventSerializer = new JsonEventSerializer(
-            new QuotaThresholdChangedEventDTOModule(),
-            new TestEventDTOModule());
-
-        eventStoreDao = new EventStoreDao(cassandra.getConf(), CassandraUtils.WITH_DEFAULT_CONFIGURATION,
-            jsonEventSerializer);
-    }
-
-    @Override
-    public void afterEach(ExtensionContext context) {
-        cassandra.close();
-    }
-
-    @Override
-    public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
-        return (parameterContext.getParameter().getType() == EventStore.class);
-    }
-
-    @Override
-    public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
-        return new CassandraEventStore(eventStoreDao);
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTest.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTest.java
deleted file mode 100644
index 314b185..0000000
--- a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTest.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/****************************************************************
- * 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.cassandra;
-
-import org.apache.james.eventsourcing.EventStoreTest;
-import org.junit.jupiter.api.extension.ExtendWith;
-
-@ExtendWith(CassandraEventStoreExtension.class)
-class CassandraEventStoreTest implements EventStoreTest {
-
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializerTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializerTest.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializerTest.java
deleted file mode 100644
index 89770bb..0000000
--- a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializerTest.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/****************************************************************
- * 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.cassandra;
-
-import static net.javacrumbs.jsonunit.fluent.JsonFluentAssert.assertThatJson;
-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.apache.james.eventsourcing.cassandra.dto.OtherEvent;
-import org.apache.james.eventsourcing.cassandra.dto.OtherTestEventDTOModule;
-import org.apache.james.eventsourcing.cassandra.dto.TestEventDTOModule;
-import org.junit.jupiter.api.Test;
-
-class JsonEventSerializerTest {
-    public static final EventId EVENT_ID = EventId.fromSerialized(0);
-    public static final TestAggregateId AGGREGATE_ID = TestAggregateId.testId(1);
-
-    public static final OtherEvent OTHER_EVENT = new OtherEvent(EVENT_ID, AGGREGATE_ID, 1);
-    public static final TestEvent TEST_EVENT = new TestEvent(EVENT_ID, AGGREGATE_ID, "first");
-
-    public static final String TEST_EVENT_JSON = "{\"type\":\"TestType\",\"data\":\"first\",\"eventId\":0,\"aggregate\":1}";
-    public static final String OTHER_EVENT_JSON = "{\"type\":\"other-type\",\"data\":1,\"eventId\":0,\"aggregate\":1}";
-
-    @Test
-    void shouldDeserializeKnownEvent() throws Exception {
-        assertThat(new JsonEventSerializer(new TestEventDTOModule())
-            .deserialize(TEST_EVENT_JSON))
-            .isEqualTo(TEST_EVENT);
-    }
-
-    @Test
-    void shouldThrowWhenDeserializeUnknownEvent() {
-        assertThatThrownBy(() -> new JsonEventSerializer()
-            .deserialize(TEST_EVENT_JSON))
-            .isInstanceOf(JsonEventSerializer.UnknownEventException.class);
-    }
-
-    @Test
-    void serializeShouldHandleAllKnownEvents() throws Exception {
-        JsonEventSerializer jsonEventSerializer = new JsonEventSerializer(
-            new TestEventDTOModule(),
-            new OtherTestEventDTOModule());
-
-        assertThatJson(
-            jsonEventSerializer.serialize(OTHER_EVENT))
-            .isEqualTo(OTHER_EVENT_JSON);
-
-        assertThatJson(
-            jsonEventSerializer.serialize(TEST_EVENT))
-            .isEqualTo(TEST_EVENT_JSON);
-    }
-
-    @Test
-    void deserializeShouldHandleAllKnownEvents() throws Exception {
-        JsonEventSerializer jsonEventSerializer = new JsonEventSerializer(
-            new TestEventDTOModule(),
-            new OtherTestEventDTOModule());
-
-        assertThatJson(
-            jsonEventSerializer.deserialize(OTHER_EVENT_JSON))
-            .isEqualTo(OTHER_EVENT);
-
-        assertThatJson(
-            jsonEventSerializer.deserialize(TEST_EVENT_JSON))
-            .isEqualTo(TEST_EVENT);
-    }
-
-    @Test
-    void shouldSerializeKnownEvent() throws Exception {
-        assertThatJson(new JsonEventSerializer(new TestEventDTOModule())
-            .serialize(TEST_EVENT))
-            .isEqualTo(TEST_EVENT_JSON);
-    }
-
-    @Test
-    void shouldThrowWhenSerializeUnknownEvent() {
-        assertThatThrownBy(() -> new JsonEventSerializer()
-            .serialize(TEST_EVENT))
-            .isInstanceOf(JsonEventSerializer.UnknownEventException.class);
-    }
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/OtherEvent.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/OtherEvent.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/OtherEvent.java
deleted file mode 100644
index 4e74720..0000000
--- a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/OtherEvent.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/****************************************************************
- * 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.cassandra.dto;
-
-import org.apache.james.eventsourcing.Event;
-import org.apache.james.eventsourcing.EventId;
-import org.apache.james.eventsourcing.TestAggregateId;
-
-public class OtherEvent implements Event {
-    private final EventId eventId;
-    private final TestAggregateId aggregateId;
-    private final long payload;
-
-    public OtherEvent(EventId eventId, TestAggregateId aggregateId, long payload) {
-        this.eventId = eventId;
-        this.aggregateId = aggregateId;
-        this.payload = payload;
-    }
-
-    @Override
-    public EventId eventId() {
-        return eventId;
-    }
-
-    @Override
-    public TestAggregateId getAggregateId() {
-        return aggregateId;
-    }
-
-    public long getPayload() {
-        return payload;
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/OtherTestEventDTO.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/OtherTestEventDTO.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/OtherTestEventDTO.java
deleted file mode 100644
index 4f35c12..0000000
--- a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/OtherTestEventDTO.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/****************************************************************
- * 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.cassandra.dto;
-
-import org.apache.james.eventsourcing.Event;
-import org.apache.james.eventsourcing.EventId;
-import org.apache.james.eventsourcing.TestAggregateId;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-public class OtherTestEventDTO implements EventDTO {
-    private final String type;
-    private final long data;
-    private final int eventId;
-    private final int aggregate;
-
-    @JsonCreator
-    public OtherTestEventDTO(
-            @JsonProperty("type") String type,
-            @JsonProperty("data") long data,
-            @JsonProperty("eventId") int eventId,
-            @JsonProperty("aggregate") int aggregate) {
-        this.type = type;
-        this.data = data;
-        this.eventId = eventId;
-        this.aggregate = aggregate;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public long getData() {
-        return data;
-    }
-
-    public long getEventId() {
-        return eventId;
-    }
-
-    public int getAggregate() {
-        return aggregate;
-    }
-
-    @JsonIgnore
-    @Override
-    public Event toEvent() {
-        return new OtherEvent(
-            EventId.fromSerialized(eventId),
-            TestAggregateId.testId(aggregate),
-            data);
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/OtherTestEventDTOModule.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/OtherTestEventDTOModule.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/OtherTestEventDTOModule.java
deleted file mode 100644
index deb9a02..0000000
--- a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/OtherTestEventDTOModule.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************
- * 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.cassandra.dto;
-
-import org.apache.james.eventsourcing.Event;
-import org.testcontainers.shaded.com.google.common.base.Preconditions;
-
-public class OtherTestEventDTOModule implements EventDTOModule {
-
-    public static final String OTHER_TYPE = "other-type";
-
-    @Override
-    public String getType() {
-        return OTHER_TYPE;
-    }
-
-    @Override
-    public Class<? extends EventDTO> getDTOClass() {
-        return OtherTestEventDTO.class;
-    }
-
-    @Override
-    public Class<? extends Event> getEventClass() {
-        return OtherEvent.class;
-    }
-
-    @Override
-    public EventDTO toDTO(Event event) {
-        Preconditions.checkArgument(event instanceof OtherEvent);
-        OtherEvent otherEvent = (OtherEvent) event;
-
-        return new OtherTestEventDTO(
-            OTHER_TYPE,
-            otherEvent.getPayload(),
-            otherEvent.eventId().serialize(),
-            otherEvent.getAggregateId().getId());
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTO.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTO.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTO.java
deleted file mode 100644
index 529d8a9..0000000
--- a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTO.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/****************************************************************
- * 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.cassandra.dto;
-
-import org.apache.james.eventsourcing.Event;
-import org.apache.james.eventsourcing.EventId;
-import org.apache.james.eventsourcing.TestAggregateId;
-import org.apache.james.eventsourcing.TestEvent;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-public class TestEventDTO implements EventDTO {
-    private final String type;
-    private final String data;
-    private final int eventId;
-    private final int aggregate;
-
-    @JsonCreator
-    public TestEventDTO(
-            @JsonProperty("type") String type,
-            @JsonProperty("data") String data,
-            @JsonProperty("eventId") int eventId,
-            @JsonProperty("aggregate") int aggregate) {
-        this.type = type;
-        this.data = data;
-        this.eventId = eventId;
-        this.aggregate = aggregate;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public String getData() {
-        return data;
-    }
-
-    public long getEventId() {
-        return eventId;
-    }
-
-    public int getAggregate() {
-        return aggregate;
-    }
-
-    @JsonIgnore
-    @Override
-    public Event toEvent() {
-        return new TestEvent(
-            EventId.fromSerialized(eventId),
-            TestAggregateId.testId(aggregate),
-            data);
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTOModule.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTOModule.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTOModule.java
deleted file mode 100644
index 6b46268..0000000
--- a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTOModule.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************
- * 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.cassandra.dto;
-
-import org.apache.james.eventsourcing.Event;
-import org.apache.james.eventsourcing.TestEvent;
-import org.testcontainers.shaded.com.google.common.base.Preconditions;
-
-public class TestEventDTOModule implements EventDTOModule {
-
-    public static final String TEST_TYPE = "TestType";
-
-    @Override
-    public String getType() {
-        return TEST_TYPE;
-    }
-
-    @Override
-    public Class<? extends EventDTO> getDTOClass() {
-        return TestEventDTO.class;
-    }
-
-    @Override
-    public Class<? extends Event> getEventClass() {
-        return TestEvent.class;
-    }
-
-    @Override
-    public EventDTO toDTO(Event event) {
-        Preconditions.checkArgument(event instanceof TestEvent);
-
-        TestEvent testEvent = (TestEvent) event;
-        return new TestEventDTO(
-            TEST_TYPE,
-            testEvent.getData(),
-            testEvent.eventId().serialize(),
-            testEvent.getAggregateId().getId());
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/dto/DTOTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/dto/DTOTest.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/dto/DTOTest.java
index 2f48a55..ca063d9 100644
--- a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/dto/DTOTest.java
+++ b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/dto/DTOTest.java
@@ -28,7 +28,7 @@ import java.time.Instant;
 
 import org.apache.james.core.User;
 import org.apache.james.eventsourcing.EventId;
-import org.apache.james.eventsourcing.cassandra.JsonEventSerializer;
+import org.apache.james.eventsourcing.eventstore.cassandra.JsonEventSerializer;
 import org.apache.james.mailbox.model.Quota;
 import org.apache.james.mailbox.quota.QuotaCount;
 import org.apache.james.mailbox.quota.QuotaSize;
@@ -46,24 +46,24 @@ import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
 
 class DTOTest {
 
-    public static final Quota<QuotaSize> SIZE_QUOTA = Quota.<QuotaSize>builder().used(QuotaSize.size(23)).computedLimit(QuotaSize.size(33)).build();
-    public static final Quota<QuotaCount> COUNT_QUOTA = Quota.<QuotaCount>builder().used(QuotaCount.count(12)).computedLimit(QuotaCount.count(45)).build();
-    public static final Instant INSTANT = Instant.ofEpochMilli(45554);
-    public static final QuotaThresholdChangedEvent EVENT = new QuotaThresholdChangedEvent(
+    static final Quota<QuotaSize> SIZE_QUOTA = Quota.<QuotaSize>builder().used(QuotaSize.size(23)).computedLimit(QuotaSize.size(33)).build();
+    static final Quota<QuotaCount> COUNT_QUOTA = Quota.<QuotaCount>builder().used(QuotaCount.count(12)).computedLimit(QuotaCount.count(45)).build();
+    static final Instant INSTANT = Instant.ofEpochMilli(45554);
+    static final QuotaThresholdChangedEvent EVENT = new QuotaThresholdChangedEvent(
         EventId.first(),
         HistoryEvolution.noChanges(),
         HistoryEvolution.noChanges(),
         SIZE_QUOTA,
         COUNT_QUOTA,
         UserQuotaThresholds.Id.from(User.fromUsername("foo@bar.com")));
-    public static final QuotaThresholdChangedEvent EVENT_2 = new QuotaThresholdChangedEvent(
+    static final QuotaThresholdChangedEvent EVENT_2 = new QuotaThresholdChangedEvent(
         EventId.first(),
         HistoryEvolution.lowerThresholdReached(new QuotaThresholdChange(_75, INSTANT)),
         HistoryEvolution.noChanges(),
         SIZE_QUOTA,
         Quota.<QuotaCount>builder().used(QuotaCount.count(12)).computedLimit(QuotaCount.unlimited()).build(),
         UserQuotaThresholds.Id.from(User.fromUsername("foo@bar.com")));
-    public static final QuotaThresholdChangedEvent EVENT_3 = new QuotaThresholdChangedEvent(
+    static final QuotaThresholdChangedEvent EVENT_3 = new QuotaThresholdChangedEvent(
         EventId.first(),
         HistoryEvolution.lowerThresholdReached(new QuotaThresholdChange(_75, INSTANT)),
         HistoryEvolution.higherThresholdReached(new QuotaThresholdChange(_80, INSTANT),
@@ -71,7 +71,7 @@ class DTOTest {
         SIZE_QUOTA,
         Quota.<QuotaCount>builder().used(QuotaCount.count(12)).computedLimit(QuotaCount.unlimited()).build(),
         UserQuotaThresholds.Id.from(User.fromUsername("foo@bar.com")));
-    public static final QuotaThresholdChangedEvent EVENT_4 = new QuotaThresholdChangedEvent(
+    static final QuotaThresholdChangedEvent EVENT_4 = new QuotaThresholdChangedEvent(
         EventId.first(),
         HistoryEvolution.lowerThresholdReached(new QuotaThresholdChange(_75, INSTANT)),
         HistoryEvolution.higherThresholdReached(new QuotaThresholdChange(_80, INSTANT),
@@ -80,24 +80,24 @@ class DTOTest {
         Quota.<QuotaCount>builder().used(QuotaCount.count(12)).computedLimit(QuotaCount.unlimited()).build(),
         UserQuotaThresholds.Id.from(User.fromUsername("foo@bar.com")));
 
-    public static final String EVENT_JSON = ClassLoaderUtils.getSystemResourceAsString("json/event.json");
-    public static final String EVENT_JSON_2 = ClassLoaderUtils.getSystemResourceAsString("json/event2.json");
-    public static final String EVENT_JSON_3 = ClassLoaderUtils.getSystemResourceAsString("json/event3.json");
-    public static final String EVENT_JSON_4 = ClassLoaderUtils.getSystemResourceAsString("json/event4.json");
+    static final String EVENT_JSON = ClassLoaderUtils.getSystemResourceAsString("json/event.json");
+    static final String EVENT_JSON_2 = ClassLoaderUtils.getSystemResourceAsString("json/event2.json");
+    static final String EVENT_JSON_3 = ClassLoaderUtils.getSystemResourceAsString("json/event3.json");
+    static final String EVENT_JSON_4 = ClassLoaderUtils.getSystemResourceAsString("json/event4.json");
 
-    public static final String COUNT_QUOTA_JSON = "{" +
+    static final String COUNT_QUOTA_JSON = "{" +
         "   \"used\": 12," +
         "   \"limit\": 45" +
         " }";
 
-    public static final String NO_CHANGES_JSON = "{" +
+    static final String NO_CHANGES_JSON = "{" +
         "  \"change\":\"NoChange\"" +
         "}";
 
     private ObjectMapper objectMapper;
 
     @BeforeEach
-    public void setUp() {
+    void setUp() {
         objectMapper = new ObjectMapper();
         objectMapper.registerModule(new Jdk8Module());
         objectMapper.setSerializationInclusion(JsonInclude.Include.NON_ABSENT);

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/listeners/CassandraEventStoreExtension.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/listeners/CassandraEventStoreExtension.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/listeners/CassandraEventStoreExtension.java
new file mode 100644
index 0000000..f46a605
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/listeners/CassandraEventStoreExtension.java
@@ -0,0 +1,32 @@
+/****************************************************************
+ * 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.mailbox.quota.cassandra.listeners;
+
+import org.apache.james.eventsourcing.eventstore.cassandra.CassandraGenericEventStoreExtension;
+import org.apache.james.mailbox.quota.cassandra.dto.QuotaThresholdChangedEventDTOModule;
+
+import com.google.common.collect.ImmutableSet;
+
+public class CassandraEventStoreExtension extends CassandraGenericEventStoreExtension {
+    public CassandraEventStoreExtension() {
+        super(ImmutableSet.of(
+            new QuotaThresholdChangedEventDTOModule()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/listeners/CassandraQuotaMailingListenersIntegrationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/listeners/CassandraQuotaMailingListenersIntegrationTest.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/listeners/CassandraQuotaMailingListenersIntegrationTest.java
index 3ea097a..7835898 100644
--- a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/listeners/CassandraQuotaMailingListenersIntegrationTest.java
+++ b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/listeners/CassandraQuotaMailingListenersIntegrationTest.java
@@ -19,7 +19,6 @@
 
 package org.apache.james.mailbox.quota.cassandra.listeners;
 
-import org.apache.james.eventsourcing.cassandra.CassandraEventStoreExtension;
 import org.apache.james.mailbox.quota.mailing.listeners.QuotaThresholdMailingIntegrationTest;
 import org.junit.jupiter.api.extension.ExtendWith;
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-memory/pom.xml
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-memory/pom.xml b/mailbox/plugin/quota-mailing-memory/pom.xml
index ad3a70f..aa28165 100644
--- a/mailbox/plugin/quota-mailing-memory/pom.xml
+++ b/mailbox/plugin/quota-mailing-memory/pom.xml
@@ -36,6 +36,27 @@
     <dependencies>
         <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>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>event-sourcing-event-store-memory</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>event-sourcing-event-store-memory</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
             <artifactId>apache-james-mailbox-api</artifactId>
             <type>test-jar</type>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-memory/src/main/java/org/apache/james/eventsource/InMemoryEventStore.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-memory/src/main/java/org/apache/james/eventsource/InMemoryEventStore.java b/mailbox/plugin/quota-mailing-memory/src/main/java/org/apache/james/eventsource/InMemoryEventStore.java
deleted file mode 100644
index 11c8e46..0000000
--- a/mailbox/plugin/quota-mailing-memory/src/main/java/org/apache/james/eventsource/InMemoryEventStore.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/****************************************************************
- * 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.eventsource;
-
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.james.eventsourcing.AggregateId;
-import org.apache.james.eventsourcing.Event;
-import org.apache.james.eventsourcing.EventStore;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-
-public class InMemoryEventStore implements EventStore {
-
-    private final ConcurrentHashMap<AggregateId, History> store;
-
-    public InMemoryEventStore() {
-        this.store = new ConcurrentHashMap<>();
-    }
-
-    @Override
-    public void appendAll(List<Event> events) {
-        if (events.isEmpty()) {
-            return;
-        }
-        AggregateId aggregateId = getAggregateId(events);
-
-        if (!store.containsKey(aggregateId)) {
-            appendToEmptyHistory(aggregateId, events);
-        } else {
-            appendToExistingHistory(aggregateId, events);
-        }
-    }
-
-    private AggregateId getAggregateId(List<? extends Event> events) {
-        Preconditions.checkArgument(!events.isEmpty());
-        Preconditions.checkArgument(Event.belongsToSameAggregate(events));
-
-        return events.stream()
-            .map(Event::getAggregateId)
-            .findFirst()
-            .get();
-    }
-
-    private void appendToEmptyHistory(AggregateId aggregateId, List<Event> events) {
-        History newHistory = History.of(events);
-
-        History previousHistory = store.putIfAbsent(aggregateId, newHistory);
-        if (previousHistory != null) {
-            throw new EventStore.EventStoreFailedException();
-        }
-    }
-
-    private void appendToExistingHistory(AggregateId aggregateId, List<? extends Event> events) {
-        History currentHistory = store.get(aggregateId);
-        List<Event> updatedEvents = updatedEvents(currentHistory, events);
-        History updatedHistory = History.of(updatedEvents);
-
-        boolean isReplaced = store.replace(aggregateId, currentHistory, updatedHistory);
-        if (!isReplaced) {
-            throw new EventStore.EventStoreFailedException();
-        }
-    }
-
-    private List<Event> updatedEvents(History currentHistory, List<? extends Event> newEvents) {
-        return ImmutableList.<Event>builder()
-            .addAll(currentHistory.getEvents())
-            .addAll(newEvents)
-            .build();
-    }
-
-    @Override
-    public History getEventsOfAggregate(AggregateId aggregateId) {
-        return Optional.ofNullable(store.get(aggregateId))
-            .orElse(History.empty());
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/eventsourcing/InMemoryEventSourcingSystemTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/eventsourcing/InMemoryEventSourcingSystemTest.java b/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/eventsourcing/InMemoryEventSourcingSystemTest.java
deleted file mode 100644
index c2f9935..0000000
--- a/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/eventsourcing/InMemoryEventSourcingSystemTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/****************************************************************
- * 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 org.junit.jupiter.api.extension.ExtendWith;
-
-@ExtendWith(InMemoryEventStoreExtension.class)
-public class InMemoryEventSourcingSystemTest implements EventSourcingSystemTest {
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/eventsourcing/InMemoryEventStoreExtension.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/eventsourcing/InMemoryEventStoreExtension.java b/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/eventsourcing/InMemoryEventStoreExtension.java
deleted file mode 100644
index c4b345e..0000000
--- a/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/eventsourcing/InMemoryEventStoreExtension.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/****************************************************************
- * 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 org.apache.james.eventsource.InMemoryEventStore;
-import org.junit.jupiter.api.extension.ExtensionContext;
-import org.junit.jupiter.api.extension.ParameterContext;
-import org.junit.jupiter.api.extension.ParameterResolutionException;
-import org.junit.jupiter.api.extension.ParameterResolver;
-
-public class InMemoryEventStoreExtension implements ParameterResolver {
-
-    @Override
-    public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
-        return (parameterContext.getParameter().getType() == EventStore.class);
-    }
-
-    @Override
-    public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
-        return new InMemoryEventStore();
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/eventsourcing/InMemoryEventStoreTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/eventsourcing/InMemoryEventStoreTest.java b/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/eventsourcing/InMemoryEventStoreTest.java
deleted file mode 100644
index eddc2d0..0000000
--- a/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/eventsourcing/InMemoryEventStoreTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/****************************************************************
- * 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 org.junit.jupiter.api.extension.ExtendWith;
-
-@ExtendWith(InMemoryEventStoreExtension.class)
-public class InMemoryEventStoreTest implements EventStoreTest {
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/mailbox/quota/memory/listeners/InMemoryQuotaMailingListenersIntegrationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/mailbox/quota/memory/listeners/InMemoryQuotaMailingListenersIntegrationTest.java b/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/mailbox/quota/memory/listeners/InMemoryQuotaMailingListenersIntegrationTest.java
index 4b17682..1a1ecb2 100644
--- a/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/mailbox/quota/memory/listeners/InMemoryQuotaMailingListenersIntegrationTest.java
+++ b/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/mailbox/quota/memory/listeners/InMemoryQuotaMailingListenersIntegrationTest.java
@@ -19,7 +19,7 @@
 
 package org.apache.james.mailbox.quota.memory.listeners;
 
-import org.apache.james.eventsourcing.InMemoryEventStoreExtension;
+import org.apache.james.eventsourcing.eventstore.memory.InMemoryEventStoreExtension;
 import org.apache.james.mailbox.quota.mailing.listeners.QuotaThresholdMailingIntegrationTest;
 import org.junit.jupiter.api.extension.ExtendWith;
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/mailbox/quota/memory/listeners/InMemoryQuotaThresholdConfigurationChangesTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/mailbox/quota/memory/listeners/InMemoryQuotaThresholdConfigurationChangesTest.java b/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/mailbox/quota/memory/listeners/InMemoryQuotaThresholdConfigurationChangesTest.java
index ba65405..409e726 100644
--- a/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/mailbox/quota/memory/listeners/InMemoryQuotaThresholdConfigurationChangesTest.java
+++ b/mailbox/plugin/quota-mailing-memory/src/test/java/org/apache/james/mailbox/quota/memory/listeners/InMemoryQuotaThresholdConfigurationChangesTest.java
@@ -19,7 +19,7 @@
 
 package org.apache.james.mailbox.quota.memory.listeners;
 
-import org.apache.james.eventsourcing.InMemoryEventStoreExtension;
+import org.apache.james.eventsourcing.eventstore.memory.InMemoryEventStoreExtension;
 import org.apache.james.mailbox.quota.mailing.listeners.QuotaThresholdConfigurationChangesTest;
 import org.junit.jupiter.api.extension.ExtendWith;
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing/pom.xml
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/pom.xml b/mailbox/plugin/quota-mailing/pom.xml
index 8f45fb1..b10bd8e 100644
--- a/mailbox/plugin/quota-mailing/pom.xml
+++ b/mailbox/plugin/quota-mailing/pom.xml
@@ -36,6 +36,20 @@
     <dependencies>
         <dependency>
             <groupId>${project.groupId}</groupId>
+            <artifactId>event-sourcing-core</artifactId>
+        </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>apache-james-mailbox-api</artifactId>
         </dependency>
         <dependency>

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/AggregateId.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/AggregateId.java b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/AggregateId.java
deleted file mode 100644
index 18c6224..0000000
--- a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/AggregateId.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/****************************************************************
- * 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/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/CommandDispatcher.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/CommandDispatcher.java b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/CommandDispatcher.java
deleted file mode 100644
index de8b63f..0000000
--- a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/CommandDispatcher.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/****************************************************************
- * 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.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.function.Supplier;
-import java.util.stream.IntStream;
-
-import javax.inject.Inject;
-
-import com.github.steveash.guavate.Guavate;
-
-public class CommandDispatcher {
-
-    private static final int MAX_RETRY = 10;
-
-    public interface Command {
-    }
-
-    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;
-        }
-    }
-
-    public interface CommandHandler<C extends Command> {
-        Class<C> handledClass();
-
-        List<? extends Event> handle(C c);
-    }
-
-    private final EventBus eventBus;
-    @SuppressWarnings("rawtypes")
-    private final Map<Class, CommandHandler> handlers;
-
-    @Inject
-    public CommandDispatcher(EventBus eventBus, Collection<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 (EventStore.EventStoreFailedException e) {
-            return false;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/189490a4/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/Event.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/Event.java b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/Event.java
deleted file mode 100644
index 2b31374..0000000
--- a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/Event.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/****************************************************************
- * 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/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/EventBus.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/EventBus.java b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/EventBus.java
deleted file mode 100644
index 067d432..0000000
--- a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/EventBus.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/****************************************************************
- * 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.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 EventStore.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/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/EventId.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/EventId.java b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/EventId.java
deleted file mode 100644
index cb5bd1e..0000000
--- a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/EventId.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/****************************************************************
- * 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;
-    }
-}


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org