refactor: Migrate from monolithic to modular architecture
1. **Dokumentation der Architektur:**
- Vervollständigen Sie die C4-Diagramme im docs-Verzeichnis
- Dokumentieren Sie die wichtigsten Architekturentscheidungen in ADRs
2. **Redis-Integration finalisieren:**
- Implementieren Sie die verteilte Cache-Lösung für die Offline-Fähigkeit
- Nutzen Sie Redis Streams für das Event-Sourcing
This commit is contained in:
+69
@@ -0,0 +1,69 @@
|
||||
package at.mocode.infrastructure.cache.api
|
||||
|
||||
import java.time.Duration
|
||||
|
||||
/**
|
||||
* Configuration for the distributed cache.
|
||||
*/
|
||||
interface CacheConfiguration {
|
||||
/**
|
||||
* Default time-to-live for cache entries.
|
||||
* If null, entries do not expire by default.
|
||||
*/
|
||||
val defaultTtl: Duration?
|
||||
|
||||
/**
|
||||
* Maximum number of entries to store in the local cache.
|
||||
* If null, there is no limit.
|
||||
*/
|
||||
val localCacheMaxSize: Int?
|
||||
|
||||
/**
|
||||
* Whether to enable offline mode.
|
||||
* If true, the cache will store entries locally when offline
|
||||
* and synchronize them when online.
|
||||
*/
|
||||
val offlineModeEnabled: Boolean
|
||||
|
||||
/**
|
||||
* How often to attempt synchronization in offline mode.
|
||||
*/
|
||||
val synchronizationInterval: Duration
|
||||
|
||||
/**
|
||||
* Maximum age of entries to keep in the local cache when offline.
|
||||
* If null, entries do not expire when offline.
|
||||
*/
|
||||
val offlineEntryMaxAge: Duration?
|
||||
|
||||
/**
|
||||
* Prefix to add to all cache keys.
|
||||
* This can be used to namespace cache entries.
|
||||
*/
|
||||
val keyPrefix: String
|
||||
|
||||
/**
|
||||
* Whether to compress cache entries.
|
||||
*/
|
||||
val compressionEnabled: Boolean
|
||||
|
||||
/**
|
||||
* Threshold in bytes above which to compress cache entries.
|
||||
* Only used if compressionEnabled is true.
|
||||
*/
|
||||
val compressionThreshold: Int
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of CacheConfiguration.
|
||||
*/
|
||||
data class DefaultCacheConfiguration(
|
||||
override val defaultTtl: Duration? = Duration.ofHours(1),
|
||||
override val localCacheMaxSize: Int? = 10000,
|
||||
override val offlineModeEnabled: Boolean = true,
|
||||
override val synchronizationInterval: Duration = Duration.ofMinutes(5),
|
||||
override val offlineEntryMaxAge: Duration? = Duration.ofDays(7),
|
||||
override val keyPrefix: String = "",
|
||||
override val compressionEnabled: Boolean = true,
|
||||
override val compressionThreshold: Int = 1024
|
||||
) : CacheConfiguration
|
||||
Vendored
+96
@@ -0,0 +1,96 @@
|
||||
package at.mocode.infrastructure.cache.api
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* Represents an entry in the cache with metadata for offline capability.
|
||||
*
|
||||
* @param key The key of the cache entry
|
||||
* @param value The value stored in the cache
|
||||
* @param createdAt When the entry was created
|
||||
* @param expiresAt When the entry expires, or null if it doesn't expire
|
||||
* @param lastModifiedAt When the entry was last modified
|
||||
* @param isDirty Whether the entry has been modified locally and needs to be synchronized
|
||||
* @param isLocal Whether the entry is only stored locally (not yet synchronized)
|
||||
*/
|
||||
data class CacheEntry<T : Any>(
|
||||
val key: String,
|
||||
val value: T,
|
||||
val createdAt: Instant = Instant.now(),
|
||||
val expiresAt: Instant? = null,
|
||||
val lastModifiedAt: Instant = Instant.now(),
|
||||
val isDirty: Boolean = false,
|
||||
val isLocal: Boolean = false
|
||||
) {
|
||||
/**
|
||||
* Checks if the entry is expired.
|
||||
*
|
||||
* @return true if the entry is expired, false otherwise
|
||||
*/
|
||||
fun isExpired(): Boolean {
|
||||
return expiresAt?.isBefore(Instant.now()) ?: false
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entry with the isDirty flag set to true.
|
||||
*
|
||||
* @return A new CacheEntry with isDirty set to true
|
||||
*/
|
||||
fun markDirty(): CacheEntry<T> {
|
||||
return copy(
|
||||
isDirty = true,
|
||||
lastModifiedAt = Instant.now()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entry with the isDirty flag set to false.
|
||||
*
|
||||
* @return A new CacheEntry with isDirty set to false
|
||||
*/
|
||||
fun markClean(): CacheEntry<T> {
|
||||
return copy(
|
||||
isDirty = false,
|
||||
isLocal = false,
|
||||
lastModifiedAt = Instant.now()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entry with the isLocal flag set to true.
|
||||
*
|
||||
* @return A new CacheEntry with isLocal set to true
|
||||
*/
|
||||
fun markLocal(): CacheEntry<T> {
|
||||
return copy(
|
||||
isLocal = true,
|
||||
lastModifiedAt = Instant.now()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entry with an updated value.
|
||||
*
|
||||
* @param newValue The new value
|
||||
* @return A new CacheEntry with the updated value
|
||||
*/
|
||||
fun updateValue(newValue: T): CacheEntry<T> {
|
||||
return copy(
|
||||
value = newValue,
|
||||
lastModifiedAt = Instant.now()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entry with an updated expiration time.
|
||||
*
|
||||
* @param newExpiresAt The new expiration time
|
||||
* @return A new CacheEntry with the updated expiration time
|
||||
*/
|
||||
fun updateExpiration(newExpiresAt: Instant?): CacheEntry<T> {
|
||||
return copy(
|
||||
expiresAt = newExpiresAt,
|
||||
lastModifiedAt = Instant.now()
|
||||
)
|
||||
}
|
||||
}
|
||||
infrastructure/cache/cache-api/src/main/kotlin/at/mocode/infrastructure/cache/api/CacheSerializer.kt
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
package at.mocode.infrastructure.cache.api
|
||||
|
||||
/**
|
||||
* Interface for serializing and deserializing cache entries.
|
||||
*/
|
||||
interface CacheSerializer {
|
||||
/**
|
||||
* Serializes a value to a byte array.
|
||||
*
|
||||
* @param value The value to serialize
|
||||
* @return The serialized value as a byte array
|
||||
*/
|
||||
fun <T : Any> serialize(value: T): ByteArray
|
||||
|
||||
/**
|
||||
* Deserializes a byte array to a value.
|
||||
*
|
||||
* @param bytes The byte array to deserialize
|
||||
* @param clazz The class of the value to deserialize to
|
||||
* @return The deserialized value
|
||||
*/
|
||||
fun <T : Any> deserialize(bytes: ByteArray, clazz: Class<T>): T
|
||||
|
||||
/**
|
||||
* Serializes a cache entry to a byte array.
|
||||
*
|
||||
* @param entry The cache entry to serialize
|
||||
* @return The serialized cache entry as a byte array
|
||||
*/
|
||||
fun <T : Any> serializeEntry(entry: CacheEntry<T>): ByteArray
|
||||
|
||||
/**
|
||||
* Deserializes a byte array to a cache entry.
|
||||
*
|
||||
* @param bytes The byte array to deserialize
|
||||
* @param valueClass The class of the value in the cache entry
|
||||
* @return The deserialized cache entry
|
||||
*/
|
||||
fun <T : Any> deserializeEntry(bytes: ByteArray, valueClass: Class<T>): CacheEntry<T>
|
||||
|
||||
/**
|
||||
* Compresses a byte array.
|
||||
*
|
||||
* @param bytes The byte array to compress
|
||||
* @return The compressed byte array
|
||||
*/
|
||||
fun compress(bytes: ByteArray): ByteArray
|
||||
|
||||
/**
|
||||
* Decompresses a byte array.
|
||||
*
|
||||
* @param bytes The byte array to decompress
|
||||
* @return The decompressed byte array
|
||||
*/
|
||||
fun decompress(bytes: ByteArray): ByteArray
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package at.mocode.infrastructure.cache.api
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* Represents the connection status of the cache.
|
||||
*/
|
||||
enum class ConnectionState {
|
||||
/**
|
||||
* The cache is connected to the remote server.
|
||||
*/
|
||||
CONNECTED,
|
||||
|
||||
/**
|
||||
* The cache is disconnected from the remote server.
|
||||
*/
|
||||
DISCONNECTED,
|
||||
|
||||
/**
|
||||
* The cache is attempting to reconnect to the remote server.
|
||||
*/
|
||||
RECONNECTING
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for tracking the connection status of the cache.
|
||||
*/
|
||||
interface ConnectionStatusTracker {
|
||||
/**
|
||||
* Gets the current connection state.
|
||||
*
|
||||
* @return The current connection state
|
||||
*/
|
||||
fun getConnectionState(): ConnectionState
|
||||
|
||||
/**
|
||||
* Gets the time when the connection state last changed.
|
||||
*
|
||||
* @return The time when the connection state last changed
|
||||
*/
|
||||
fun getLastStateChangeTime(): Instant
|
||||
|
||||
/**
|
||||
* Registers a listener to be notified when the connection state changes.
|
||||
*
|
||||
* @param listener The listener to register
|
||||
*/
|
||||
fun registerConnectionListener(listener: ConnectionStateListener)
|
||||
|
||||
/**
|
||||
* Unregisters a connection state listener.
|
||||
*
|
||||
* @param listener The listener to unregister
|
||||
*/
|
||||
fun unregisterConnectionListener(listener: ConnectionStateListener)
|
||||
|
||||
/**
|
||||
* Checks if the cache is currently connected.
|
||||
*
|
||||
* @return true if the cache is connected, false otherwise
|
||||
*/
|
||||
fun isConnected(): Boolean = getConnectionState() == ConnectionState.CONNECTED
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for connection state changes.
|
||||
*/
|
||||
interface ConnectionStateListener {
|
||||
/**
|
||||
* Called when the connection state changes.
|
||||
*
|
||||
* @param newState The new connection state
|
||||
* @param timestamp The time when the state changed
|
||||
*/
|
||||
fun onConnectionStateChanged(newState: ConnectionState, timestamp: Instant)
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
package at.mocode.infrastructure.cache.api
|
||||
|
||||
import java.time.Duration
|
||||
|
||||
/**
|
||||
* Interface for a distributed cache that supports offline capability.
|
||||
* This cache can be used to store and retrieve data across multiple instances
|
||||
* and provides mechanisms for offline operation.
|
||||
*/
|
||||
interface DistributedCache {
|
||||
/**
|
||||
* Retrieves a value from the cache.
|
||||
*
|
||||
* @param key The key to retrieve
|
||||
* @return The value associated with the key, or null if not found
|
||||
*/
|
||||
fun <T : Any> get(key: String, clazz: Class<T>): T?
|
||||
|
||||
/**
|
||||
* Stores a value in the cache with an optional time-to-live.
|
||||
*
|
||||
* @param key The key to store the value under
|
||||
* @param value The value to store
|
||||
* @param ttl Optional time-to-live for the cache entry
|
||||
*/
|
||||
fun <T : Any> set(key: String, value: T, ttl: Duration? = null)
|
||||
|
||||
/**
|
||||
* Removes a value from the cache.
|
||||
*
|
||||
* @param key The key to remove
|
||||
*/
|
||||
fun delete(key: String)
|
||||
|
||||
/**
|
||||
* Checks if a key exists in the cache.
|
||||
*
|
||||
* @param key The key to check
|
||||
* @return true if the key exists, false otherwise
|
||||
*/
|
||||
fun exists(key: String): Boolean
|
||||
|
||||
/**
|
||||
* Retrieves multiple values from the cache.
|
||||
*
|
||||
* @param keys The keys to retrieve
|
||||
* @return A map of keys to values, with missing keys omitted
|
||||
*/
|
||||
fun <T : Any> multiGet(keys: Collection<String>, clazz: Class<T>): Map<String, T>
|
||||
|
||||
/**
|
||||
* Stores multiple values in the cache with an optional time-to-live.
|
||||
*
|
||||
* @param entries The key-value pairs to store
|
||||
* @param ttl Optional time-to-live for the cache entries
|
||||
*/
|
||||
fun <T : Any> multiSet(entries: Map<String, T>, ttl: Duration? = null)
|
||||
|
||||
/**
|
||||
* Removes multiple values from the cache.
|
||||
*
|
||||
* @param keys The keys to remove
|
||||
*/
|
||||
fun multiDelete(keys: Collection<String>)
|
||||
|
||||
/**
|
||||
* Synchronizes the local cache with the distributed cache.
|
||||
* This is used to ensure that the local cache is up-to-date with the distributed cache
|
||||
* after being offline.
|
||||
*
|
||||
* @param keys Optional collection of keys to synchronize. If null, all keys are synchronized.
|
||||
*/
|
||||
fun synchronize(keys: Collection<String>? = null)
|
||||
|
||||
/**
|
||||
* Marks a key as dirty, indicating that it has been modified locally
|
||||
* and needs to be synchronized with the distributed cache.
|
||||
*
|
||||
* @param key The key to mark as dirty
|
||||
*/
|
||||
fun markDirty(key: String)
|
||||
|
||||
/**
|
||||
* Gets all keys that have been marked as dirty.
|
||||
*
|
||||
* @return A collection of dirty keys
|
||||
*/
|
||||
fun getDirtyKeys(): Collection<String>
|
||||
|
||||
/**
|
||||
* Clears all entries from the cache.
|
||||
*/
|
||||
fun clear()
|
||||
}
|
||||
Reference in New Issue
Block a user