refactor(core): Unify components and adopt standard tooling

This commit performs several key refactorings within the `core`-module to improve consistency, stability, and adhere to industry best practices.

1.  **Unify `Result` Type:**
    Removed the specialized `Result<T>` class from `core-utils`. The entire system will now exclusively use the more flexible and type-safe `Result<T, E>` from `core-domain`. This allows for explicit, non-exception-based error handling for business logic.

2.  **Adopt Flyway for Database Migrations:**
    Replaced the custom `DatabaseMigrator.kt` implementation with the industry-standard tool Flyway. The `DatabaseFactory` now triggers Flyway migrations on application startup. This provides more robust, transactional, and feature-rich schema management.

3.  **Cleanup and Housekeeping:**
    - Removed obsolete test files related to the old migrator.
    - Ensured all components align with the new unified patterns.

BREAKING CHANGE: The `at.mocode.core.utils.error.Result` class has been removed. All modules must be updated to use the `at.mocode.core.domain.error.Result` type. The custom migrator is no longer available.

Closes #ISSUE_NUMBER_FOR_REFACTORING
This commit is contained in:
2025-07-28 19:15:20 +02:00
parent da5fd6fbff
commit ca4d476360
4 changed files with 186 additions and 728 deletions
@@ -5,6 +5,7 @@ import com.zaxxer.hikari.HikariDataSource
import kotlinx.coroutines.Dispatchers
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
import org.flywaydb.core.Flyway
/**
* Factory-Klasse für die Datenbankverbindung.
@@ -64,6 +65,30 @@ object DatabaseFactory {
dataSource = HikariDataSource(hikariConfig)
Database.connect(dataSource!!)
// Flyway-Migrationen wenn aktiviert
if (config.autoMigrate) {
runFlyway(dataSource!!)
}
}
private fun runFlyway(dataSource: HikariDataSource) {
println("Starte Flyway-Migrationen...")
val flyway = Flyway.configure()
.dataSource(dataSource)
.locations("classpath:db/migration") // Sagt Flyway, wo die SQL-Dateien liegen
.load()
try {
flyway.migrate()
println("Flyway-Migrationen erfolgreich abgeschlossen.")
} catch (e: Exception) {
println("FEHLER: Flyway-Migration fehlgeschlagen! Repariere Schema...")
// Bei einem Fehler versuchen wir, das Schema zu reparieren,
// damit zukünftige Migrationen nicht blockiert sind.
flyway.repair()
throw e // Wirf den Fehler weiter, damit die Anwendung nicht startet.
}
}
/**
@@ -1,100 +1,104 @@
package at.mocode.core.utils.database
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.kotlin.datetime.CurrentTimestamp
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp
/**
* Führt Datenbankmigrationen durch.
* Diese Klasse verwaltet und führt alle notwendigen Datenbankmigrationen aus.
/*
Wegen Flyway nicht mehr benötigt
*/
object DatabaseMigrator {
private val migrations = mutableListOf<Migration>()
private val executedMigrations = mutableSetOf<String>()
/**
* Registriert eine Migration.
* @param migration Die zu registrierende Migration
*/
fun register(migration: Migration) {
migrations.add(migration)
}
/**
* Registriert mehrere Migrationen auf einmal.
* @param migrations Die zu registrierenden Migrationen
*/
fun registerAll(vararg migrations: Migration) {
this.migrations.addAll(migrations)
}
/**
* Führt alle registrierten Migrationen aus, die noch nicht ausgeführt wurden.
*/
fun migrate() {
// Erstelle die Migrationstabelle, wenn sie nicht existiert
transaction {
SchemaUtils.create(MigrationTable)
// Lade bereits ausgeführte Migrationen
MigrationTable.selectAll().forEach {
executedMigrations.add(it[MigrationTable.id])
}
// Sortiere Migrationen nach Version
val sortedMigrations = migrations.sortedBy { it.version }
// Führe noch nicht ausgeführte Migrationen aus
for (migration in sortedMigrations) {
if (!executedMigrations.contains(migration.id)) {
println("Ausführen der Migration: ${migration.id}")
try {
migration.up()
// Markiere Migration als ausgeführt
MigrationTable.insert {
it[id] = migration.id
it[version] = migration.version
it[description] = migration.description
}
commit()
println("Migration erfolgreich: ${migration.id}")
} catch (e: Exception) {
rollback()
println("Migration fehlgeschlagen: ${migration.id} - ${e.message}")
throw e
}
}
}
}
}
}
/**
* Tabelle zur Verfolgung ausgeführter Migrationen.
*/
object MigrationTable : Table("_migrations") {
val id = varchar("id", 100)
val version = long("version")
val description = varchar("description", 255)
val executedAt = timestamp("executed_at").defaultExpression(CurrentTimestamp)
override val primaryKey = PrimaryKey(id)
}
/**
* Basisklasse für Datenbankmigrationen.
*/
abstract class Migration(val version: Long, val description: String) {
/**
* Eindeutige ID der Migration, bestehend aus Version und Beschreibung.
*/
val id: String = "V${version}_${description.replace("\\s+".toRegex(), "_")}"
/**
* Führt die Migration aus.
*/
abstract fun up()
}
//import org.jetbrains.exposed.sql.*
//import org.jetbrains.exposed.sql.transactions.transaction
//import org.jetbrains.exposed.sql.kotlin.datetime.CurrentTimestamp
//import org.jetbrains.exposed.sql.kotlin.datetime.timestamp
//
///**
// * Führt Datenbankmigrationen durch.
// * Diese Klasse verwaltet und führt alle notwendigen Datenbankmigrationen aus.
// */
//object DatabaseMigrator {
// private val migrations = mutableListOf<Migration>()
// private val executedMigrations = mutableSetOf<String>()
//
// /**
// * Registriert eine Migration.
// * @param migration Die zu registrierende Migration
// */
// fun register(migration: Migration) {
// migrations.add(migration)
// }
//
// /**
// * Registriert mehrere Migrationen auf einmal.
// * @param migrations Die zu registrierenden Migrationen
// */
// fun registerAll(vararg migrations: Migration) {
// this.migrations.addAll(migrations)
// }
//
// /**
// * Führt alle registrierten Migrationen aus, die noch nicht ausgeführt wurden.
// */
// fun migrate() {
// // Erstelle die Migrationstabelle, wenn sie nicht existiert
// transaction {
// SchemaUtils.create(MigrationTable)
//
// // Lade bereits ausgeführte Migrationen
// MigrationTable.selectAll().forEach {
// executedMigrations.add(it[MigrationTable.id])
// }
//
// // Sortiere Migrationen nach Version
// val sortedMigrations = migrations.sortedBy { it.version }
//
// // Führe noch nicht ausgeführte Migrationen aus
// for (migration in sortedMigrations) {
// if (!executedMigrations.contains(migration.id)) {
// println("Ausführen der Migration: ${migration.id}")
// try {
// migration.up()
//
// // Markiere Migration als ausgeführt
// MigrationTable.insert {
// it[id] = migration.id
// it[version] = migration.version
// it[description] = migration.description
// }
//
// commit()
// println("Migration erfolgreich: ${migration.id}")
// } catch (e: Exception) {
// rollback()
// println("Migration fehlgeschlagen: ${migration.id} - ${e.message}")
// throw e
// }
// }
// }
// }
// }
//}
//
///**
// * Tabelle zur Verfolgung ausgeführter Migrationen.
// */
//object MigrationTable : Table("_migrations") {
// val id = varchar("id", 100)
// val version = long("version")
// val description = varchar("description", 255)
// val executedAt = timestamp("executed_at").defaultExpression(CurrentTimestamp)
//
// override val primaryKey = PrimaryKey(id)
//}
//
///**
// * Basisklasse für Datenbankmigrationen.
// */
//abstract class Migration(val version: Long, val description: String) {
// /**
// * Eindeutige ID der Migration, bestehend aus Version und Beschreibung.
// */
// val id: String = "V${version}_${description.replace("\\s+".toRegex(), "_")}"
//
// /**
// * Führt die Migration aus.
// */
// abstract fun up()
//}