Skip to content

Commit

Permalink
[Feat/#452] 이벤트 라이브러리 구현 (#463)
Browse files Browse the repository at this point in the history
* feat: 이벤트 모듈 추가

* feat: 이벤트 클래스 구현

* feat: 메시지 클래스 구현

* feat: LocalMessage 환경 구현

* feat: EventTestFixtures 구현

* feat: EventRule 구현

* test: MessageTest 구현

* test: LocalMessageBroadCasterTest 구현

* fix: message가 아닌 method로 검증하고 있는 문제 해결

* fix: 불필요하게 json으로 변화하는 과정 제거

* refactor: LocalMessageBroadCaster 검증에서 한줄에 message와 messageReverseRelay가 나오는지 확인하도록 수정
  • Loading branch information
belljun3395 authored Dec 22, 2024
1 parent bec0188 commit af709c0
Show file tree
Hide file tree
Showing 28 changed files with 1,080 additions and 1 deletion.
11 changes: 11 additions & 0 deletions event/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
tasks.getByName("bootJar") {
enabled = false
}

tasks.getByName("jar") {
enabled = true
}

dependencies {
implementation("org.springframework.boot:spring-boot-starter-json")
}
23 changes: 23 additions & 0 deletions event/src/main/kotlin/event/Event.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package event

/**
* Event
*
* 이벤트
*
* @property eventId 이벤트 식별자
* @property eventType 이벤트 행위 타입
* @property eventTime 이벤트 발행 시간 (기본값: 현재 시간)
*/
abstract class Event(
protected val eventId: String = EventUtils.generateEventId(),
protected val eventType: String,
protected val eventTime: Long = System.currentTimeMillis(),
) {
/**
* Get data
*
* @return 이벤트 데이터
*/
abstract fun getData(): Map<String, Any>
}
14 changes: 14 additions & 0 deletions event/src/main/kotlin/event/EventDetails.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package event

/**
* Event details
*
* 이벤트 상세 정보
*
* @property outBox 이벤트 외부 발행 여부
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class EventDetails(
val outBox: Boolean = false,
)
18 changes: 18 additions & 0 deletions event/src/main/kotlin/event/EventHandler.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package event

/**
* Event handler
*
* 이벤트 핸들러
*
* @param T Event 클래스를 상속 받은 클래스
* @see Event
*/
interface EventHandler<T : Event> {
/**
* Handle event
*
* @param event 이벤트
*/
fun handle(event: T)
}
15 changes: 15 additions & 0 deletions event/src/main/kotlin/event/EventRePlayer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package event

/**
* Event RePlayer
*
* 이벤트 재생기
*/
abstract class EventRePlayer {
/**
* Replay
*
* 정상 처리 되지 않은 이벤트를 재생합니다.
*/
abstract fun replay()
}
26 changes: 26 additions & 0 deletions event/src/main/kotlin/event/EventUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package event

import java.util.*

/**
* Is out box extension for Event Class
*
* 이벤트 외부 발행 여부를 확인합니다.
*
* @return 이벤트 외부 발행 여부
* @see Event
*/
fun Event.isOutBox(): Boolean = this::class.annotations.any { annotation -> annotation is EventDetails && annotation.outBox }

class EventUtils {
companion object {
/**
* Generate event id
*
* 이벤트 ID를 생성합니다.
*
* @return 이벤트 ID
*/
fun generateEventId(): String = UUID.randomUUID().toString()
}
}
83 changes: 83 additions & 0 deletions event/src/main/kotlin/event/TimeOutEvent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package event

import org.springframework.context.ApplicationEventPublisher

/**
* Time out event
*
* 시간이 설정된 이벤트
*
* @param expiredTime 만료 시간
* @param completed 완료 여부(기본값: false)
* @param eventPublisher 이벤트 발행자
*
* @see TimeExpiredEvent 시간 만료 이벤트
*/
abstract class TimeOutEvent(
eventId: String = EventUtils.generateEventId(),
eventType: String,
eventTime: Long = System.currentTimeMillis(),
protected val expiredTime: Long,
protected var completed: Boolean = false,
protected val eventPublisher: ApplicationEventPublisher,
) : Event(
eventId,
eventType,
eventTime,
),
Runnable {
/**
* Complete event
*
* 이벤트 완료 처리
*/
fun complete() {
completed = true
}

/**
* Is expired
*
* @param time 시간 (기본값: 현재 시간)
* @return 이벤트 만료 여부
*/
fun isExpired(time: Long = System.currentTimeMillis()): Boolean = !completed && time > expiredTime

/**
* Run
*
* 이벤트 만료시 실행
*/
override fun run() {
publishExpiredTimeEvent()
}

/**
* Publish expired time event
*
* 시간 만료 이벤트 발행
*/
private fun publishExpiredTimeEvent() {
eventPublisher.publishEvent(timeExpiredEvent())
}

/**
* Expired time event
*
* 시간 만료 이벤트
*/
abstract fun timeExpiredEvent(): TimeExpiredEvent
}

/**
* Expired time event
*/
abstract class TimeExpiredEvent(
eventId: String,
eventType: String,
eventTime: Long,
) : Event(
eventId,
eventType,
eventTime,
)
13 changes: 13 additions & 0 deletions event/src/main/kotlin/event/config/EventConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package event.config

import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration

@Configuration
@ComponentScan(basePackages = [EventConfig.BASE_PACKAGE])
class EventConfig {
companion object {
const val BASE_PACKAGE = "event"
const val BEAN_NAME_PREFIX = "event"
}
}
30 changes: 30 additions & 0 deletions event/src/main/kotlin/event/message/Message.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package event.message

import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty

/**
* Message
*
* 외부 시스템과의 통신을 위한 메시지
*
* @property payload 메시지 페이로드
*/
abstract class Message
@JsonCreator
constructor(
@JsonProperty("payload") var payload: MessagePayload?,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false

other as Message

if (payload != other.payload) return false

return true
}

override fun hashCode(): Int = payload?.hashCode() ?: 0
}
27 changes: 27 additions & 0 deletions event/src/main/kotlin/event/message/MessageMapper.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package event.message

