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:
@@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+100
-96
@@ -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()
|
||||
//}
|
||||
|
||||
Reference in New Issue
Block a user