Bounded Context에 새로운 도메인 이벤트 핸들러를 추가하거나 수정할 때 사용하세요.
도메인 이벤트 핸들러를 추가하거나 수정할 때 다음 규칙을 따르세요:
이벤트 핸들러는 이벤트를 수신하는 쪽 BC의 infrastructure/event/ 디렉토리에 위치합니다. 기능상 필요하지 않다면 이 디렉토리 안에는 파일이 존재하지 않을 수 있습니다.
{bounded-context}/
└── infrastructure/
└── event/
└── SomeEventHandler.kt
이벤트 관련 공통 인터페이스와 구현체는 common/ 레이어에 위치합니다:
common/event/DomainEvent.kt — 이벤트 래퍼 인터페이스common/event/DomainEventHandler.kt — 핸들러 인터페이스common/event/DomainEventPublisher.kt — 발행 인터페이스common/event/payload/ — 이벤트 페이로드 정의common/infrastructure/DomainEventDispatcher.kt — 중앙 디스패처 (Spring @TransactionalEventListener 기반)핸들러는 DomainEventHandler<T> 인터페이스를 구현하고, @Component로 등록합니다. DomainEventDispatcher가 자동으로 핸들러를 수집하여 이벤트를 분배합니다.
@Component
class SomeEventHandler(
private val someRepository: SomeRepository,
) : DomainEventHandler<SomeEventPayload.Completed> {
override fun handle(event: DomainEvent<SomeEventPayload.Completed>) {
val payload = event.payload
// 이벤트 처리 로직
}
override fun supports(payloadType: KClass<*>): Boolean =
payloadType == SomeEventPayload.Completed::class
}
이벤트 페이로드는 common/event/payload/ 디렉토리에 sealed interface로 정의합니다. 페이로드 필드에는 common/domain/의 VO 클래스를 사용합니다.
sealed interface SomeEventPayload {
data class Completed(
val someId: SomeId,
val memberId: MemberId,
) : SomeEventPayload
}
이벤트를 발행하는 쪽(주로 application service)에서는 DomainEventPublisher를 주입받아 사용합니다.
@Service
class SomeService(
private val eventPublisher: DomainEventPublisher,
) {
fun doSomething() {
// 비즈니스 로직 수행
eventPublisher.publish(SomeEventPayload.Completed(...))
}
}
DomainEventDispatcher는 TransactionPhase.BEFORE_COMMIT 단계에서 실행되므로, 핸들러의 작업은 발행자와 같은 트랜잭션 내에서 수행됩니다.