feat: ドメインイベントのサブスクライバー一覧を出力できるように

This commit is contained in:
usbharu 2024-09-13 13:40:52 +09:00
parent d74101a2b6
commit 1c80911365
Signed by: usbharu
GPG Key ID: 6556747BF94EEBC8
10 changed files with 151 additions and 66 deletions

View File

@ -30,7 +30,6 @@ services:
- "9200:9200" - "9200:9200"
volumes: volumes:
- elasticsearch-data:/usr/share/elasticsearch/data - elasticsearch-data:/usr/share/elasticsearch/data
restart: always
kibana: kibana:
image: docker.elastic.co/kibana/kibana:7.17.24 image: docker.elastic.co/kibana/kibana:7.17.24
container_name: kibana container_name: kibana
@ -42,7 +41,6 @@ services:
- ELASTICSEARCH_PASSWORD=Passw0rd - ELASTICSEARCH_PASSWORD=Passw0rd
depends_on: depends_on:
- elasticsearch - elasticsearch
restart: always
fluentd: fluentd:
build: ./fluentd build: ./fluentd

View File

@ -5,6 +5,7 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
interface DomainEventSubscriber { interface DomainEventSubscriber {
fun <T : DomainEventBody> subscribe(eventName: String, domainEventConsumer: DomainEventConsumer<T>) fun <T : DomainEventBody> subscribe(eventName: String, domainEventConsumer: DomainEventConsumer<T>)
fun getSubscribers(): Map<String, List<DomainEventConsumer<*>>>
} }
typealias DomainEventConsumer<T> = suspend (DomainEvent<T>) -> Unit typealias DomainEventConsumer<T> = suspend (DomainEvent<T>) -> Unit

View File

@ -3,19 +3,22 @@ package dev.usbharu.hideout.core.application.domainevent.subscribers
import dev.usbharu.hideout.core.domain.event.userdetail.UserDetailEvent import dev.usbharu.hideout.core.domain.event.userdetail.UserDetailEvent
import dev.usbharu.hideout.core.domain.event.userdetail.UserDetailEventBody import dev.usbharu.hideout.core.domain.event.userdetail.UserDetailEventBody
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@Component @Component
class RegisterLocalUserSetHomeTimelineSubscriber( class RegisterLocalUserSetHomeTimelineSubscriber(
private val domainEventSubscriber: DomainEventSubscriber, private val domainEventSubscriber: DomainEventSubscriber,
private val userRegisterHomeTimelineApplicationService: UserRegisterHomeTimelineApplicationService private val userRegisterHomeTimelineApplicationService: UserRegisterHomeTimelineApplicationService
) : Subscriber { ) : Subscriber, DomainEventConsumer<UserDetailEventBody> {
override fun init() { override fun init() {
domainEventSubscriber.subscribe<UserDetailEventBody>(UserDetailEvent.CREATE.eventName) { domainEventSubscriber.subscribe<UserDetailEventBody>(UserDetailEvent.CREATE.eventName, this)
userRegisterHomeTimelineApplicationService.execute( }
RegisterHomeTimeline(it.body.getUserDetail().id),
Anonymous override suspend fun invoke(p1: DomainEvent<UserDetailEventBody>) {
) userRegisterHomeTimelineApplicationService.execute(
} RegisterHomeTimeline(p1.body.getUserDetail().id),
Anonymous
)
} }
} }

View File

@ -5,17 +5,20 @@ import dev.usbharu.hideout.core.application.timeline.SetTimleineStore
import dev.usbharu.hideout.core.domain.event.timeline.TimelineEvent import dev.usbharu.hideout.core.domain.event.timeline.TimelineEvent
import dev.usbharu.hideout.core.domain.event.timeline.TimelineEventBody import dev.usbharu.hideout.core.domain.event.timeline.TimelineEventBody
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@Component @Component
class RegisterTimelineSetTimelineStoreSubscriber( class RegisterTimelineSetTimelineStoreSubscriber(
private val domainEventSubscriber: DomainEventSubscriber, private val domainEventSubscriber: DomainEventSubscriber,
private val setTimelineToTimelineStoreApplicationService: SetTimelineToTimelineStoreApplicationService private val setTimelineToTimelineStoreApplicationService: SetTimelineToTimelineStoreApplicationService
) : Subscriber { ) : Subscriber, DomainEventConsumer<TimelineEventBody> {
override fun init() { override fun init() {
domainEventSubscriber.subscribe<TimelineEventBody>(TimelineEvent.CREATE.eventName) { domainEventSubscriber.subscribe<TimelineEventBody>(TimelineEvent.CREATE.eventName, this)
setTimelineToTimelineStoreApplicationService.execute(SetTimleineStore(it.body.getTimelineId()), Anonymous) }
}
override suspend fun invoke(p1: DomainEvent<TimelineEventBody>) {
setTimelineToTimelineStoreApplicationService.execute(SetTimleineStore(p1.body.getTimelineId()), Anonymous)
} }
} }

