マイクロサービスっぽくサービスを分解していると、ログを串刺しでみたり分析したりするためにトラッキングIDを付与したいことがあるが、DDDの文脈からいうと、トラッキングIDはドメインの処理とは関係ないため、どうやって引回すか悩む。
例
↓のデータソースに渡す、TrackingIdをどうやって取得するか、という話です。
class SomeService {
someEntityRepository: SomeEntityRepository
someService(id: Id): Unit
}
class SomeEntity {
id: Id
someFunction(): SomeEvent
}
class SomeEvent {
id: Id
}
interface SomeEntityRepository {
findBy(id: Id): SomeEntity
save(event: SomeEvent): Unit
}
class SomeEntityRepositoryImpl {
entityDataSource: EntityDataSource
eventDataSource: EventDataSource
}
class EntityDataSource {
selectEntity(id: Id): (TrackingId, SomeEntity)
postEvent(trackingId: TrackingId, event: SomeEvent): Unit
}
SomeService *-down-> SomeEntityRepository
SomeService -down-> SomeEntity
SomeEntity -down-> SomeEvent
SomeEntityRepository <|-down- SomeEntityRepositoryImpl
SomeEntityRepositoryImpl *-down-> EntityDataSource
object SomeService {
def someService(id: Id): Unit {
val entity = someRepository.findBy(id)
val event = entity.someFunction
someRepository.save(event)
}
}
案1:Entityに属性を追加して引回す
case class SomeEntity(id: Id, trackingId: TrackingId) {
....
}
サービス層は変更ないですが、ドメイン層がTrackingIdに汚染される。ぶっちゃけTrackingIdだったらまぁって気がしてますが、EventDataSourceに引回す項目が増えてくると大変。
案2:サービス層で保存する
Repositoryを以下のようにして、サービス層で引回す
trait SomeEntityRepository {
def findBy(id: Id): (SomeEntity, TrackingId)
def save(event: SomeEvent, TrackingId): Unit
}
Entityは汚染を防げるが、Repositoryは汚染される。EntityがクリーンならテストしやすいのでRepositoryが汚れてもいいかーという割り切りがありならあり。複数のエンティティを操作する場合に、TrackingIdが入れ替わらないように注意が必要。
案3:EntityDataSourceへの参照をキャッシュしてRepositoryで再度参照
これが一番良さそう。キャッシュの有効期限とか、キャッシュの排他制御が大変。 超雑に書くと↓、スレッドセーフではない。
object SomeEntityRepositoryImpl {
val entityCache: TrieMap = TrieMap[Id, TrackingId]
val entityDataSource: EntityDataSource = EntityDataSource
val eventDataSource: EventDataSource = EventDataSource
def findBy(id: Id): SomeEntity = {
val res = entityDataSource.selectEntity(id)
entityCache.put(res._1.id, res._2)
res._2
}
def save(event: SomeEvent): Unit = {
val trackingId = entityCache.get(id)._2
eventDataSource.postEvent(event, trackingId)
}
}
SpringFrameworkなら、RequestScopeやJobScopeのオブジェクトが使えそう。