docs: Migrationsplan für Projekt-Restrukturierung hinzugefügt

- Detaillierter Plan zur Migration von alter zu neuer Modulstruktur
- Umfasst Überführung von shared-kernel zu core-Modulen
- Definiert Migration von Fachdomänen zu bounded contexts:
  * master-data → masterdata-Module
  * member-management → members-Module
  * horse-registry → horses-Module
  * event-management → events-Module
- Beschreibt Verlagerung von api-gateway zu infrastructure/gateway
- Strukturiert nach Domain-driven Design Prinzipien
- Berücksichtigt Clean Architecture Layering (domain, application, infrastructure, api)
This commit is contained in:
stefan
2025-07-25 13:14:44 +02:00
parent 65a0084f91
commit 4c382e64a5
11 changed files with 35 additions and 55 deletions
+3 -1
View File
@@ -1,3 +1,5 @@
import java.util.Locale
plugins { plugins {
kotlin("jvm") version "2.1.21" apply false kotlin("jvm") version "2.1.21" apply false
kotlin("plugin.spring") version "2.1.21" apply false kotlin("plugin.spring") version "2.1.21" apply false
@@ -113,7 +115,7 @@ tasks.register("generateOpenApiDocs") {
{ {
"openapi": "3.0.3", "openapi": "3.0.3",
"info": { "info": {
"title": "${moduleName.capitalize()} API", "title": "${moduleName.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }} API",
"description": "REST API for ${moduleName} management", "description": "REST API for ${moduleName} management",
"version": "1.0.0", "version": "1.0.0",
"contact": { "contact": {
@@ -2,8 +2,6 @@ package at.mocode.core.domain.event
import com.benasher44.uuid.Uuid import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4 import com.benasher44.uuid.uuid4
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
/** /**
* Interface for all domain events in the system. * Interface for all domain events in the system.
@@ -4,7 +4,6 @@ import at.mocode.core.domain.event.BaseDomainEvent
import at.mocode.core.domain.event.DomainEvent import at.mocode.core.domain.event.DomainEvent
import at.mocode.infrastructure.eventstore.api.EventSerializer import at.mocode.infrastructure.eventstore.api.EventSerializer
import at.mocode.infrastructure.eventstore.api.EventStore import at.mocode.infrastructure.eventstore.api.EventStore
import at.mocode.infrastructure.eventstore.api.Subscription
import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@@ -15,9 +14,8 @@ import org.testcontainers.containers.GenericContainer
import org.testcontainers.junit.jupiter.Container import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers import org.testcontainers.junit.jupiter.Testcontainers
import org.testcontainers.utility.DockerImageName import org.testcontainers.utility.DockerImageName
import java.time.Duration
import java.time.Instant import java.time.Instant
import java.util.UUID import java.util.*
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -1,10 +1,8 @@
package at.mocode.infrastructure.eventstore.redis package at.mocode.infrastructure.eventstore.redis
import at.mocode.core.domain.event.BaseDomainEvent import at.mocode.core.domain.event.BaseDomainEvent
import at.mocode.core.domain.event.DomainEvent
import at.mocode.infrastructure.eventstore.api.ConcurrencyException import at.mocode.infrastructure.eventstore.api.ConcurrencyException
import at.mocode.infrastructure.eventstore.api.EventSerializer import at.mocode.infrastructure.eventstore.api.EventSerializer
import at.mocode.infrastructure.eventstore.api.Subscription
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.AfterEach
@@ -19,12 +17,8 @@ import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers import org.testcontainers.junit.jupiter.Testcontainers
import org.testcontainers.utility.DockerImageName import org.testcontainers.utility.DockerImageName
import java.time.Instant import java.time.Instant
import java.util.UUID import java.util.*
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@Testcontainers @Testcontainers
class RedisEventStoreTest { class RedisEventStoreTest {
@@ -4,7 +4,6 @@ import at.mocode.core.domain.event.BaseDomainEvent
import at.mocode.core.domain.event.DomainEvent import at.mocode.core.domain.event.DomainEvent
import at.mocode.infrastructure.eventstore.api.EventSerializer import at.mocode.infrastructure.eventstore.api.EventSerializer
import at.mocode.infrastructure.eventstore.api.EventStore import at.mocode.infrastructure.eventstore.api.EventStore
import at.mocode.infrastructure.eventstore.api.Subscription
import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@@ -16,7 +15,7 @@ import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers import org.testcontainers.junit.jupiter.Testcontainers
import org.testcontainers.utility.DockerImageName import org.testcontainers.utility.DockerImageName
import java.time.Instant import java.time.Instant
import java.util.UUID import java.util.*
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -88,7 +88,7 @@ class AltersklasseController(
val sparteFilter = sparteFilterParam?.let { val sparteFilter = sparteFilterParam?.let {
try { try {
SparteE.valueOf(it.uppercase()) SparteE.valueOf(it.uppercase())
} catch (e: Exception) { } catch (_: Exception) {
return@get call.respond( return@get call.respond(
HttpStatusCode.BadRequest, HttpStatusCode.BadRequest,
ApiResponse.error<List<AltersklasseDto>>("Invalid sparte parameter: $it") ApiResponse.error<List<AltersklasseDto>>("Invalid sparte parameter: $it")
@@ -224,7 +224,7 @@ class AltersklasseController(
val sparte = try { val sparte = try {
SparteE.valueOf(sparteParam.uppercase()) SparteE.valueOf(sparteParam.uppercase())
} catch (e: Exception) { } catch (_: Exception) {
return@get call.respond(HttpStatusCode.BadRequest, ApiResponse.error<List<AltersklasseDto>>("Invalid sport type: $sparteParam")) return@get call.respond(HttpStatusCode.BadRequest, ApiResponse.error<List<AltersklasseDto>>("Invalid sport type: $sparteParam"))
} }
@@ -264,7 +264,7 @@ class AltersklasseController(
val sparteFilter = createDto.sparteFilter?.let { val sparteFilter = createDto.sparteFilter?.let {
try { try {
SparteE.valueOf(it.uppercase()) SparteE.valueOf(it.uppercase())
} catch (e: Exception) { } catch (_: Exception) {
return@post call.respond( return@post call.respond(
HttpStatusCode.BadRequest, HttpStatusCode.BadRequest,
ApiResponse.error<AltersklasseDto>("Invalid sparte filter: $it") ApiResponse.error<AltersklasseDto>("Invalid sparte filter: $it")
@@ -286,7 +286,7 @@ class AltersklasseController(
val oetoRegelReferenzId = createDto.oetoRegelReferenzId?.let { val oetoRegelReferenzId = createDto.oetoRegelReferenzId?.let {
try { try {
uuidFrom(it) uuidFrom(it)
} catch (e: Exception) { } catch (_: Exception) {
return@post call.respond( return@post call.respond(
HttpStatusCode.BadRequest, HttpStatusCode.BadRequest,
ApiResponse.error<AltersklasseDto>("Invalid OETO regel referenz ID format") ApiResponse.error<AltersklasseDto>("Invalid OETO regel referenz ID format")
@@ -345,7 +345,7 @@ class AltersklasseController(
val sparteFilter = updateDto.sparteFilter?.let { val sparteFilter = updateDto.sparteFilter?.let {
try { try {
SparteE.valueOf(it.uppercase()) SparteE.valueOf(it.uppercase())
} catch (e: Exception) { } catch (_: Exception) {
return@put call.respond( return@put call.respond(
HttpStatusCode.BadRequest, HttpStatusCode.BadRequest,
ApiResponse.error<AltersklasseDto>("Invalid sparte filter: $it") ApiResponse.error<AltersklasseDto>("Invalid sparte filter: $it")
@@ -367,7 +367,7 @@ class AltersklasseController(
val oetoRegelReferenzId = updateDto.oetoRegelReferenzId?.let { val oetoRegelReferenzId = updateDto.oetoRegelReferenzId?.let {
try { try {
uuidFrom(it) uuidFrom(it)
} catch (e: Exception) { } catch (_: Exception) {
return@put call.respond( return@put call.respond(
HttpStatusCode.BadRequest, HttpStatusCode.BadRequest,
ApiResponse.error<AltersklasseDto>("Invalid OETO regel referenz ID format") ApiResponse.error<AltersklasseDto>("Invalid OETO regel referenz ID format")
@@ -130,7 +130,7 @@ class BundeslandController(
val landId = try { val landId = try {
uuidFrom(landIdParam) uuidFrom(landIdParam)
} catch (e: Exception) { } catch (_: Exception) {
return@get call.respond(HttpStatusCode.BadRequest, ApiResponse.error<BundeslandDto>("Invalid country ID format")) return@get call.respond(HttpStatusCode.BadRequest, ApiResponse.error<BundeslandDto>("Invalid country ID format"))
} }
@@ -235,7 +235,7 @@ class BundeslandController(
try { try {
uuidFrom(createDto.landId) uuidFrom(createDto.landId)
} catch (e: Exception) { } catch (_: Exception) {
call.respond( call.respond(
HttpStatusCode.BadRequest, HttpStatusCode.BadRequest,
ApiResponse.error<BundeslandDto>("Invalid country ID format") ApiResponse.error<BundeslandDto>("Invalid country ID format")
@@ -284,7 +284,7 @@ class BundeslandController(
try { try {
uuidFrom(updateDto.landId) uuidFrom(updateDto.landId)
} catch (e: Exception) { } catch (_: Exception) {
call.respond( call.respond(
HttpStatusCode.BadRequest, HttpStatusCode.BadRequest,
ApiResponse.error<BundeslandDto>("Invalid country ID format") ApiResponse.error<BundeslandDto>("Invalid country ID format")
@@ -153,7 +153,7 @@ class PlatzController(
val typ = try { val typ = try {
PlatzTypE.valueOf(typParam.uppercase()) PlatzTypE.valueOf(typParam.uppercase())
} catch (e: Exception) { } catch (_: Exception) {
return@get call.respond(HttpStatusCode.BadRequest, ApiResponse.error<List<PlatzDto>>("Invalid venue type: $typParam")) return@get call.respond(HttpStatusCode.BadRequest, ApiResponse.error<List<PlatzDto>>("Invalid venue type: $typParam"))
} }
@@ -223,7 +223,7 @@ class PlatzController(
val requiredType = try { val requiredType = try {
PlatzTypE.valueOf(typParam.uppercase()) PlatzTypE.valueOf(typParam.uppercase())
} catch (e: Exception) { } catch (_: Exception) {
return@get call.respond(HttpStatusCode.BadRequest, ApiResponse.error<List<PlatzDto>>("Invalid venue type: $typParam")) return@get call.respond(HttpStatusCode.BadRequest, ApiResponse.error<List<PlatzDto>>("Invalid venue type: $typParam"))
} }
@@ -255,7 +255,7 @@ class PlatzController(
val turnierId = try { val turnierId = try {
uuidFrom(createDto.turnierId) uuidFrom(createDto.turnierId)
} catch (e: Exception) { } catch (_: Exception) {
return@post call.respond( return@post call.respond(
HttpStatusCode.BadRequest, HttpStatusCode.BadRequest,
ApiResponse.error<PlatzDto>("Invalid tournament ID format") ApiResponse.error<PlatzDto>("Invalid tournament ID format")
@@ -264,7 +264,7 @@ class PlatzController(
val typ = try { val typ = try {
PlatzTypE.valueOf(createDto.typ.uppercase()) PlatzTypE.valueOf(createDto.typ.uppercase())
} catch (e: Exception) { } catch (_: Exception) {
return@post call.respond( return@post call.respond(
HttpStatusCode.BadRequest, HttpStatusCode.BadRequest,
ApiResponse.error<PlatzDto>("Invalid venue type: ${createDto.typ}") ApiResponse.error<PlatzDto>("Invalid venue type: ${createDto.typ}")
@@ -311,7 +311,7 @@ class PlatzController(
val turnierId = try { val turnierId = try {
uuidFrom(updateDto.turnierId) uuidFrom(updateDto.turnierId)
} catch (e: Exception) { } catch (_: Exception) {
return@put call.respond( return@put call.respond(
HttpStatusCode.BadRequest, HttpStatusCode.BadRequest,
ApiResponse.error<PlatzDto>("Invalid tournament ID format") ApiResponse.error<PlatzDto>("Invalid tournament ID format")
@@ -320,7 +320,7 @@ class PlatzController(
val typ = try { val typ = try {
PlatzTypE.valueOf(updateDto.typ.uppercase()) PlatzTypE.valueOf(updateDto.typ.uppercase())
} catch (e: Exception) { } catch (_: Exception) {
return@put call.respond( return@put call.respond(
HttpStatusCode.BadRequest, HttpStatusCode.BadRequest,
ApiResponse.error<PlatzDto>("Invalid venue type: ${updateDto.typ}") ApiResponse.error<PlatzDto>("Invalid venue type: ${updateDto.typ}")
@@ -387,7 +387,7 @@ class PlatzController(
val typ = try { val typ = try {
PlatzTypE.valueOf(typParam.uppercase()) PlatzTypE.valueOf(typParam.uppercase())
} catch (e: Exception) { } catch (_: Exception) {
return@get call.respond(HttpStatusCode.BadRequest, ApiResponse.error<Long>("Invalid venue type: $typParam")) return@get call.respond(HttpStatusCode.BadRequest, ApiResponse.error<Long>("Invalid venue type: $typParam"))
} }
@@ -433,7 +433,7 @@ class PlatzController(
val requiredType = requiredTypeParam?.let { val requiredType = requiredTypeParam?.let {
try { try {
PlatzTypE.valueOf(it.uppercase()) PlatzTypE.valueOf(it.uppercase())
} catch (e: Exception) { } catch (_: Exception) {
return@get call.respond(HttpStatusCode.BadRequest, ApiResponse.error<Map<String, Any>>("Invalid required type: $it")) return@get call.respond(HttpStatusCode.BadRequest, ApiResponse.error<Map<String, Any>>("Invalid required type: $it"))
} }
} }
@@ -1,9 +1,8 @@
package at.mocode.masterdata.infrastructure.persistence package at.mocode.masterdata.infrastructure.persistence
import at.mocode.core.utils.database.DatabaseFactory
import at.mocode.masterdata.domain.model.LandDefinition import at.mocode.masterdata.domain.model.LandDefinition
import at.mocode.masterdata.domain.repository.LandRepository import at.mocode.masterdata.domain.repository.LandRepository
import at.mocode.masterdata.infrastructure.persistence.LandTable
import at.mocode.core.utils.database.DatabaseFactory
import com.benasher44.uuid.Uuid import com.benasher44.uuid.Uuid
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
@@ -1,30 +1,21 @@
package at.mocode.members.api.rest package at.mocode.members.api.rest
import at.mocode.core.domain.model.ApiResponse import at.mocode.core.domain.model.ApiResponse
import at.mocode.members.application.usecase.CreateMemberUseCase
import at.mocode.members.application.usecase.DeleteMemberUseCase
import at.mocode.members.application.usecase.FindExpiringMembershipsUseCase
import at.mocode.members.application.usecase.FindMembersByDateRangeUseCase
import at.mocode.members.application.usecase.GetMemberUseCase
import at.mocode.members.application.usecase.UpdateMemberUseCase
import at.mocode.members.application.usecase.ValidateMemberDataUseCase
import at.mocode.members.domain.repository.MemberRepository
import at.mocode.infrastructure.messaging.client.EventPublisher import at.mocode.infrastructure.messaging.client.EventPublisher
import com.benasher44.uuid.Uuid import at.mocode.members.application.usecase.*
import at.mocode.members.domain.repository.MemberRepository
import com.benasher44.uuid.uuidFrom import com.benasher44.uuid.uuidFrom
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.responses.ApiResponses
import io.swagger.v3.oas.annotations.tags.Tag
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate
import org.springframework.beans.factory.annotation.Qualifier import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.http.HttpStatus import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.*
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.responses.ApiResponse as SwaggerApiResponse import io.swagger.v3.oas.annotations.responses.ApiResponse as SwaggerApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import io.swagger.v3.oas.annotations.tags.Tag
import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.media.Schema
/** /**
* Simple no-op EventPublisher implementation for the controller. * Simple no-op EventPublisher implementation for the controller.
@@ -1,21 +1,20 @@
package at.mocode.members.service.integration package at.mocode.members.service.integration
import at.mocode.infrastructure.messaging.client.EventPublisher
import at.mocode.members.api.rest.MemberController import at.mocode.members.api.rest.MemberController
import at.mocode.members.domain.model.Member import at.mocode.members.domain.model.Member
import at.mocode.members.domain.repository.MemberRepository import at.mocode.members.domain.repository.MemberRepository
import at.mocode.members.infrastructure.persistence.MemberRepositoryImpl
import at.mocode.infrastructure.messaging.client.EventPublisher
import kotlinx.datetime.LocalDate
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test import kotlinx.datetime.LocalDate
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.MockBean import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.TestPropertySource import org.springframework.test.context.TestPropertySource
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
import kotlin.test.assertTrue import kotlin.test.assertTrue