diff --git a/aggregates/README.md b/aggregates/README.md
new file mode 100644
index 0000000..bd13c87
--- /dev/null
+++ b/aggregates/README.md
@@ -0,0 +1,3 @@
+# cqrs4j-example-aggregates
+DDD related code for all demo applications (aggregates, entities and business exceptions).
+
diff --git a/aggregates/pom.xml b/aggregates/pom.xml
new file mode 100644
index 0000000..ce6b96a
--- /dev/null
+++ b/aggregates/pom.xml
@@ -0,0 +1,141 @@
+
+
+ 4.0.0
+
+ org.fuin.cqrs4j.example
+ cqrs4j-example-aggregates
+ 0.1.0-SNAPSHOT
+ cqrs4j-example-aggregates
+ DDD related code for all demo applications (aggregates, entities and business exceptions)
+
+
+ UTF-8
+ 1.8
+ 1.8
+ 1.8
+ 0.3.1-SNAPSHOT
+
+
+
+
+
+
+
+ org.fuin.cqrs4j.example
+ cqrs4j-example-shared
+ 0.1.0-SNAPSHOT
+
+
+
+ org.fuin
+ ddd-4-java
+ 0.2.1
+
+
+
+ org.fuin
+ cqrs-4-java
+ 0.2.1
+
+
+
+ org.fuin
+ objects4j
+ 0.6.9
+
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.25
+
+
+
+ org.glassfish
+ javax.json
+ 1.1.4
+
+
+
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+
+ org.assertj
+ assertj-core
+ 3.10.0
+ test
+
+
+
+ org.fuin
+ units4j
+ 0.8.4
+ test
+
+
+
+ nl.jqno.equalsverifier
+ equalsverifier
+ 2.4.6
+ test
+
+
+
+ commons-io
+ commons-io
+ 2.6
+ test
+
+
+
+ org.eclipse
+ yasson
+ 1.0.3
+ test
+
+
+
+ org.hibernate.validator
+ hibernate-validator
+ 6.0.10.Final
+ test
+
+
+ jaxb-impl
+ com.sun.xml.bind
+
+
+ jaxb-api
+ javax.xml.bind
+
+
+
+
+
+
+
+
+
+ sonatype.oss.snapshots
+ Sonatype OSS Snapshot Repository
+ https://oss.sonatype.org/content/repositories/snapshots
+
+ false
+
+
+ always
+ true
+
+
+
+
+
+
diff --git a/aggregates/src/main/java/org/fuin/cqrs4j/example/aggregates/DuplicatePersonNameException.java b/aggregates/src/main/java/org/fuin/cqrs4j/example/aggregates/DuplicatePersonNameException.java
new file mode 100644
index 0000000..753eea0
--- /dev/null
+++ b/aggregates/src/main/java/org/fuin/cqrs4j/example/aggregates/DuplicatePersonNameException.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved. http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with this library. If not, see
+ * http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.example.aggregates;
+
+import javax.validation.constraints.NotNull;
+
+import org.fuin.cqrs4j.example.shared.PersonId;
+import org.fuin.cqrs4j.example.shared.PersonName;
+
+
+/**
+ * A name that should be unique does already exist.
+ */
+public final class DuplicatePersonNameException extends Exception {
+
+ private static final long serialVersionUID = 1000L;
+
+ private PersonId personId;
+
+ private PersonName name;
+
+ /**
+ * Constructor with mandatory data.
+ *
+ * @param personId
+ * Identifier of the resource that caused the problem.
+ * @param name
+ * Name of the resource that caused the problem.
+ */
+ public DuplicatePersonNameException(@NotNull final PersonId personId, @NotNull final PersonName name) {
+ super("The name '" + name + "' already exists: " + personId.asString());
+ this.personId = personId;
+ this.name = name;
+ }
+
+ /**
+ * Returns the identifier of the entity that has the name.
+ *
+ * @return Identifier.
+ */
+ public final PersonId getPersonId() {
+ return personId;
+ }
+
+ /**
+ * Returns the name that already exists.
+ *
+ * @return Name.
+ */
+ public final PersonName getName() {
+ return name;
+ }
+
+}
\ No newline at end of file
diff --git a/aggregates/src/main/java/org/fuin/cqrs4j/example/aggregates/Person.java b/aggregates/src/main/java/org/fuin/cqrs4j/example/aggregates/Person.java
new file mode 100644
index 0000000..e896997
--- /dev/null
+++ b/aggregates/src/main/java/org/fuin/cqrs4j/example/aggregates/Person.java
@@ -0,0 +1,111 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved. http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with this library. If not, see
+ * http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.example.aggregates;
+
+import java.io.Serializable;
+import java.util.Optional;
+
+import javax.validation.constraints.NotNull;
+
+import org.fuin.cqrs4j.example.shared.PersonCreatedEvent;
+import org.fuin.cqrs4j.example.shared.PersonId;
+import org.fuin.cqrs4j.example.shared.PersonName;
+import org.fuin.ddd4j.ddd.AbstractAggregateRoot;
+import org.fuin.ddd4j.ddd.ApplyEvent;
+import org.fuin.ddd4j.ddd.EntityType;
+import org.fuin.objects4j.common.Contract;
+
+/**
+ * Represents a natural person.
+ */
+public class Person extends AbstractAggregateRoot implements Serializable {
+
+ private static final long serialVersionUID = 1000L;
+
+ @NotNull
+ private PersonId id;
+
+ /**
+ * Default constructor that is mandatory for aggregate roots.
+ */
+ public Person() {
+ super();
+ }
+
+ /**
+ * Constructor with all data.
+ *
+ * @param id
+ * Unique identifier of the person.
+ * @param name
+ * Unique name of the person.
+ * @param service
+ * Service required by the method.
+ *
+ * @throws DuplicatePersonNameException
+ * The name already exists for another person.
+ */
+ public Person(@NotNull final PersonId id, @NotNull final PersonName name, final CreatePersonService service)
+ throws DuplicatePersonNameException {
+ super();
+
+ // VERIFY PRECONDITIONS
+ Contract.requireArgNotNull("id", id);
+ Contract.requireArgNotNull("name", name);
+
+ // VERIFY BUSINESS RULES
+
+ // Rule 1: The name of the person must be unique
+ final Optional otherId = service.loadPersonIdByName(name);
+ if (otherId.isPresent()) {
+ throw new DuplicatePersonNameException(otherId.get(), name);
+ }
+
+ // CREATE EVENT
+ apply(new PersonCreatedEvent(id, name));
+
+ }
+
+ @Override
+ public PersonId getId() {
+ return id;
+ }
+
+ @Override
+ public EntityType getType() {
+ return PersonId.TYPE;
+ }
+
+ @ApplyEvent
+ public void applyEvent(final PersonCreatedEvent event) {
+ this.id = event.getEntityId();
+ }
+
+ /**
+ * Service for the constructor.
+ */
+ public static interface CreatePersonService {
+
+ /**
+ * Loads the person's identifier for a given name.
+ *
+ * @param name
+ * Person's name.
+ *
+ * @return Office identifier or empty if not found.
+ */
+ public Optional loadPersonIdByName(@NotNull PersonName name);
+
+ }
+
+}
diff --git a/aggregates/src/main/java/org/fuin/cqrs4j/example/aggregates/PersonRepository.java b/aggregates/src/main/java/org/fuin/cqrs4j/example/aggregates/PersonRepository.java
new file mode 100644
index 0000000..ed6dad6
--- /dev/null
+++ b/aggregates/src/main/java/org/fuin/cqrs4j/example/aggregates/PersonRepository.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved. http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with this library. If not, see
+ * http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.example.aggregates;
+
+import javax.annotation.concurrent.NotThreadSafe;
+import javax.validation.constraints.NotNull;
+
+import org.fuin.cqrs4j.example.shared.PersonId;
+import org.fuin.ddd4j.ddd.EntityType;
+import org.fuin.ddd4j.esrepo.EventStoreRepository;
+import org.fuin.esc.api.EventStore;
+
+/**
+ * Event sourced repository for storing a {@link Person} aggregate.
+ */
+@NotThreadSafe
+public class PersonRepository extends EventStoreRepository {
+
+ /**
+ * Constructor all mandatory data.
+ *
+ * @param eventStore
+ * Event store.
+ */
+ public PersonRepository(final EventStore eventStore) {
+ super(eventStore);
+ }
+
+ @Override
+ @NotNull
+ public Class getAggregateClass() {
+ return Person.class;
+ }
+
+ @Override
+ @NotNull
+ public EntityType getAggregateType() {
+ return PersonId.TYPE;
+ }
+
+ @Override
+ @NotNull
+ public Person create() {
+ return new Person();
+ }
+
+ @Override
+ @NotNull
+ public String getIdParamName() {
+ return "personId";
+ }
+
+}
\ No newline at end of file
diff --git a/aggregates/src/main/java/org/fuin/cqrs4j/example/aggregates/package-info.java b/aggregates/src/main/java/org/fuin/cqrs4j/example/aggregates/package-info.java
new file mode 100644
index 0000000..f486708
--- /dev/null
+++ b/aggregates/src/main/java/org/fuin/cqrs4j/example/aggregates/package-info.java
@@ -0,0 +1,17 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved. http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with this library. If not, see
+ * http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.example.aggregates;
+
+/**
+ * Domain specific code like aggregate root classes and their repositories.
+ */
diff --git a/aggregates/src/main/resources/.gitkeep b/aggregates/src/main/resources/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/aggregates/src/test/java/org/fuin/cqrs4j/example/aggregates/PersonTest.java b/aggregates/src/test/java/org/fuin/cqrs4j/example/aggregates/PersonTest.java
new file mode 100644
index 0000000..a3752f5
--- /dev/null
+++ b/aggregates/src/test/java/org/fuin/cqrs4j/example/aggregates/PersonTest.java
@@ -0,0 +1,57 @@
+package org.fuin.cqrs4j.example.aggregates;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+
+import java.util.Optional;
+import java.util.UUID;
+
+import org.fuin.cqrs4j.example.shared.PersonCreatedEvent;
+import org.fuin.cqrs4j.example.shared.PersonId;
+import org.fuin.cqrs4j.example.shared.PersonName;
+import org.junit.Test;
+
+/**
+ * Test for the {@link Person} class.
+ */
+public class PersonTest {
+
+ @Test
+ public final void testCreateOK() throws DuplicatePersonNameException {
+
+ // PREPARE
+ final PersonId personId = new PersonId(UUID.fromString("f645969a-402d-41a9-882b-d2d8000d0f43"));
+ final PersonName personName = new PersonName("Peter Parker");
+
+ // TEST
+ final Person testee = new Person(personId, personName, pid -> { return Optional.empty(); }) ;
+
+ // VERIFY
+ assertThat(testee.getUncommittedChanges()).hasSize(1);
+ assertThat(testee.getUncommittedChanges().get(0)).isInstanceOf(PersonCreatedEvent.class);
+ final PersonCreatedEvent event = (PersonCreatedEvent) testee.getUncommittedChanges().get(0);
+ assertThat(event.getEntityId()).isEqualTo(personId);
+ assertThat(event.getName()).isEqualTo(personName);
+
+ }
+
+
+ @Test
+ public final void testCreateDuplicateName() {
+
+ // PREPARE
+ final PersonId personId = new PersonId(UUID.randomUUID());
+ final PersonName personName = new PersonName("Peter Parker");
+ final PersonId otherId = new PersonId(UUID.randomUUID());
+
+ // TEST & VERIFY
+ try {
+ new Person(personId, personName, pid -> { return Optional.of(otherId); }) ;
+ fail("Excpected duplicate name exception");
+ } catch (final DuplicatePersonNameException ex) {
+ assertThat(ex.getMessage()).isEqualTo("The name 'Peter Parker' already exists: " + otherId);
+ }
+
+ }
+
+}
diff --git a/aggregates/src/test/resources/.gitkeep b/aggregates/src/test/resources/.gitkeep
new file mode 100644
index 0000000..e69de29