View File

@ -1,12 +1,47 @@
package dev.usbharu.hideout.core.application.domainevent.subscribers package dev.usbharu.hideout.core.application.domainevent.subscribers
import org.slf4j.LoggerFactory
import org.springframework.boot.ApplicationArguments import org.springframework.boot.ApplicationArguments
import org.springframework.boot.ApplicationRunner import org.springframework.boot.ApplicationRunner
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@Component @Component
class SubscriberRunner(private val subscribers: List<Subscriber>) : ApplicationRunner { class SubscriberRunner(
private val subscribers: List<Subscriber>,
private val domainEventSubscriber: DomainEventSubscriber
) : ApplicationRunner {
override fun run(args: ApplicationArguments?) { override fun run(args: ApplicationArguments?) {
subscribers.forEach { it.init() } subscribers.forEach { it.init() }
if (logger.isDebugEnabled) {
val stringListMap = domainEventSubscriber.getSubscribers()
val header = """
|====== Domain Event Subscribers Report =====
|
|
|
""".trimMargin()
val value = stringListMap.map {
it.key + "\n\t" + it.value.joinToString("\n", "[", "]") { suspendFunction1 ->
suspendFunction1::class.qualifiedName.orEmpty()
}
}.joinToString("\n\n\n")
val footer = """
|
|
|
|===== Domain Event Subscribers Report =====
""".trimMargin()
logger.debug("{}{}{}", header, value, footer)
}
}
companion object {
private val logger = LoggerFactory.getLogger(SubscriberRunner::class.java)
} }
} }

View File

@ -5,16 +5,19 @@ import dev.usbharu.hideout.core.application.timeline.TimelineAddPostApplicationS
import dev.usbharu.hideout.core.domain.event.post.PostEvent import dev.usbharu.hideout.core.domain.event.post.PostEvent
import dev.usbharu.hideout.core.domain.event.post.PostEventBody import dev.usbharu.hideout.core.domain.event.post.PostEventBody
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@Component @Component
class TimelinePostCreateSubscriber( class TimelinePostCreateSubscriber(
private val timelineAddPostApplicationService: TimelineAddPostApplicationService, private val timelineAddPostApplicationService: TimelineAddPostApplicationService,
private val domainEventSubscriber: DomainEventSubscriber, private val domainEventSubscriber: DomainEventSubscriber,
) : Subscriber { ) : Subscriber, DomainEventConsumer<PostEventBody> {
override fun init() { override fun init() {
domainEventSubscriber.subscribe<PostEventBody>(PostEvent.CREATE.eventName) { domainEventSubscriber.subscribe<PostEventBody>(PostEvent.CREATE.eventName, this)
timelineAddPostApplicationService.execute(AddPost(it.body.getPostId()), Anonymous) }
}
override suspend fun invoke(p1: DomainEvent<PostEventBody>) {
timelineAddPostApplicationService.execute(AddPost(p1.body.getPostId()), Anonymous)
} }
} }

View File

