From 6b0a9d687104e50ef70fda5e37cb011e15154bdc Mon Sep 17 00:00:00 2001 From: Jocelyn Ntakpe Date: Fri, 6 May 2022 15:35:57 +0200 Subject: [PATCH] GH-51 - Introduce Kotlin flavor of jMolecules DDD. The type-based flavor of the DDD building blocks uses methods that follow the Java Beans naming convention. Unfortunately, Kotlin currently does not implement these methods if properties are declared that match the convention [0]. This commit adds a kmolecules-ddd module that completely defines the same abstractions as jmolecules-ddd in Kotlin so that they can be properly used from Kotlin as well. The types are located in the org.jmolecules.ddd package so that we can phase out this module as soon as that glitch in Kotlin is fixed and users can just drop this JAR here in favor of the jMolecules DDD one. [0] https://youtrack.jetbrains.com/issue/KT-6653 --- kmolecules-ddd/pom.xml | 61 +++++++++++++++++++ .../ddd/annotation/AggregateRoot.kt | 22 +++++++ .../org/jmolecules/ddd/annotation/Entity.kt | 18 ++++++ .../org/jmolecules/ddd/annotation/Factory.kt | 17 ++++++ .../jmolecules/ddd/annotation/Repository.kt | 21 +++++++ .../org/jmolecules/ddd/annotation/Service.kt | 17 ++++++ .../jmolecules/ddd/annotation/ValueObject.kt | 16 +++++ .../org/jmolecules/ddd/types/AggregateRoot.kt | 20 ++++++ .../org/jmolecules/ddd/types/Association.kt | 55 +++++++++++++++++ .../kotlin/org/jmolecules/ddd/types/Entity.kt | 17 ++++++ .../org/jmolecules/ddd/types/Identifiable.kt | 17 ++++++ .../org/jmolecules/ddd/types/Identifier.kt | 13 ++++ .../org/jmolecules/ddd/types/Repository.kt | 19 ++++++ .../jmolecules/ddd/types/SimpleAssociation.kt | 34 +++++++++++ .../jmolecules/ddd/types/AssociationTest.kt | 56 +++++++++++++++++ pom.xml | 2 + 16 files changed, 405 insertions(+) create mode 100644 kmolecules-ddd/pom.xml create mode 100644 kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/AggregateRoot.kt create mode 100644 kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/Entity.kt create mode 100644 kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/Factory.kt create mode 100644 kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/Repository.kt create mode 100644 kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/Service.kt create mode 100644 kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/ValueObject.kt create mode 100644 kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/AggregateRoot.kt create mode 100644 kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Association.kt create mode 100644 kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Entity.kt create mode 100644 kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Identifiable.kt create mode 100644 kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Identifier.kt create mode 100644 kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Repository.kt create mode 100644 kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/SimpleAssociation.kt create mode 100644 kmolecules-ddd/src/test/kotlin/org/jmolecules/ddd/types/AssociationTest.kt diff --git a/kmolecules-ddd/pom.xml b/kmolecules-ddd/pom.xml new file mode 100644 index 0000000..f422939 --- /dev/null +++ b/kmolecules-ddd/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + + org.jmolecules + jmolecules + 1.5.0-SNAPSHOT + + + kmolecules-ddd + + kMolecules - DDD + Kotlin flavor of jMolecules DDD + + + org.kmolecules.ddd + + + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + + compile + + compile + + + + + test-compile + + test-compile + + + + + + + + + diff --git a/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/AggregateRoot.kt b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/AggregateRoot.kt new file mode 100644 index 0000000..724182e --- /dev/null +++ b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/AggregateRoot.kt @@ -0,0 +1,22 @@ +package org.jmolecules.ddd.annotation + +/** + * Identifies an aggregate root, i.e. the root entity of an aggregate. An aggregate forms a cluster of consistent rules + * usually formed around a set of entities by defining invariants based on the properties of the aggregate that have to + * be met before and after operations on it. Aggregates usually refer to other aggregates by their identifier. + * References to aggregate internals should be avoided and at least not considered strongly consistent (i.e. a reference + * held could possibly have been gone or become invalid at any point in time). They also act as scope of consistency, + * i.e. changes on a single aggregate are expected to be strongly consistent while changes across multiple ones should + * only expect eventual consistency. + * + * Kotlin's counterpart of the Java's [AggregateRoot](https://github.com/xmolecules/jmolecules/blob/main/jmolecules-ddd/src/main/java/org/jmolecules/ddd/annotation/AggregateRoot.java) + * + * @author Jocelyn Ntakpe + * + * See also : [Domain-Driven Design Reference (Evans) - Aggregates](https://domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf) + */ +@Entity +@Retention +@MustBeDocumented +@Target(AnnotationTarget.CLASS) +annotation class AggregateRoot diff --git a/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/Entity.kt b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/Entity.kt new file mode 100644 index 0000000..d2dbd89 --- /dev/null +++ b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/Entity.kt @@ -0,0 +1,18 @@ +package org.jmolecules.ddd.annotation + +/** + * Identifies an [Entity]. Entities represent a thread of continuity and identity, going through a lifecycle, + * though their attributes may change. Means of identification may come from the outside, or it may be an arbitrary + * identifier created by and for the system, but it must correspond to the identity distinctions in the model. The model + * must define what it means to be the same thing. + * + * Kotlin's counterpart of the Java's [Entity](https://github.com/xmolecules/jmolecules/blob/main/jmolecules-ddd/src/main/java/org/jmolecules/ddd/annotation/Entity.java) + * + * @author Jocelyn Ntakpe + * + * See also : [Domain-Driven Design Reference (Evans) - Entities](https://domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf) + */ +@Retention +@MustBeDocumented +@Target(AnnotationTarget.CLASS) +annotation class Entity diff --git a/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/Factory.kt b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/Factory.kt new file mode 100644 index 0000000..c9c1274 --- /dev/null +++ b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/Factory.kt @@ -0,0 +1,17 @@ +package org.jmolecules.ddd.annotation + +/** + * Identifies a [Factory]. Factories encapsulate the responsibility of creating complex objects in general and + * Aggregates in particular. Objects returned by the factory methods are guaranteed to be in valid state. + * + * Kotlin's counterpart of the Java's [Factory](https://github.com/xmolecules/jmolecules/blob/main/jmolecules-ddd/src/main/java/org/jmolecules/ddd/annotation/Factory.java) + * + * @author Jocelyn Ntakpe + * + * See also : [AggregateRoot] + * See also : [Domain-Driven Design Reference (Evans) - Factories](https://domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf) + */ +@Retention +@MustBeDocumented +@Target(AnnotationTarget.CLASS) +annotation class Factory diff --git a/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/Repository.kt b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/Repository.kt new file mode 100644 index 0000000..3bea7a6 --- /dev/null +++ b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/Repository.kt @@ -0,0 +1,21 @@ +package org.jmolecules.ddd.annotation + +/** + * Identifies a [Repository]. Repositories simulate a collection of aggregates to which aggregate instances can be + * added and removed. They usually also expose API to select a subset of aggregates matching certain criteria. Access to + * projections of an aggregate might be provided as well but also via a dedicated separate abstraction. + * + * Implementations use a dedicated persistence mechanism appropriate to the data structure and query requirements at + * hand. However, they should make sure that no persistence mechanism specific APIs leak into client code. + * + * Kotlin's counterpart of the Java's [Repository](https://github.com/xmolecules/jmolecules/blob/main/jmolecules-ddd/src/main/java/org/jmolecules/ddd/annotation/Repository.java) + * + * @author Jocelyn Ntakpe + * + * See also : [AggregateRoot] + * See also : [Domain-Driven Design Reference (Evans) - Repositories](https://domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf) + */ +@Retention +@MustBeDocumented +@Target(AnnotationTarget.CLASS) +annotation class Repository diff --git a/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/Service.kt b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/Service.kt new file mode 100644 index 0000000..f69e590 --- /dev/null +++ b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/Service.kt @@ -0,0 +1,17 @@ +package org.jmolecules.ddd.annotation + +/** + * Identifies a domain [Service]. A service is a significant process or transformation in the domain that is not a + * natural responsibility of an entity or value object, add an operation to the model as a standalone interface declared + * as a service. Define a service contract, a set of assertions about interactions with the service. (See assertions.) + * State these assertions in the ubiquitous language of a specific bounded context. Give the service a name, which also + * becomes part of the ubiquitous language. + * + * Kotlin's counterpart of the Java's [Service](https://github.com/xmolecules/jmolecules/blob/main/jmolecules-ddd/src/main/java/org/jmolecules/ddd/annotation/Service.java) + * + * @author Jocelyn Ntakpe + */ +@Retention +@MustBeDocumented +@Target(AnnotationTarget.CLASS) +annotation class Service diff --git a/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/ValueObject.kt b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/ValueObject.kt new file mode 100644 index 0000000..b6f1840 --- /dev/null +++ b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/annotation/ValueObject.kt @@ -0,0 +1,16 @@ +package org.jmolecules.ddd.annotation + +/** + * Identifies a value object. Domain concepts that are modeled as value objects have no conceptual identity or + * lifecycle. Implementations should be immutable, operations on it are side effect free. + * + * Kotlin's counterpart of the Java's [ValueObject](https://github.com/xmolecules/jmolecules/blob/main/jmolecules-ddd/src/main/java/org/jmolecules/ddd/annotation/ValueObject.java) + * + * @author Jocelyn Ntakpe + * + * See also : [Domain-Driven Design Reference (Evans) - Value objects](https://domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf) + */ +@Retention +@MustBeDocumented +@Target(AnnotationTarget.CLASS) +annotation class ValueObject diff --git a/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/AggregateRoot.kt b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/AggregateRoot.kt new file mode 100644 index 0000000..127c9b4 --- /dev/null +++ b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/AggregateRoot.kt @@ -0,0 +1,20 @@ +package org.jmolecules.ddd.types + +/** + * Identifies an aggregate root, i.e. the root entity of an aggregate. An aggregate forms a cluster of consistent rules + * usually formed around a set of entities by defining invariants based on the properties of the aggregate that have to + * be met before and after operations on it. Aggregates usually refer to other aggregates by their identifier. + * References to aggregate internals should be avoided and at least not considered strongly consistent (i.e. a reference + * held could possibly have been gone or become invalid at any point in time). They also act as scope of consistency, + * i.e. changes on a single aggregate are expected to be strongly consistent while changes across multiple ones should + * only expect eventual consistency. + * + * Kotlin's counterpart of Java's [AggregateRoot](https://github.com/xmolecules/jmolecules/blob/main/jmolecules-ddd/src/main/java/org/jmolecules/ddd/types/AggregateRoot.java) + * + * @author Jocelyn Ntakpe + * @since 1.0 + * + * See also: [Domain-Driven Design Reference (Evans) - Aggregates](https://domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf) + * See also: [John Sullivan - Advancing Enterprise DDD - Reinstating the Aggregate](https://scabl.blogspot.com/2015/04/aeddd-9.html) + */ +interface AggregateRoot, ID : Identifier> : Entity diff --git a/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Association.kt b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Association.kt new file mode 100644 index 0000000..ab1b171 --- /dev/null +++ b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Association.kt @@ -0,0 +1,55 @@ +package org.jmolecules.ddd.types + +/** + * An association to an [AggregateRoot]. + * + * Kotlin's counterpart of Java's [Association](https://github.com/xmolecules/jmolecules/blob/main/jmolecules-ddd/src/main/java/org/jmolecules/ddd/types/Association.java) + * + * @author Jocelyn Ntakpe + * @since 1.0 + * See also: [John Sullivan - Advancing Enterprise DDD - Reinstating the Aggregate](https://scabl.blogspot.com/2015/04/aeddd-9.html) + */ +interface Association, ID : Identifier> : Identifiable { + + companion object { + + /** + * Creates an [Association] pointing to the [Identifier] of the given [AggregateRoot]. + * + * @param T the concrete [AggregateRoot] type. + * @param ID the concrete [Identifier] type. + * @param aggregate used to create the association + * @return an [Association] pointing to the [Identifier] of the given [AggregateRoot]. + * @since 1.3 + */ + @JvmStatic + fun , ID : Identifier> forAggregate(aggregate: T): Association { + return SimpleAssociation { aggregate.id } + } + + /** + * Creates an [Association] pointing to the given [Identifier]. + * + * @param T the concrete [AggregateRoot] type. + * @param ID the concrete [Identifier] type. + * @param identifier used to create the association + * @return an [Association] pointing to the given [Identifier]. + * @since 1.3 + */ + @JvmStatic + fun , ID : Identifier> forId(identifier: ID): Association { + return SimpleAssociation { identifier } + } + } + + /** + * Returns whether the current [Association] points to the same [AggregateRoot] as the given one. Unlike + * [equals] and [hashCode] that also check for type equality of the [Association] + * itself, this only compares the target [Identifier] instances. + * + * @param other association compared with the current instance + * @return whether the current [Association] points to the same [AggregateRoot] as the given one. + * @since 1.3 + */ + fun pointsToSameAggregateAs(other: Association<*, ID>): Boolean = id == other.id +} diff --git a/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Entity.kt b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Entity.kt new file mode 100644 index 0000000..fd8056a --- /dev/null +++ b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Entity.kt @@ -0,0 +1,17 @@ +package org.jmolecules.ddd.types + +/** + * Identifies an [Entity]. Entities represent a thread of continuity and identity, going through a lifecycle, + * though their attributes may change. Means of identification may come from the outside, or it may be an arbitrary + * identifier created by and for the system, but it must correspond to the identity distinctions in the model. The model + * must define what it means to be the same thing. + * + * Kotlin's counterpart of Java's [Entity](https://github.com/xmolecules/jmolecules/blob/main/jmolecules-ddd/src/main/java/org/jmolecules/ddd/types/Entity.java) + * + * @author Jocelyn Ntakpe + * @since 1.0 + * + * See also: [Domain-Driven Design Reference (Evans) - Entities](https://domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf) + * See also: [John Sullivan - Advancing Enterprise DDD - Reinstating the Aggregate](https://scabl.blogspot.com/2015/04/aeddd-9.html) + */ +interface Entity, ID> : Identifiable diff --git a/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Identifiable.kt b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Identifiable.kt new file mode 100644 index 0000000..28392bc --- /dev/null +++ b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Identifiable.kt @@ -0,0 +1,17 @@ +package org.jmolecules.ddd.types + +/** + * An identifiable type, i.e. anything that exposes an [Identifier]. + * + * Kotlin's counterpart of Java's [Identifiable](https://github.com/xmolecules/jmolecules/blob/main/jmolecules-ddd/src/main/java/org/jmolecules/ddd/types/Identifiable.java) + * + * @author Jocelyn Ntakpe + * @since 1.0 + */ +interface Identifiable { + + /** + * Identifier + */ + val id: ID +} diff --git a/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Identifier.kt b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Identifier.kt new file mode 100644 index 0000000..5ede48e --- /dev/null +++ b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Identifier.kt @@ -0,0 +1,13 @@ +package org.jmolecules.ddd.types + +/** + * Marker interface for identifiers. Exists primarily to easily identify types that are supposed to be identifiers + * within the code base and let the compiler verify the correctness of declared relationships. + * + * Kotlin's counterpart of Java's [Identifier](https://github.com/xmolecules/jmolecules/blob/main/jmolecules-ddd/src/main/java/org/jmolecules/ddd/types/Identifier.java) + * + * @author Jocelyn Ntakpe + * @since 1.0 + * @see Identifiable + */ +interface Identifier diff --git a/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Repository.kt b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Repository.kt new file mode 100644 index 0000000..53d5570 --- /dev/null +++ b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/Repository.kt @@ -0,0 +1,19 @@ +package org.jmolecules.ddd.types + +/** + * Identifies a [Repository]. Repositories simulate a collection of aggregates to which aggregate instances can be + * added and removed. They usually also expose API to select a subset of aggregates matching certain criteria. Access to + * projections of an aggregate might be provided as well but also via a dedicated separate abstraction. + * + * Implementations use a dedicated persistence mechanism appropriate to the data structure and query requirements at + * hand. However, they should make sure that no persistence mechanism specific APIs leak into client code. + * + * Kotlin's counterpart of Java's [Repository](https://github.com/xmolecules/jmolecules/blob/main/jmolecules-ddd/src/main/java/org/jmolecules/ddd/types/Repository.java) + * + * @author Jocelyn Ntakpe + * @since 1.0 + * @see [AggregateRoot] + * + * See also: [Domain-Driven Design Reference (Evans) - Repositories](https://domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf) + */ +interface Repository, ID : Identifier> diff --git a/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/SimpleAssociation.kt b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/SimpleAssociation.kt new file mode 100644 index 0000000..2ef48df --- /dev/null +++ b/kmolecules-ddd/src/main/kotlin/org/jmolecules/ddd/types/SimpleAssociation.kt @@ -0,0 +1,34 @@ +package org.jmolecules.ddd.types + +/** + * Simple implementation of [Association] to effectively only define [equals] and + * [hashCode] on [Association]'s static factory methods. + * + * Kotlin's equivalent of Java's [SimpleAssociation](https://github.com/xmolecules/jmolecules/blob/main/jmolecules-ddd/src/main/java/org/jmolecules/ddd/types/SimpleAssociation.java) + * + * @author Jocelyn Ntakpe + * @since 1.0 + * @see Association.forId + * @see Association.forAggregate + */ +class SimpleAssociation, ID : Identifier>(private val identifier: () -> ID) : + Association { + + override val id: ID + get() = identifier() + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as SimpleAssociation<*, *> + + if (id != other.id) return false + + return true + } + + override fun hashCode(): Int = id.hashCode() + + override fun toString(): String = id.toString() +} diff --git a/kmolecules-ddd/src/test/kotlin/org/jmolecules/ddd/types/AssociationTest.kt b/kmolecules-ddd/src/test/kotlin/org/jmolecules/ddd/types/AssociationTest.kt new file mode 100644 index 0000000..1bda92e --- /dev/null +++ b/kmolecules-ddd/src/test/kotlin/org/jmolecules/ddd/types/AssociationTest.kt @@ -0,0 +1,56 @@ +package org.jmolecules.ddd.types + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +/** + * Unit tests for [Association] + * + * @author Jocelyn Ntakpe + */ +internal class AssociationTest { + + @Test + fun `creates association from identifier`() { + val id = SampleIdentifier() + assertThat(Association.forId(id).id).isEqualTo(id) + } + + @Test + fun `creates association from aggregate`() { + val id = SampleIdentifier() + val aggregate = SampleAggregate(id) + assertThat(Association.forAggregate(aggregate).id).isEqualTo(id) + } + + @Test + fun `associations equal if they point to the same id`() { + val id = SampleIdentifier() + val aggregate = SampleAggregate(id) + assertThat(Association.forId(id)).isEqualTo( + Association.forAggregate( + aggregate + ) + ) + assertThat(Association.forAggregate(aggregate)).isEqualTo( + Association.forId( + id + ) + ) + } + + @Test + fun `points to same aggregate`() { + val id = SampleIdentifier() + val simpleAssociation: Association = Association.forId(id) + val sampleAssociation = SampleAssociation(id) + assertThat(simpleAssociation.pointsToSameAggregateAs(sampleAssociation)).isTrue + assertThat(sampleAssociation.pointsToSameAggregateAs(simpleAssociation)).isTrue + } + + class SampleIdentifier : Identifier + + class SampleAggregate(override val id: SampleIdentifier) : AggregateRoot + + class SampleAssociation(override val id: SampleIdentifier) : Association +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 98e3ae6..812dbce 100644 --- a/pom.xml +++ b/pom.xml @@ -15,12 +15,14 @@ jmolecules-ddd jmolecules-events jmolecules-architecture + kmolecules-ddd UTF-8 3.20.2 5.7.2 + 1.6.21