import event.Event
import java.util.Optional

/**
* Message mapper
*
* 이벤트를 메시지로 변환하기 위한 매퍼
*
* @param T Event 클래스를 상속 받은 클래스
* @param R Message 클래스를 상속 받은 클래스
*
* @see Event
* @see Message
*/
abstract class MessageMapper<T : Event, R : Message> {
/**
* Map
*
* 이벤트를 메시지로 변환
*
* @param event 이벤트
* @return Optional<Message> 메시지
*/
abstract fun map(event: T): Optional<R>
}
26 changes: 26 additions & 0 deletions event/src/main/kotlin/event/message/MessagePayload.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package event.message

import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty

/**
* Message payload
*
* 메시지 페이로드
*
* @property eventId 이벤트 식별자
* @property eventType 이벤트 행위 타입
* @property eventTime 이벤트 발행 시간
* @property data 이벤트 데이터
*
* @see event.Event
*/

data class MessagePayload
@JsonCreator
constructor(
@JsonProperty("eventId") val eventId: String?,
@JsonProperty("eventType") val eventType: String?,
@JsonProperty("eventTime") val eventTime: Long?,
@JsonProperty("data") val data: Map<String, Any>?,
)
18 changes: 18 additions & 0 deletions event/src/main/kotlin/event/message/MessageRelay.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package event.message

/**
* Message relay
*
* 내부 이벤트를 외부에 메시지로 전달하기 위한 메시지 릴레이
*
* @param T Message 클래스를 상속 받은 클래스
* @see MessageSender 메시지를 전달하기 위한 메시지 발신자
*/
interface MessageRelay<T : Message> {
/**
* Publish
*
* @param message 외부로 전달할 메시지
*/
fun publish(message: T)
}
46 changes: 46 additions & 0 deletions event/src/main/kotlin/event/message/MessageReverseRelay.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package event.message

import event.Event

/**
* Message reverse relay
*
* 외부 시스템에서 전달 받은 메시지를 내부에 이벤트로 전달하기 위한 메시지 리버스 릴레이
*
* `Bar 메시지를 수신하여 Foo 이벤트로 변환하여 내부로 전달하는 예시`
*
* ```kotlin
* class FooBarLocalMessageReverseRelay(
* private val messageSender: MessageSender<BarMessage>,
* private val messageMapper: MessageMapper<FooEvent, BarMessage>,
* private val eventPublisher: ApplicationEventPublisher,
* ) : MessageReverseRelay<FooEvent> {
*
* override fun publish(event: FooEvent) {
* applicationEventPublisher.publishEvent(event)
* }
*
* @EventListener
* @LocalSubscribeMessage(topic ="bar")
* fun onApplicationEvent(message: BarMessage) {
* messageMapper.map(message).ifPresent { publish(it) }
* }
* }
* ```
* @param T Event 클래스를 상속 받은 클래스
*
* @see MessageSender 메시지를 전달하기 위한 메시지 발신자
* @see MessageMapper 메시지를 이벤트로 변환하기 위한 메시지 매퍼
* @see event.message.local.LocalSubscribeMessage 내부 메시지 수신을 위한 어노테이션
*
* @see org.springframework.context.event.EventListener 이벤트 리스너 (기본 설정 메서드 명: onApplicationEvent)
* @see org.springframework.context.ApplicationEventPublisher
*/
interface MessageReverseRelay<T : Event> {
/**
* Publish
*
* @param event 내부로 전달할 이벤트
*/
fun publish(event: T)
}
17 changes: 17 additions & 0 deletions event/src/main/kotlin/event/message/MessageSender.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package event.message

/**
* Message sender
*
* 외부 시스템으로 메시지를 전달하기 위한 메시지 발신자
*
* @param T Message 클래스를 상속 받은 클래스
*/
interface MessageSender<T : Message> {
/**
* Send
*
* @param message 전달할 메시지
*/
fun send(message: T)
}
Loading

0 comments on commit af709c0

Please sign in to comment.