diff --git a/spring-boot/shared/pom.xml b/spring-boot/shared/pom.xml
index 1a06a25..4ac3c45 100644
--- a/spring-boot/shared/pom.xml
+++ b/spring-boot/shared/pom.xml
@@ -28,6 +28,12 @@
0.2.1-SNAPSHOT
+
+ org.fuin
+ cqrs-4-java
+ 0.2.1-SNAPSHOT
+
+
org.fuin
objects4j
diff --git a/spring-boot/shared/src/main/java/org/fuin/cqrs4j/example/spring/shared/CreatePersonCommand.java b/spring-boot/shared/src/main/java/org/fuin/cqrs4j/example/spring/shared/CreatePersonCommand.java
new file mode 100644
index 0000000..9bfb9b6
--- /dev/null
+++ b/spring-boot/shared/src/main/java/org/fuin/cqrs4j/example/spring/shared/CreatePersonCommand.java
@@ -0,0 +1,88 @@
+/**
+ * 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.spring.shared;
+
+import javax.annotation.concurrent.Immutable;
+import javax.json.bind.annotation.JsonbProperty;
+import javax.validation.constraints.NotNull;
+
+import org.fuin.cqrs4j.AbstractAggregateCommand;
+import org.fuin.ddd4j.ddd.DomainEventExpectedEntityIdPath;
+import org.fuin.ddd4j.ddd.EventType;
+import org.fuin.esc.spi.SerializedDataType;
+import org.fuin.objects4j.common.Contract;
+
+/**
+ * A new person should be created in the system.
+ */
+@Immutable
+@DomainEventExpectedEntityIdPath(PersonId.class)
+public final class CreatePersonCommand extends AbstractAggregateCommand {
+
+ private static final long serialVersionUID = 1000L;
+
+ /** Never changing unique event type name. */
+ public static final EventType TYPE = new EventType("CreatePersonCommand");
+
+ /** Unique name used for marshalling/unmarshalling the event. */
+ public static final SerializedDataType SER_TYPE = new SerializedDataType(CreatePersonCommand.TYPE.asBaseType());
+
+ @NotNull
+ @JsonbProperty("name")
+ private PersonName name;
+
+ /**
+ * Protected default constructor for deserialization.
+ */
+ protected CreatePersonCommand() {
+ super();
+ }
+
+ /**
+ * A new person was created in the system.
+ *
+ * @param id Identifies uniquely a person.
+ * @param name Name of a person.
+ */
+ public CreatePersonCommand(@NotNull final PersonId id, @NotNull final PersonName name) {
+ super(id, null);
+ Contract.requireArgNotNull("name", name);
+ this.name = name;
+ }
+
+ @Override
+ public final EventType getEventType() {
+ return CreatePersonCommand.TYPE;
+ }
+
+ /**
+ * Returns: Name of a person.
+ *
+ * @return Current value.
+ */
+ @NotNull
+ public final PersonName getName() {
+ return name;
+ }
+
+ @Override
+ public final String toString() {
+ return "Create person '" + name + "' with identifier '" + getAggregateRootId() + "'";
+ }
+
+}
\ No newline at end of file
diff --git a/spring-boot/shared/src/test/java/org/fuin/cqrs4j/example/spring/shared/CreatePersonCommandTest.java b/spring-boot/shared/src/test/java/org/fuin/cqrs4j/example/spring/shared/CreatePersonCommandTest.java
new file mode 100644
index 0000000..1d4ee4b
--- /dev/null
+++ b/spring-boot/shared/src/test/java/org/fuin/cqrs4j/example/spring/shared/CreatePersonCommandTest.java
@@ -0,0 +1,108 @@
+/**
+ * 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.spring.shared;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.time.ZonedDateTime;
+import java.util.UUID;
+
+import javax.json.bind.Jsonb;
+import javax.json.bind.JsonbBuilder;
+import javax.json.bind.JsonbConfig;
+
+import org.apache.commons.io.IOUtils;
+import org.eclipse.yasson.FieldAccessStrategy;
+import org.fuin.utils4j.Utils4J;
+import org.junit.Test;
+
+// CHECKSTYLE:OFF
+public final class CreatePersonCommandTest {
+
+ private static final String PERSON_UUID = "84565d62-115e-4502-b7c9-38ad69c64b05";
+
+
+ @Test
+ public final void testSerializeDeserialize() {
+
+ // PREPARE
+ final CreatePersonCommand original = createTestee();
+
+ // TEST
+ final CreatePersonCommand copy = Utils4J.deserialize(Utils4J.serialize(original));
+
+ // VERIFY
+ assertThat(copy).isEqualTo(original);
+ assertThat(copy.getAggregateRootId()).isEqualTo(original.getAggregateRootId());
+ assertThat(copy.getName()).isEqualTo(original.getName());
+
+ }
+
+ @Test
+ public final void testMarshalUnmarshalJson() {
+
+ // PREPARE
+ final CreatePersonCommand original = createTestee();
+
+ final JsonbConfig config = new JsonbConfig().withAdapters(SharedUtils.JSONB_ADAPTERS)
+ .withPropertyVisibilityStrategy(new FieldAccessStrategy());
+ final Jsonb jsonb = JsonbBuilder.create(config);
+
+ // TEST
+ final String json = jsonb.toJson(original, CreatePersonCommand.class);
+ final CreatePersonCommand copy = jsonb.fromJson(json, CreatePersonCommand.class);
+
+ // VERIFY
+ assertThat(copy).isEqualTo(original);
+ assertThat(copy.getAggregateRootId()).isEqualTo(original.getAggregateRootId());
+ assertThat(copy.getName()).isEqualTo(original.getName());
+
+ }
+
+ @Test
+ public final void testUnmarshalJsonFromFile() throws IOException {
+
+ // PREPARE
+ final String json = IOUtils.toString(this.getClass().getResourceAsStream("/commands/CreatePersonCommand.json"),
+ Charset.forName("utf-8"));
+ final JsonbConfig config = new JsonbConfig().withAdapters(SharedUtils.JSONB_ADAPTERS)
+ .withPropertyVisibilityStrategy(new FieldAccessStrategy());
+ final Jsonb jsonb = JsonbBuilder.create(config);
+
+
+ // TEST
+ final CreatePersonCommand copy = jsonb.fromJson(json, CreatePersonCommand.class);
+
+ // VERIFY
+ assertThat(copy.getEventId().asBaseType()).isEqualTo(UUID.fromString("109a77b2-1de2-46fc-aee1-97fa7740a552"));
+ assertThat(copy.getTimestamp()).isEqualTo(ZonedDateTime.parse("2019-11-17T10:27:13.183+01:00[Europe/Berlin]"));
+ assertThat(copy.getAggregateRootId().asString()).isEqualTo(PERSON_UUID);
+ assertThat(copy.getName().asString()).isEqualTo("Peter Parker");
+
+ }
+
+ private CreatePersonCommand createTestee() {
+ final PersonId personId = new PersonId(UUID.fromString(PERSON_UUID));
+ final PersonName personName = new PersonName("Peter Parker");
+ return new CreatePersonCommand(personId, personName);
+ }
+
+}
+// CHECKSTYLE:ON
diff --git a/spring-boot/shared/src/test/java/org/fuin/cqrs4j/example/spring/shared/PersonCreatedEventTest.java b/spring-boot/shared/src/test/java/org/fuin/cqrs4j/example/spring/shared/PersonCreatedEventTest.java
index fb683ca..6244547 100644
--- a/spring-boot/shared/src/test/java/org/fuin/cqrs4j/example/spring/shared/PersonCreatedEventTest.java
+++ b/spring-boot/shared/src/test/java/org/fuin/cqrs4j/example/spring/shared/PersonCreatedEventTest.java
@@ -1,10 +1,26 @@
+/**
+ * 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.spring.shared;
import static org.assertj.core.api.Assertions.assertThat;
import static org.fuin.utils4j.Utils4J.deserialize;
import static org.fuin.utils4j.Utils4J.serialize;
-import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.UUID;
diff --git a/spring-boot/shared/src/test/java/org/fuin/cqrs4j/example/spring/shared/PersonIdTest.java b/spring-boot/shared/src/test/java/org/fuin/cqrs4j/example/spring/shared/PersonIdTest.java
new file mode 100644
index 0000000..cd8c793
--- /dev/null
+++ b/spring-boot/shared/src/test/java/org/fuin/cqrs4j/example/spring/shared/PersonIdTest.java
@@ -0,0 +1,74 @@
+package org.fuin.cqrs4j.example.spring.shared;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+
+import java.util.UUID;
+
+import org.fuin.ddd4j.ddd.EntityType;
+import org.fuin.ddd4j.ddd.StringBasedEntityType;
+import org.fuin.objects4j.common.ConstraintViolationException;
+import org.junit.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import nl.jqno.equalsverifier.Warning;
+
+/**
+ * Test for {@link PersonId}.
+ */
+public final class PersonIdTest {
+
+ private static final String PERSON_UUID = "84565d62-115e-4502-b7c9-38ad69c64b05";
+
+ @Test
+ public void testEquals() {
+ EqualsVerifier.forClass(PersonId.class).suppress(Warning.NONFINAL_FIELDS)
+ .withNonnullFields("entityType", "uuid")
+ .withPrefabValues(EntityType.class, new StringBasedEntityType("A"), new StringBasedEntityType("B"))
+ .verify();
+ }
+
+ @Test
+ public void testValueOf() {
+ final PersonId personId = PersonId.valueOf(PERSON_UUID);
+
+ assertThat(personId.asString()).isEqualTo(PERSON_UUID);
+
+ }
+
+ @Test
+ public void testValueOfIllegalArgumentCharacter() {
+ try {
+ PersonId.valueOf("abc");
+ fail();
+ } catch (final ConstraintViolationException ex) {
+ assertThat(ex.getMessage()).isEqualTo("The argument 'value' is not valid: 'abc'");
+ }
+ }
+
+ @Test
+ public final void testConverterUnmarshal() throws Exception {
+
+ // PREPARE
+ final String personIdValue = PERSON_UUID;
+
+ // TEST
+ final PersonId personId = new PersonId.Converter().adaptFromJson(UUID.fromString(PERSON_UUID));
+
+ // VERIFY
+ assertThat(personId.asString()).isEqualTo(personIdValue);
+ }
+
+ @Test
+ public void testConverterMarshal() throws Exception {
+
+ final PersonId personId = PersonId.valueOf(PERSON_UUID);
+
+ // TEST
+ final UUID uuid = new PersonId.Converter().adaptToJson(personId);
+
+ // VERIFY
+ assertThat(uuid).isEqualTo(UUID.fromString(PERSON_UUID));
+ }
+
+}
diff --git a/spring-boot/shared/src/test/java/org/fuin/cqrs4j/example/spring/shared/PersonNameTest.java b/spring-boot/shared/src/test/java/org/fuin/cqrs4j/example/spring/shared/PersonNameTest.java
new file mode 100644
index 0000000..d9ea349
--- /dev/null
+++ b/spring-boot/shared/src/test/java/org/fuin/cqrs4j/example/spring/shared/PersonNameTest.java
@@ -0,0 +1,134 @@
+/**
+ * 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.spring.shared;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.fuin.objects4j.common.ConstraintViolationException;
+import org.fuin.utils4j.Utils4J;
+import org.junit.Assert;
+import org.junit.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import nl.jqno.equalsverifier.Warning;
+
+// CHECKSTYLE:OFF
+public final class PersonNameTest {
+
+ @Test
+ public void testSerialize() {
+ final PersonName original = new PersonName("Peter Parker");
+ final PersonName copy = Utils4J.deserialize(Utils4J.serialize(original));
+ assertThat(original).isEqualTo(copy);
+ }
+
+ @Test
+ public void testHashCodeEquals() {
+ EqualsVerifier.forClass(PersonName.class).suppress(Warning.NULL_FIELDS).withRedefinedSuperclass().verify();
+ }
+
+ @Test
+ public void testMarshalJson() throws Exception {
+
+ // PREPARE
+ final String str = "Peter Parker";
+ final PersonName testee = new PersonName(str);
+
+ // TEST & VERIFY
+ assertThat(new PersonName.Converter().adaptToJson(testee)).isEqualTo(str);
+ assertThat(new PersonName.Converter().adaptToJson(null)).isNull();
+
+ }
+
+ @Test
+ public void testUnmarshalJson() throws Exception {
+
+ // PREPARE
+ final String str = "Peter Parker";
+ final PersonName testee = new PersonName(str);
+
+ // TEST & VERIFY
+ assertThat(new PersonName.Converter().adaptFromJson(str)).isEqualTo(testee);
+ assertThat(new PersonName.Converter().adaptFromJson(null)).isNull();
+
+ }
+
+ @Test
+ public void testIsValid() {
+
+ assertThat(PersonName.isValid(null)).isTrue();
+ assertThat(PersonName.isValid("Peter Parker")).isTrue();
+
+ assertThat(PersonName.isValid("")).isFalse();
+ assertThat(PersonName.isValid("123456789.123456789.123456789.123456789.123456789."
+ + "123456789.123456789.123456789.123456789.123456789." + "12345")).isFalse();
+
+ }
+
+ @Test
+ public void testRequireArgValid() {
+
+ PersonName.requireArgValid("a", "Peter Parker");
+ PersonName.requireArgValid("b", null);
+
+ try {
+ PersonName.requireArgValid("c", "");
+ Assert.fail();
+ } catch (final ConstraintViolationException ex) {
+ assertThat(ex.getMessage()).isEqualTo("The argument 'c' is not valid: ''");
+ }
+
+ try {
+ PersonName.requireArgValid("d", "123456789.123456789.123456789.123456789.123456789."
+ + "123456789.123456789.123456789.123456789.123456789." + "12345");
+ Assert.fail();
+ } catch (final ConstraintViolationException ex) {
+ assertThat(ex.getMessage())
+ .isEqualTo("The argument 'd' is not valid: '" + "123456789.123456789.123456789.123456789.123456789."
+ + "123456789.123456789.123456789.123456789.123456789." + "12345" + "'");
+ }
+
+ }
+
+ @Test
+ public void testValidator() {
+
+ assertThat(new PersonName.Validator().isValid(null, null)).isTrue();
+ assertThat(new PersonName.Validator().isValid("Peter Parker", null)).isTrue();
+
+ assertThat(new PersonName.Validator().isValid("", null)).isFalse();
+ assertThat(new PersonName.Validator().isValid("123456789.123456789.123456789.123456789.123456789."
+ + "123456789.123456789.123456789.123456789.123456789." + "12345", null)).isFalse();
+
+ }
+
+ @Test
+ public void testValueObjectConverter() {
+
+ assertThat(new PersonName.Converter().getBaseTypeClass()).isEqualTo(String.class);
+ assertThat(new PersonName.Converter().getValueObjectClass()).isEqualTo(PersonName.class);
+ assertThat(new PersonName.Converter().isValid(null)).isTrue();
+ assertThat(new PersonName.Converter().isValid("Peter Parker")).isTrue();
+
+ assertThat(new PersonName.Converter().isValid("123456789.123456789.123456789.123456789.123456789."
+ + "123456789.123456789.123456789.123456789.123456789." + "12345")).isFalse();
+
+ }
+
+}
diff --git a/spring-boot/shared/src/test/resources/commands/CreatePersonCommand.json b/spring-boot/shared/src/test/resources/commands/CreatePersonCommand.json
new file mode 100644
index 0000000..92ceb71
--- /dev/null
+++ b/spring-boot/shared/src/test/resources/commands/CreatePersonCommand.json
@@ -0,0 +1,6 @@
+{
+ "event-id": "109a77b2-1de2-46fc-aee1-97fa7740a552",
+ "event-timestamp": "2019-11-17T10:27:13.183+01:00[Europe/Berlin]",
+ "entity-id-path": "PERSON 84565d62-115e-4502-b7c9-38ad69c64b05",
+ "name": "Peter Parker"
+}
\ No newline at end of file