Skip to content

Commit

Permalink
Update Kotlin to 1.5.31
Browse files Browse the repository at this point in the history
Move kotlintest to kotest
Add KRepository (with suspend functions)
  • Loading branch information
Alex Mihailov committed Nov 3, 2021
1 parent 5572e5e commit 8257d70
Show file tree
Hide file tree
Showing 19 changed files with 556 additions and 73 deletions.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@

# DDD Repository pattern for [Lagom](https://www.lagomframework.com)/[Play](https://playframework.com)

API of library contains only one interface [Repository](https://www.javadoc.io/doc/org.taymyr.play/play-repository-api-java) for DDD aggregate, inspired the book
API of library contains interface [Repository](https://www.javadoc.io/doc/org.taymyr.play/play-repository-api-java) for DDD aggregate, inspired the book
[Implementing Domain-Driven Design](https://www.amazon.com/Implementing-Domain-Driven-Design-Vaughn-Vernon/dp/0321834577) by Vaughn Vernon.
The library also contains the [KRepository](https://www.javadoc.io/doc/org.taymyr.play/play-repository-api-java) interface for supporting [coroutines](https://kotlinlang.org/docs/coroutines-overview.html).

## Example

Expand All @@ -21,6 +22,10 @@ Create the interface of repository for aggregate
public interface AggregateRepository extends Repository<Aggregate, UUID> { }
```

```kotlin
interface AggregateKRepository : KRepository<Aggregate, UUID>
```

and implement it

```java
Expand All @@ -38,6 +43,18 @@ public class AggregateRepositoryImpl extends JPARepository<Aggregate, UUID> impl
}
```

```kotlin
class AggregateKRepositoryImpl @Inject constructor(
jpaApi: JPAApi,
executionContext: DatabaseExecutionContext,
): KJPARepository<Aggregate, UUID>(jpaApi, executionContext, Aggregate::class.java), AggregateKRepository {

override fun nextIdentity(): UUID {
return UUID.randomUUID()
}
}
```

## Contributors

Other persistence implementations (for _MongoDB_/_Cassandra_/_Redis_) are welcome.
Expand Down
6 changes: 4 additions & 2 deletions api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ plugins {

val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions.jvmTarget = "1.8"
compileKotlin.kotlinOptions.freeCompilerArgs += listOf("-Xjvm-default=enable", "-Xjsr305=strict")
compileKotlin.kotlinOptions.freeCompilerArgs += listOf("-Xjvm-default=all", "-Xjsr305=strict")

dependencies {
implementation(kotlin("stdlib-jdk8"))
Expand All @@ -20,7 +20,9 @@ dependencies {
ktlint {
version.set(Versions.ktlint)
outputToConsole.set(true)
reporters.set(setOf(ReporterType.CHECKSTYLE))
reporters {
reporter(ReporterType.CHECKSTYLE)
}
}

val sourcesJar by tasks.creating(Jar::class) {
Expand Down
142 changes: 142 additions & 0 deletions api/src/main/kotlin/org/taymyr/play/repository/domain/KRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package org.taymyr.play.repository.domain

import akka.Done

/**
* DDD repository for identified aggregate (use coroutines).
*/
interface KRepository<Aggregate, Identity> {

/**
* Generate a new identifier.
*/
fun nextIdentity(): Identity

/**
* Get aggregate by the identifier.
* @param id Identifier.
* @return aggregate or null if aggregate not exist.
* @throws Exception Any exceptions while execute a query on the database will wrapped.
*/
suspend fun get(id: Identity): Aggregate?

/**
* Get all aggregates from the repository.
* @return List of aggregates or an empty list if the repository is empty.
* @throws Exception Any exceptions while execute a query on the database will wrapped.
*/
suspend fun getAll(): List<Aggregate>

/**
* Finding aggregates on the repository by their identifiers.
* @param ids List of identifiers.
* @return List of aggregates or an empty list if aggregates not found.
* @throws Exception Any exceptions while execute a query on the database will wrapped.
*/
suspend fun findByIds(ids: List<Identity>): List<Aggregate>

/**
* Finding aggregates on the repository by their identifiers.
* @param ids List of identifiers.
* @return List of aggregates or an empty list if aggregates not found.
* @throws Exception Any exceptions while execute a query on the database will wrapped.
*/
suspend fun findByIds(vararg ids: Identity): List<Aggregate> = findByIds(ids.asList())

/**
* Removing aggregate from the repository.
* @param aggregate Aggregate.
* @return [Done] if removing successfully. Otherwise will throw an exception.
* @throws Exception Any exceptions while execute a query on the database will wrapped.
*/
suspend fun remove(aggregate: Aggregate): Done

/**
* Removing aggregates from the repository.
* @param aggregates List of aggregates.
* @return [Done] if removing successfully. Otherwise will throw an exception.
* @throws Exception Any exceptions while execute a query on the database will wrapped.
*/
suspend fun removeAll(aggregates: Collection<Aggregate>): Done

/**
* Create aggregate on the repository.
* @param aggregate Aggregate.
* @return [Done] if creation successfully. Otherwise will throw an exception.
* @throws Exception Any exceptions while execute a query on the database will wrapped.
*/
suspend fun create(aggregate: Aggregate): Done

/**
* Create aggregates on the repository.
* @param aggregates Aggregates.
* @return [Done] if creation successfully. Otherwise will throw an exception.
* @throws Exception Any exceptions while execute a query on the database will wrapped.
*/
suspend fun createAll(aggregates: Collection<Aggregate>): Done

/**
* Saving aggregate on the repository.
* @param aggregate Aggregate.
* @return [Done] if saving successfully. Otherwise will throw an exception.
* @throws Exception Any exceptions while execute a query on the database will wrapped.
*/
suspend fun save(aggregate: Aggregate): Done

/**
* Saving aggregates on the repository.
* @param aggregates Aggregates.
* @return [Done] if saving successfully. Otherwise will throw an exception.
* @throws Exception Any exceptions while execute a query on the database will wrapped.
*/
suspend fun saveAll(aggregates: Collection<Aggregate>): Done

/**
* Finding aggregates on the repository by jpaQuery.
* @param jpaQuery Jpa query.
* @param parameters Map of parameters name and value in jpa query.
* @return List of aggregates or an empty list if aggregates not found.
* @throws Exception Any exceptions while execute a query on the database will wrapped.
*/
suspend fun findAggregates(jpaQuery: String, parameters: Map<String, Any>): List<Aggregate>

/**
* Finding aggregates on the repository by jpaQuery and using pagination.
* @param jpaQuery Jpa query.
* @param parameters Map of parameters name and value in jpa query.
* @param offset Offset from the beginning of the list
* @param limit The number of elements in the sample.
* @return List of aggregates or an empty list if aggregates not found.
* @throws Exception Any exceptions while execute a query on the database will wrapped.
*/
suspend fun findAggregates(jpaQuery: String, parameters: Map<String, Any>, offset: Int, limit: Int): List<Aggregate>

/**
* Finding aggregate on the repository by jpaQuery.
* @param jpaQuery Jpa query.
* @param parameters Map of parameters name and value in jpa query.
* @return aggregate or null if aggregate not exist.
* @throws Exception Any exceptions while execute a query on the database will wrapped.
*/
suspend fun findAggregate(jpaQuery: String, parameters: Map<String, Any>): Aggregate?

/**
* Finding specific values on the repository by jpaQuery.
* @param jpaQuery Jpa query.
* @param parameters Map of parameters name and value in jpa query.
* @param clazz Class of the find value.
* @return List of aggregates or an empty list if aggregates not found.
* @throws Exception Any exceptions while execute a query on the database will wrapped.
*/
suspend fun <E> findValues(jpaQuery: String, parameters: Map<String, Any>, clazz: Class<E>): List<E>

/**
* Find specific value on the repository by jpaQuery.
* @param jpaQuery Jpa query.
* @param parameters Map of parameters name and value in jpa query.
* @param clazz Class of the find value.
* @return Specific value or null.
* @throws Exception Any exceptions while execute a query on the database will wrapped.
*/
suspend fun <E> findValue(jpaQuery: String, parameters: Map<String, Any>, clazz: Class<E>): E?
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ interface Repository<Aggregate, Identity> {
* @return List of aggregates or an empty list if aggregates not found.
* @throws Exception Any exceptions while execute a query on the database will wrapped.
*/
@JvmDefault
fun findByIds(vararg ids: Identity): CompletionStage<List<Aggregate>> = findByIds(ids.asList())

/**
Expand Down
29 changes: 18 additions & 11 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,18 @@ allprojects {
mavenCentral()
jcenter()
}
apply<JacocoPlugin>()
jacoco {
toolVersion = Versions.jacoco
}
}

subprojects {
group = "org.taymyr.play"
version = "0.2.0-SNAPSHOT"

apply<JacocoPlugin>()
apply<NexusPublishPlugin>()

jacoco {
toolVersion = Versions.jacoco
}

nexusPublishing {
repositories {
sonatype()
Expand All @@ -53,12 +52,20 @@ val jacocoAggregateReport by tasks.creating(JacocoReport::class) {
reports {
xml.isEnabled = true
}
additionalClassDirs(files(subprojects.flatMap { project ->
listOf("scala", "kotlin").map { project.buildDir.path + "/classes/$it/main" }
}))
additionalSourceDirs(files(subprojects.flatMap { project ->
listOf("scala", "kotlin").map { project.file("src/main/$it").absolutePath }
}))
additionalClassDirs(
files(
subprojects.map { project ->
listOf("scala", "kotlin").map { project.buildDir.path + "/classes/$it/main" }
}
)
)
additionalSourceDirs(
files(
subprojects.map { project ->
listOf("scala", "kotlin").map { project.file("src/main/$it").absolutePath }
}
)
)
dependsOn(jacocoAggregateMerge)
}

Expand Down
2 changes: 1 addition & 1 deletion buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
plugins {
plugins {
`kotlin-dsl`
}

Expand Down
11 changes: 6 additions & 5 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
@Suppress("ObjectPropertyName")
object Versions {
const val kotlin = "1.3.70"
const val kotlin = "1.5.31"
const val kotlinCoroutines = "1.5.2"
const val scalaBinary = "2.13"
const val lagom = "1.6.4" // "1.5.5"
const val play = "2.8.2"
const val hibernateVersion = "5.3.6.Final"
const val ktlint = "0.32.0"
const val `ktlint-plugin` = "8.0.0"
const val jacoco = "0.8.4"
const val kotlintest = "3.1.10"
const val ktlint = "0.41.0"
const val `ktlint-plugin` = "10.1.0"
const val jacoco = "0.8.7"
const val kotest = "4.6.0"
const val h2 = "1.4.197"
const val `nexus-staging` = "0.21.2"
const val `nexus-publish` = "0.4.0"
Expand Down
13 changes: 9 additions & 4 deletions jpa/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,22 @@ plugins {

val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions.jvmTarget = "1.8"
compileKotlin.kotlinOptions.freeCompilerArgs += listOf("-Xjvm-default=enable", "-Xjsr305=strict")
compileKotlin.kotlinOptions.freeCompilerArgs += listOf("-Xjvm-default=all", "-Xjsr305=strict")

val compileTestKotlin: KotlinCompile by tasks
compileTestKotlin.kotlinOptions.jvmTarget = "1.8"
compileTestKotlin.kotlinOptions.freeCompilerArgs += listOf("-Xjvm-default=enable", "-Xjsr305=strict")
compileTestKotlin.kotlinOptions.freeCompilerArgs += listOf("-Xjvm-default=all", "-Xjsr305=strict")

dependencies {
implementation(kotlin("stdlib-jdk8"))
implementation("org.jetbrains.kotlinx", "kotlinx-coroutines-core", Versions.kotlinCoroutines)
api(project(":play-repository-api-java"))
compileOnly("com.typesafe.play", "play-java-jpa_$scalaBinaryVersion", playVersion)
implementation("org.hibernate", "hibernate-entitymanager", Versions.hibernateVersion)

testImplementation("io.kotlintest", "kotlintest-runner-junit5", Versions.kotlintest)
testImplementation("io.kotest", "kotest-runner-junit5", Versions.kotest)
testImplementation("io.kotest", "kotest-assertions-core", Versions.kotest)
testImplementation("io.kotest", "kotest-property", Versions.kotest)
testImplementation("com.typesafe.play", "play-test_$scalaBinaryVersion", playVersion)
testImplementation("com.typesafe.play", "play-jdbc-evolutions_$scalaBinaryVersion", playVersion)
testImplementation("com.h2database", "h2", Versions.h2)
Expand All @@ -43,7 +46,9 @@ sourceSets.test {
ktlint {
version.set(Versions.ktlint)
outputToConsole.set(true)
reporters.set(setOf(ReporterType.CHECKSTYLE))
reporters {
reporter(ReporterType.CHECKSTYLE)
}
}

tasks.withType<Test> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ class DatabaseExecutionContext @Inject constructor(actorSystem: ActorSystem) : E
companion object {
const val DISPATCHER_NAME = "database.dispatcher"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.taymyr.play.repository.infrastructure.persistence

import play.db.jpa.JPAApi
import java.io.Serializable
import javax.persistence.EntityManager

abstract class JPABaseRepository<Aggregate : Any, Identity : Serializable> @JvmOverloads constructor(
protected val jpaApi: JPAApi,
protected val executionContext: DatabaseExecutionContext,
protected val clazz: Class<out Aggregate>,
protected val persistenceUnitName: String = "default"
) {
protected fun <E> transaction(function: (EntityManager) -> E): E = jpaApi.withTransaction(persistenceUnitName, function)

protected fun <E> readOnly(function: (EntityManager) -> E): E = jpaApi.withTransaction(persistenceUnitName, true, function)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,23 @@ import java.util.Optional
import java.util.Optional.ofNullable
import java.util.concurrent.CompletableFuture.supplyAsync
import java.util.concurrent.CompletionStage
import java.util.function.Supplier
import javax.persistence.EntityManager

/**
* JPA implementation of DDD repository for aggregates.
*/
abstract class JPARepository<Aggregate : Any, Identity : Serializable> @JvmOverloads constructor(
protected val jpaApi: JPAApi,
protected val executionContext: DatabaseExecutionContext,
protected val clazz: Class<out Aggregate>,
protected val persistenceUnitName: String = "default"
) : Repository<Aggregate, Identity> {

protected fun <E> transaction(function: (EntityManager) -> E): E = jpaApi.withTransaction(persistenceUnitName, function)

protected fun <E> readOnly(function: (EntityManager) -> E): E = jpaApi.withTransaction(persistenceUnitName, true, function)
jpaApi: JPAApi,
executionContext: DatabaseExecutionContext,
clazz: Class<out Aggregate>,
persistenceUnitName: String = "default"
) : JPABaseRepository<Aggregate, Identity>(jpaApi, executionContext, clazz, persistenceUnitName), Repository<Aggregate, Identity> {

protected fun <E> execute(function: (EntityManager) -> E): CompletionStage<E> =
supplyAsync(Supplier { transaction(function) }, executionContext)
supplyAsync({ transaction(function) }, executionContext)

protected fun <E> executeRO(function: (EntityManager) -> E): CompletionStage<E> =
supplyAsync(Supplier { readOnly(function) }, executionContext)
supplyAsync({ readOnly(function) }, executionContext)

protected fun <E> executeSession(function: (Session) -> E): CompletionStage<E> =
execute { em -> function.invoke(em.unwrap(Session::class.java)) }
Expand Down
Loading

0 comments on commit 8257d70

Please sign in to comment.