refactor: Migrate from monolithic to modular architecture

- Restructure project into domain-specific modules (core, masterdata, members, horses, events, infrastructure)
- Create shared client components in common-ui module
- Implement CI/CD workflows with GitHub Actions
- Consolidate documentation in docs directory
- Remove deprecated modules and documentation files
- Add cleanup and migration scripts for transition
- Update README with new project structure and setup instructions
This commit is contained in:
stefan
2025-07-22 18:44:18 +02:00
parent 8229e8e571
commit a256622f37
314 changed files with 5930 additions and 19817 deletions
@@ -0,0 +1,22 @@
plugins {
kotlin("jvm")
kotlin("plugin.spring")
kotlin("plugin.jpa") version "2.1.20"
}
dependencies {
implementation(projects.platform.platformDependencies)
implementation(projects.masterdata.masterdataDomain)
implementation(projects.masterdata.masterdataApplication)
implementation(projects.core.coreDomain)
implementation(projects.core.coreUtils)
implementation(projects.infrastructure.cache.cacheApi)
implementation(projects.infrastructure.eventStore.eventStoreApi)
implementation(projects.infrastructure.messaging.messagingClient)
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.postgresql:postgresql")
testImplementation(projects.platform.platformTesting)
}
@@ -0,0 +1,142 @@
package at.mocode.masterdata.infrastructure.persistence
import at.mocode.masterdata.domain.model.LandDefinition
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 kotlinx.datetime.Clock
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import kotlinx.datetime.toLocalDateTime
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
/**
* Implementierung des LandRepository für die Datenbankzugriffe.
*/
class LandRepositoryImpl : LandRepository {
/**
* Konvertiert eine Datenbankzeile in ein Domain-Objekt.
*/
private fun rowToLandDefinition(row: ResultRow): LandDefinition {
return LandDefinition(
landId = row[LandTable.id],
isoAlpha2Code = row[LandTable.isoAlpha2Code],
isoAlpha3Code = row[LandTable.isoAlpha3Code],
nameDeutsch = row[LandTable.nameDe],
nameEnglisch = row[LandTable.nameEn],
istEuMitglied = row[LandTable.istEuMitglied],
istEwrMitglied = row[LandTable.istEwrMitglied],
sortierReihenfolge = row[LandTable.sortierReihenfolge],
istAktiv = row[LandTable.istAktiv],
createdAt = row[LandTable.erstelltAm].toInstant(TimeZone.UTC),
updatedAt = row[LandTable.geaendertAm].toInstant(TimeZone.UTC)
)
}
override suspend fun findById(id: Uuid): LandDefinition? = DatabaseFactory.dbQuery {
LandTable.selectAll().where { LandTable.id eq id }
.map(::rowToLandDefinition)
.singleOrNull()
}
override suspend fun findByIsoAlpha2Code(isoAlpha2Code: String): LandDefinition? = DatabaseFactory.dbQuery {
LandTable.selectAll().where { LandTable.isoAlpha2Code eq isoAlpha2Code }
.map(::rowToLandDefinition)
.singleOrNull()
}
override suspend fun findByIsoAlpha3Code(isoAlpha3Code: String): LandDefinition? = DatabaseFactory.dbQuery {
LandTable.selectAll().where { LandTable.isoAlpha3Code eq isoAlpha3Code }
.map(::rowToLandDefinition)
.singleOrNull()
}
override suspend fun findByName(searchTerm: String, limit: Int): List<LandDefinition> = DatabaseFactory.dbQuery {
val pattern = "%$searchTerm%"
LandTable.selectAll().where { (LandTable.nameDe like pattern) or (LandTable.nameEn like pattern) }
.limit(limit)
.map(::rowToLandDefinition)
}
override suspend fun findAllActive(orderBySortierung: Boolean): List<LandDefinition> = DatabaseFactory.dbQuery {
val query = LandTable.selectAll().where { LandTable.istAktiv eq true }
if (orderBySortierung) {
query.orderBy(LandTable.sortierReihenfolge to SortOrder.ASC, LandTable.nameDe to SortOrder.ASC)
} else {
query.orderBy(LandTable.nameDe to SortOrder.ASC)
}
query.map(::rowToLandDefinition)
}
override suspend fun findEuMembers(): List<LandDefinition> = DatabaseFactory.dbQuery {
LandTable.selectAll().where { (LandTable.istEuMitglied eq true) and (LandTable.istAktiv eq true) }
.orderBy(LandTable.sortierReihenfolge to SortOrder.ASC, LandTable.nameDe to SortOrder.ASC)
.map(::rowToLandDefinition)
}
override suspend fun findEwrMembers(): List<LandDefinition> = DatabaseFactory.dbQuery {
LandTable.selectAll().where { (LandTable.istEwrMitglied eq true) and (LandTable.istAktiv eq true) }
.orderBy(LandTable.sortierReihenfolge to SortOrder.ASC, LandTable.nameDe to SortOrder.ASC)
.map(::rowToLandDefinition)
}
override suspend fun save(land: LandDefinition): LandDefinition = DatabaseFactory.dbQuery {
val now = Clock.System.now()
val existingLand = LandTable.selectAll().where { LandTable.id eq land.landId }.singleOrNull()
if (existingLand == null) {
// Insert a new country
LandTable.insert { stmt ->
stmt[id] = land.landId
stmt[isoAlpha2Code] = land.isoAlpha2Code
stmt[isoAlpha3Code] = land.isoAlpha3Code
stmt[nameDe] = land.nameDeutsch
stmt[nameEn] = land.nameEnglisch ?: ""
stmt[istEuMitglied] = land.istEuMitglied ?: false
stmt[istEwrMitglied] = land.istEwrMitglied ?: false
stmt[sortierReihenfolge] = land.sortierReihenfolge ?: 999
stmt[istAktiv] = land.istAktiv
stmt[erstelltAm] = land.createdAt.toLocalDateTime(TimeZone.UTC)
stmt[geaendertAm] = now.toLocalDateTime(TimeZone.UTC)
}
} else {
// Update existing country
LandTable.update({ LandTable.id eq land.landId }) { stmt ->
stmt[isoAlpha2Code] = land.isoAlpha2Code
stmt[isoAlpha3Code] = land.isoAlpha3Code
stmt[nameDe] = land.nameDeutsch
stmt[nameEn] = land.nameEnglisch ?: ""
stmt[istEuMitglied] = land.istEuMitglied ?: false
stmt[istEwrMitglied] = land.istEwrMitglied ?: false
stmt[sortierReihenfolge] = land.sortierReihenfolge ?: 999
stmt[istAktiv] = land.istAktiv
stmt[geaendertAm] = now.toLocalDateTime(TimeZone.UTC)
}
}
land.copy(updatedAt = now)
}
override suspend fun delete(id: Uuid): Boolean = DatabaseFactory.dbQuery {
LandTable.deleteWhere { LandTable.id eq id } > 0
}
override suspend fun existsByIsoAlpha2Code(isoAlpha2Code: String): Boolean = DatabaseFactory.dbQuery {
LandTable.selectAll().where { LandTable.isoAlpha2Code eq isoAlpha2Code }
.count() > 0
}
override suspend fun existsByIsoAlpha3Code(isoAlpha3Code: String): Boolean = DatabaseFactory.dbQuery {
LandTable.selectAll().where { LandTable.isoAlpha3Code eq isoAlpha3Code }
.count() > 0
}
override suspend fun countActive(): Long = DatabaseFactory.dbQuery {
LandTable.selectAll().where { LandTable.istAktiv eq true }.count()
}
}
@@ -0,0 +1,24 @@
package at.mocode.masterdata.infrastructure.persistence
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.datetime
import org.jetbrains.exposed.sql.kotlin.datetime.CurrentDateTime
/**
* Exposed-Tabellendefinition für die Land-Entität (Länderstammdaten).
*/
object LandTable : Table("land") {
val id = uuid("id").autoGenerate()
val isoAlpha2Code = varchar("iso_alpha2_code", 2).uniqueIndex()
val isoAlpha3Code = varchar("iso_alpha3_code", 3).uniqueIndex()
val nameDe = varchar("name_de", 100)
val nameEn = varchar("name_en", 100)
val istEuMitglied = bool("ist_eu_mitglied").default(false)
val istEwrMitglied = bool("ist_ewr_mitglied").default(false)
val sortierReihenfolge = integer("sortier_reihenfolge").default(999)
val istAktiv = bool("ist_aktiv").default(true)
val erstelltAm = datetime("erstellt_am").defaultExpression(CurrentDateTime)
val geaendertAm = datetime("geaendert_am").defaultExpression(CurrentDateTime)
override val primaryKey = PrimaryKey(id)
}