(vision) SCS/DDD

Service Discovery einführen
Consul als Service-Registry implementieren
Services für automatische Registrierung konfigurieren
Dynamisches Service-Routing im API-Gateway einrichten
Health-Checks für jeden Service implementieren
This commit is contained in:
2025-07-22 00:03:51 +02:00
parent 1ecac43d72
commit 8229e8e571
9 changed files with 212 additions and 54 deletions
@@ -157,7 +157,7 @@ private fun getRolePermissions(roles: List<UserRole>): List<Permission> {
roles.forEach { role ->
when (role) {
UserRole.ADMIN -> {
permissions.addAll(Permission.values())
permissions.addAll(Permission.entries.toTypedArray())
}
UserRole.VEREINS_ADMIN -> {
permissions.addAll(listOf(
@@ -354,7 +354,7 @@ val PipelineContext<Unit, ApplicationCall>.userAuthContext: UserAuthContext?
get() = call.principal<JWTPrincipal>()?.getUserAuthContext()
/**
* Application call extension to check if user has specific role.
* Application call extension to check if the user has a specific role.
*/
fun ApplicationCall.hasRole(role: UserRole): Boolean {
val authContext = principal<JWTPrincipal>()?.getUserAuthContext()
@@ -362,7 +362,7 @@ fun ApplicationCall.hasRole(role: UserRole): Boolean {
}
/**
* Application call extension to check if user has specific permission.
* Application call extension to check if the user has specific permission.
*/
fun ApplicationCall.hasPermission(permission: Permission): Boolean {
val authContext = principal<JWTPrincipal>()?.getUserAuthContext()
@@ -95,23 +95,23 @@ class CachingConfig(
}
/**
* Put a value in cache with TTL in minutes
* Put a value in a cache with TTL in minutes
*/
fun <T> put(cacheName: String, key: String, value: T, ttlMinutes: Long = defaultTtlMinutes) {
val stats = cacheStats.computeIfAbsent(cacheName) { CacheStats() }
stats.puts++
// Store in local cache
// Store in a local cache
val expiresAt = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(ttlMinutes)
val entry = CacheEntry(value as Any, expiresAt)
getCacheMap(cacheName)[key] = entry
}
/**
* Remove a value from cache
* Remove a value from the cache
*/
fun remove(cacheName: String, key: String) {
// Remove from local cache
// Remove from the local cache
getCacheMap(cacheName).remove(key)
}
@@ -136,7 +136,7 @@ class CachingConfig(
}
/**
* Get the appropriate cache map based on cache name
* Get the appropriate cache map based on the cache name
*/
private fun getCacheMap(cacheName: String): ConcurrentHashMap<String, CacheEntry<Any>> {
return when (cacheName) {
@@ -1,18 +1,13 @@
package at.mocode.gateway.config
import io.ktor.server.application.*
import io.ktor.server.plugins.*
import io.ktor.server.request.*
import io.ktor.server.routing.*
import io.ktor.util.*
import io.micrometer.core.instrument.Counter
import io.micrometer.core.instrument.MeterRegistry
import io.micrometer.core.instrument.Timer
import io.micrometer.core.instrument.binder.MeterBinder
import io.micrometer.prometheus.PrometheusMeterRegistry
import java.time.Duration
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit
/**
* Custom application metrics configuration.
@@ -4,19 +4,10 @@ import at.mocode.dto.base.ApiResponse
import at.mocode.shared.config.AppConfig
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.metrics.micrometer.*
import io.ktor.server.plugins.calllogging.*
import io.ktor.server.plugins.statuspages.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics
import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics
import io.micrometer.core.instrument.binder.system.ProcessorMetrics
import io.micrometer.prometheus.PrometheusConfig
import io.micrometer.prometheus.PrometheusMeterRegistry
import org.slf4j.event.Level
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
@@ -131,7 +131,7 @@ class ServiceDiscovery(
* @return The complete URL
*/
fun buildServiceUrl(instance: ServiceInstance, path: String): String {
val baseUrl = "http://${instance.host}:${instance.port}"
val baseUrl = "https://${instance.host}:${instance.port}"
return URI(baseUrl).resolve(path).toString()
}
@@ -143,7 +143,7 @@ class ServiceDiscovery(
*/
suspend fun isServiceHealthy(serviceName: String): Boolean {
try {
val response = httpClient.get("http://$consulHost:$consulPort/v1/health/service/$serviceName?passing=true")
val response = httpClient.get("https://$consulHost:$consulPort/v1/health/service/$serviceName?passing=true")
val responseBody = response.bodyAsText()
val healthyServices = Json.decodeFromString<List<Any>>(responseBody)
return healthyServices.isNotEmpty()
@@ -1,6 +1,5 @@
package at.mocode.gateway.plugins
import at.mocode.gateway.config.CachingConfig
import at.mocode.gateway.config.getCachingConfig
import io.ktor.http.*
import io.ktor.server.application.*
@@ -10,7 +9,6 @@ import io.ktor.util.pipeline.*
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.*
import kotlin.text.Charsets
/**
* Configures enhanced HTTP caching headers for the application.
@@ -190,7 +188,7 @@ suspend fun PipelineContext<Unit, ApplicationCall>.checkLastModifiedAndRespond(t
call.respond(HttpStatusCode.NotModified)
return true
}
} catch (e: Exception) {
} catch (_: Exception) {
// If we can't parse the date, ignore it
}
}
@@ -217,7 +215,7 @@ suspend fun <T> PipelineContext<Unit, ApplicationCall>.checkCacheAndRespond(
val application = call.application
val cachingConfig = try {
application.getCachingConfig()
} catch (e: Exception) {
} catch (_: Exception) {
return false
}