feat(horses-service): remove legacy configuration and repository classes
Build and Publish Docker Images / build-and-push (., backend/infrastructure/gateway/Dockerfile, api-gateway, api-gateway) (push) Successful in 7m12s
Build and Publish Docker Images / build-and-push (., backend/services/ping/Dockerfile, ping-service, ping-service) (push) Successful in 6m42s
Build and Publish Docker Images / build-and-push (., config/docker/caddy/web-app/Dockerfile, web-app, web-app) (push) Successful in 6m28s
Build and Publish Docker Images / build-and-push (., config/docker/keycloak/Dockerfile, keycloak, keycloak) (push) Successful in 1m51s

- Deleted outdated `ApplicationConfiguration` and `HorseRepositoryImpl` classes.
- Migrated to streamlined modular Gradle configuration in `build.gradle.kts`.
- Updated domain models and dependencies to use multiplatform and serialization plugins.
- Added modular setup for `horses` namespace in `settings.gradle.kts`.
- Reorganized database configuration with minimal JDBC bindings for development.

Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
2026-03-23 13:47:04 +01:00
parent 7b35831d8c
commit c53daa926a
44 changed files with 75781 additions and 530 deletions
@@ -0,0 +1,26 @@
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.kotlinSerialization)
}
kotlin {
jvm()
sourceSets {
commonMain {
kotlin.srcDir("src/main/kotlin")
dependencies {
implementation(projects.core.coreDomain)
implementation(projects.core.coreUtils)
implementation(libs.kotlinx.datetime)
implementation(libs.kotlinx.serialization.json)
}
}
commonTest {
kotlin.srcDir("src/test/kotlin")
dependencies {
implementation(projects.platform.platformTesting)
}
}
}
}
@@ -0,0 +1,38 @@
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
package at.mocode.clubs.domain.model
import at.mocode.core.domain.model.DatenQuelleE
import at.mocode.core.domain.serialization.KotlinInstantSerializer
import at.mocode.core.domain.serialization.UuidSerializer
import kotlinx.serialization.Serializable
import kotlin.time.Clock
import kotlin.time.Instant
import kotlin.uuid.Uuid
/**
* Domain model representing a club (Verein) in the registry system.
*
* @property clubId Unique internal identifier (UUID).
* @property vereinsNummer ÖPS club number (from ZNS VEREIN01.dat).
* @property name Club name.
* @property datenQuelle Source of the data.
* @property createdAt Timestamp when this record was created.
* @property updatedAt Timestamp when this record was last updated.
*/
@Serializable
data class DomClub(
@Serializable(with = UuidSerializer::class)
val clubId: Uuid = Uuid.random(),
val vereinsNummer: String,
val name: String,
val istAktiv: Boolean = true,
val datenQuelle: DatenQuelleE = DatenQuelleE.IMPORT_ZNS,
@Serializable(with = KotlinInstantSerializer::class)
val createdAt: Instant = Clock.System.now(),
@Serializable(with = KotlinInstantSerializer::class)
var updatedAt: Instant = Clock.System.now()
)
@@ -0,0 +1,23 @@
plugins {
alias(libs.plugins.kotlinJvm)
alias(libs.plugins.kotlinSpring)
alias(libs.plugins.kotlinSerialization)
}
dependencies {
implementation(projects.platform.platformDependencies)
implementation(projects.clubs.clubsDomain)
implementation(projects.core.coreDomain)
implementation(projects.core.coreUtils)
implementation(libs.spring.boot.starter.web)
implementation(libs.exposed.core)
implementation(libs.exposed.dao)
implementation(libs.exposed.jdbc)
implementation(libs.exposed.kotlin.datetime)
runtimeOnly(libs.postgresql.driver)
testImplementation(projects.platform.platformTesting)
}
@@ -0,0 +1,14 @@
package at.mocode.clubs.infrastructure.persistence
import org.jetbrains.exposed.v1.core.dao.id.java.UUIDTable
import org.jetbrains.exposed.v1.datetime.timestamp
/**
* Tabelle für importierte Vereine aus dem ZNS-Datenbestand (VEREIN01.dat).
* Wird ausschließlich als Dev-Seed-Daten verwendet.
*/
object ZnsClubTable : UUIDTable("zns_clubs") {
val vereinsNummer = varchar("vereins_nummer", 4).uniqueIndex()
val name = varchar("name", 50)
val createdAt = timestamp("created_at")
}
@@ -0,0 +1,38 @@
plugins {
alias(libs.plugins.kotlinJvm)
alias(libs.plugins.kotlinSpring)
alias(libs.plugins.spring.boot)
alias(libs.plugins.spring.dependencyManagement)
}
springBoot {
mainClass.set("at.mocode.clubs.service.ClubsServiceApplicationKt")
}
dependencies {
implementation(projects.platform.platformDependencies)
implementation(projects.core.coreDomain)
implementation(projects.core.coreUtils)
implementation(projects.clubs.clubsDomain)
implementation(projects.clubs.clubsInfrastructure)
implementation(libs.spring.boot.starter.web)
implementation(libs.spring.boot.starter.validation)
implementation(libs.spring.boot.starter.actuator)
implementation(libs.exposed.core)
implementation(libs.exposed.dao)
implementation(libs.exposed.jdbc)
implementation(libs.exposed.kotlin.datetime)
implementation(libs.hikari.cp)
runtimeOnly(libs.postgresql.driver)
testRuntimeOnly(libs.h2.driver)
testImplementation(projects.platform.platformTesting)
testImplementation(libs.spring.boot.starter.test)
testImplementation(libs.logback.classic)
}
tasks.test {
useJUnitPlatform()
}
@@ -0,0 +1,18 @@
package at.mocode.clubs.service
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.ComponentScan
@SpringBootApplication
@ComponentScan(
basePackages = [
"at.mocode.clubs.service",
"at.mocode.clubs.infrastructure"
]
)
class ClubsServiceApplication
fun main(args: Array<String>) {
runApplication<ClubsServiceApplication>(*args)
}
@@ -0,0 +1,31 @@
package at.mocode.clubs.service.config
import at.mocode.clubs.infrastructure.persistence.ZnsClubTable
import jakarta.annotation.PostConstruct
import org.jetbrains.exposed.v1.jdbc.Database
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
@Configuration
@Profile("dev")
class ClubsDatabaseConfiguration(
@Value("\${spring.datasource.url}") private val jdbcUrl: String,
@Value("\${spring.datasource.username}") private val username: String,
@Value("\${spring.datasource.password}") private val password: String
) {
private val log = LoggerFactory.getLogger(ClubsDatabaseConfiguration::class.java)
@PostConstruct
fun initializeDatabase() {
log.info("Initialisiere Datenbank-Schema für Clubs-Service...")
Database.connect(jdbcUrl, user = username, password = password)
transaction {
SchemaUtils.createMissingTablesAndColumns(ZnsClubTable)
log.info("Datenbank-Schema erfolgreich initialisiert")
}
}
}
@@ -0,0 +1,80 @@
package at.mocode.clubs.service.dev
import at.mocode.clubs.infrastructure.persistence.ZnsClubTable
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
import org.jetbrains.exposed.v1.jdbc.batchInsert
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.CommandLineRunner
import org.springframework.context.annotation.Profile
import org.springframework.stereotype.Component
import java.io.File
import kotlin.time.Clock
/**
* Dev-only Seeder: Importiert VEREIN01.dat (ZNS) → zns_clubs.
* Aktivierung: Umgebungsvariable ZNS_DATA_DIR setzen + Profil "dev".
*/
@Component
@Profile("dev")
class ZnsClubSeeder(
@Value("\${zns.data.dir:#{null}}") private val znsDataDir: String?
) : CommandLineRunner {
private val log = LoggerFactory.getLogger(ZnsClubSeeder::class.java)
override fun run(vararg args: String) {
if (znsDataDir == null) {
log.info("ZNS_DATA_DIR nicht gesetzt ZnsClubSeeder wird übersprungen.")
return
}
val dir = File(znsDataDir)
if (!dir.exists() || !dir.isDirectory) {
log.warn("ZNS_DATA_DIR '{}' existiert nicht oder ist kein Verzeichnis.", znsDataDir)
return
}
log.info("Starte ZNS-Vereine-Import aus: {}", dir.absolutePath)
transaction {
SchemaUtils.createMissingTablesAndColumns(ZnsClubTable)
}
seedClubs(dir)
log.info("ZNS-Vereine-Import abgeschlossen.")
}
// -------------------------------------------------------------------------
// VEREIN01.dat → zns_clubs
// Format:
// [0-3] VereinsNr (4 Zeichen)
// [4-53] Name (50 Zeichen)
// -------------------------------------------------------------------------
private fun seedClubs(dir: File) {
val file = File(dir, "VEREIN01.dat")
if (!file.exists()) {
log.warn("VEREIN01.dat nicht gefunden Vereine werden übersprungen.")
return
}
data class ClubRow(val nr: String, val name: String)
val rows = file.readLines(Charsets.ISO_8859_1)
.filter { it.length >= 4 }
.map { line ->
ClubRow(
nr = line.substring(0, 4).trim(),
name = line.substring(4).trim().take(50)
)
}
.filter { it.nr.isNotBlank() }
val now = Clock.System.now()
transaction {
ZnsClubTable.batchInsert(rows, ignore = true) { row ->
this[ZnsClubTable.vereinsNummer] = row.nr
this[ZnsClubTable.name] = row.name
this[ZnsClubTable.createdAt] = now
}
}
log.info("Vereine importiert: {} Datensätze", rows.size)
}
}