@ -7,6 +7,7 @@ import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEvent
import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEventBody import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEventBody
import dev.usbharu.hideout.core.domain.model.timelinerelationship.Visible import dev.usbharu.hideout.core.domain.model.timelinerelationship.Visible
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@ -15,28 +16,31 @@ class TimelineRelationshipFollowSubscriber(
private val userAddTimelineRelationshipApplicationService: UserAddTimelineRelationshipApplicationService, private val userAddTimelineRelationshipApplicationService: UserAddTimelineRelationshipApplicationService,
private val userDetailRepository: UserDetailRepository, private val userDetailRepository: UserDetailRepository,
private val domainEventSubscriber: DomainEventSubscriber private val domainEventSubscriber: DomainEventSubscriber
) : Subscriber { ) : Subscriber, DomainEventConsumer<RelationshipEventBody> {
override fun init() { override fun init() {
domainEventSubscriber.subscribe<RelationshipEventBody>(RelationshipEvent.ACCEPT_FOLLOW.eventName) { domainEventSubscriber.subscribe<RelationshipEventBody>(RelationshipEvent.ACCEPT_FOLLOW.eventName, this)
val relationship = it.body.getRelationship() }
val userDetail = userDetailRepository.findByActorId(relationship.actorId.id)
?: throw InternalServerException("Userdetail ${relationship.actorId} not found by actorid.")
if (userDetail.homeTimelineId == null) {
logger.warn("Home timeline for ${relationship.actorId} is not found")
return@subscribe
}
@Suppress("UnsafeCallOnNullableType")
userAddTimelineRelationshipApplicationService.execute( override suspend fun invoke(p1: DomainEvent<RelationshipEventBody>) {
AddTimelineRelationship( val relationship = p1.body.getRelationship()
userDetail.homeTimelineId!!, val userDetail = userDetailRepository.findByActorId(relationship.actorId.id)
relationship.targetActorId, ?: throw InternalServerException("Userdetail ${relationship.actorId} not found by actorid.")
Visible.FOLLOWERS if (userDetail.homeTimelineId == null) {
), logger.warn("Home timeline for ${relationship.actorId} is not found")
it.body.principal return
)
} }
@Suppress("UnsafeCallOnNullableType")
userAddTimelineRelationshipApplicationService.execute(
AddTimelineRelationship(
userDetail.homeTimelineId!!,
relationship.targetActorId,
Visible.FOLLOWERS
),
p1.body.principal
)
} }
companion object { companion object {

View File

@ -6,6 +6,7 @@ import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEvent
import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEventBody import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEventBody
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipRepository import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipRepository
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@ -15,29 +16,31 @@ class TimelineRelationshipUnfollowSubscriber(
private val userRemoveTimelineRelationshipApplicationService: UserRemoveTimelineRelationshipApplicationService, private val userRemoveTimelineRelationshipApplicationService: UserRemoveTimelineRelationshipApplicationService,
private val userDetailRepository: UserDetailRepository, private val userDetailRepository: UserDetailRepository,
private val timelineRelationshipRepository: TimelineRelationshipRepository, private val timelineRelationshipRepository: TimelineRelationshipRepository,
) : Subscriber { ) : Subscriber, DomainEventConsumer<RelationshipEventBody> {
override fun init() { override fun init() {
domainEventSubscriber.subscribe<RelationshipEventBody>(RelationshipEvent.UNFOLLOW.eventName) { domainEventSubscriber.subscribe<RelationshipEventBody>(RelationshipEvent.UNFOLLOW.eventName, this)
val relationship = it.body.getRelationship() }
val userDetail = userDetailRepository.findByActorId(relationship.actorId.id) ?: throw IllegalStateException(
"UserDetail ${relationship.actorId} not found by actorId."
)
if (userDetail.homeTimelineId == null) {
logger.warn("HomeTimeline for ${userDetail.id} not found.")
return@subscribe
}
val timelineRelationship = timelineRelationshipRepository.findByTimelineIdAndActorId( override suspend fun invoke(p1: DomainEvent<RelationshipEventBody>) {
userDetail.homeTimelineId!!, val relationship = p1.body.getRelationship()
relationship.targetActorId val userDetail = userDetailRepository.findByActorId(relationship.actorId.id) ?: throw IllegalStateException(
) "UserDetail ${relationship.actorId} not found by actorId."
?: throw IllegalStateException("TimelineRelationship ${userDetail.homeTimelineId} to ${relationship.targetActorId} not found by timelineId and ActorId") )
if (userDetail.homeTimelineId == null) {
userRemoveTimelineRelationshipApplicationService.execute( logger.warn("HomeTimeline for ${userDetail.id} not found.")
RemoveTimelineRelationship(timelineRelationship.id), return
it.body.principal
)
} }
val timelineRelationship = timelineRelationshipRepository.findByTimelineIdAndActorId(
userDetail.homeTimelineId!!,
relationship.targetActorId
)
?: throw IllegalStateException("TimelineRelationship ${userDetail.homeTimelineId} to ${relationship.targetActorId} not found by timelineId and ActorId")
userRemoveTimelineRelationshipApplicationService.execute(
RemoveTimelineRelationship(timelineRelationship.id),
p1.body.principal
)
} }
companion object { companion object {

View File

@ -18,7 +18,9 @@ package dev.usbharu.hideout.core.infrastructure.springframework.domainevent
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
import dev.usbharu.hideout.core.infrastructure.springframework.ApplicationRequestLogInterceptor
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.slf4j.MDC
import org.springframework.context.ApplicationEventPublisher import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@ -27,10 +29,18 @@ class SpringFrameworkDomainEventPublisher(private val applicationEventPublisher:
DomainEventPublisher { DomainEventPublisher {
override suspend fun publishEvent(domainEvent: DomainEvent<*>) { override suspend fun publishEvent(domainEvent: DomainEvent<*>) {
logger.trace("Publish ${domainEvent.id} ${domainEvent.name}") logger.trace("Publish ${domainEvent.id} ${domainEvent.name}")
applicationEventPublisher.publishEvent(domainEvent)
val requestId: String? = MDC.get(ApplicationRequestLogInterceptor.requestId)
val springDomainEvent = SpringDomainEvent(
requestId, domainEvent
)
applicationEventPublisher.publishEvent(springDomainEvent)
} }
companion object { companion object {
private val logger = LoggerFactory.getLogger(SpringFrameworkDomainEventPublisher::class.java) private val logger = LoggerFactory.getLogger(SpringFrameworkDomainEventPublisher::class.java)
} }
} }
data class SpringDomainEvent(val requestId: String?, val domainEvent: DomainEvent<*>)

View File

@ -2,9 +2,14 @@ package dev.usbharu.hideout.core.infrastructure.springframework.domainevent
import dev.usbharu.hideout.core.application.domainevent.subscribers.DomainEventConsumer import dev.usbharu.hideout.core.application.domainevent.subscribers.DomainEventConsumer
import dev.usbharu.hideout.core.application.domainevent.subscribers.DomainEventSubscriber import dev.usbharu.hideout.core.application.domainevent.subscribers.DomainEventSubscriber
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
import dev.usbharu.hideout.core.infrastructure.springframework.ApplicationRequestLogInterceptor.Companion.requestId
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.slf4j.MDCContext
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.slf4j.MDC
import org.springframework.context.event.EventListener import org.springframework.context.event.EventListener
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@ -17,15 +22,35 @@ class SpringFrameworkDomainEventSubscriber : DomainEventSubscriber {
map.getOrPut(eventName) { mutableListOf() }.add(domainEventConsumer as DomainEventConsumer<*>) map.getOrPut(eventName) { mutableListOf() }.add(domainEventConsumer as DomainEventConsumer<*>)
} }
override fun getSubscribers(): Map<String, List<DomainEventConsumer<*>>> {
return map
}
@EventListener @EventListener
suspend fun onDomainEventPublished(domainEvent: DomainEvent<*>) { suspend fun onDomainEventPublished(domainEvent: SpringDomainEvent) {
logger.trace("Domain Event Published: $domainEvent")
map[domainEvent.name]?.forEach { logger.debug(
try { "Domain Event Published: {} id: {} requestId: {}",
it.invoke(domainEvent) domainEvent.domainEvent.name,
} catch (@Suppress("TooGenericExceptionCaught") e: Exception) { domainEvent.domainEvent.id,
logger.error("", e) domainEvent.requestId
} )
coroutineScope {
map[domainEvent.domainEvent.name]?.map {
async(MDCContext()) {
try {
MDC.put(requestId, domainEvent.requestId)
it.invoke(domainEvent.domainEvent)
} catch (@Suppress("TooGenericExceptionCaught") e: Exception) {
logger.warn("", e)
null
} finally {
MDC.remove(requestId)
}
}
}?.awaitAll()
} }
} }