-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: 도메인 이벤트 구현 * refactor: 도메인 이벤트 구현
- Loading branch information
1 parent
5369cd1
commit 0d16ab8
Showing
13 changed files
with
392 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
domain/crm/src/main/kotlin/com/few/crm/email/domain/SentEmail.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.few.crm.email.domain | ||
|
||
import com.few.crm.email.event.send.EmailSentEvent | ||
import org.springframework.data.domain.AbstractAggregateRoot | ||
|
||
class SentEmail( | ||
private val userExternalId: String, | ||
private val emailBody: String, | ||
private val destination: String, | ||
private val emailMessageId: String, | ||
private val eventType: EmailSendEventType = EmailSendEventType.SEND, | ||
) : AbstractAggregateRoot<SentEmail>() { | ||
init { | ||
registerEvent( | ||
EmailSentEvent( | ||
userExternalId = userExternalId, | ||
emailBody = emailBody, | ||
messageId = emailMessageId, | ||
destination = destination, | ||
eventType = eventType.name, | ||
), | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
library/event/src/main/kotlin/event/domain/AfterEventPublication.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package event.domain | ||
|
||
import org.springframework.aot.hint.annotation.Reflective | ||
|
||
@Reflective | ||
@Retention(AnnotationRetention.RUNTIME) | ||
@Target( | ||
AnnotationTarget.FUNCTION, | ||
AnnotationTarget.PROPERTY_GETTER, | ||
AnnotationTarget.PROPERTY_SETTER, | ||
AnnotationTarget.ANNOTATION_CLASS, | ||
) | ||
annotation class AfterEventPublication |
37 changes: 37 additions & 0 deletions
37
library/event/src/main/kotlin/event/domain/DomainAbstractAggregateRoot.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package event.domain | ||
|
||
/** | ||
* Domain abstract aggregate root | ||
* | ||
* @see org.springframework.data.domain.AbstractAggregateRoot | ||
*/ | ||
abstract class DomainAbstractAggregateRoot<A : DomainAbstractAggregateRoot<A>> { | ||
@Transient | ||
private val domainEvents: MutableList<Any> = mutableListOf() | ||
|
||
protected fun <T> registerEvent(event: T): T { | ||
requireNotNull(event) { "Domain event must not be null" } | ||
domainEvents.add(event) | ||
return event | ||
} | ||
|
||
@AfterEventPublication | ||
protected fun clearDomainEvents() { | ||
domainEvents.clear() | ||
} | ||
|
||
@DomainEvents | ||
protected fun domainEvents(): List<Any> = domainEvents.toList() | ||
|
||
protected fun andEventsFrom(aggregate: A): A { | ||
domainEvents.addAll(aggregate.domainEvents()) | ||
@Suppress("UNCHECKED_CAST") | ||
return this as A | ||
} | ||
|
||
protected fun andEvent(event: Any): A { | ||
registerEvent(event) | ||
@Suppress("UNCHECKED_CAST") | ||
return this as A | ||
} | ||
} |
98 changes: 98 additions & 0 deletions
98
library/event/src/main/kotlin/event/domain/DomainEventPublishingMethod.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package event.domain | ||
|
||
import event.domain.DomainEventPublishingMethod.Companion.NONE | ||
import event.domain.util.AnnotationDetectionMethodCallback | ||
import org.springframework.context.ApplicationEventPublisher | ||
import org.springframework.lang.Nullable | ||
import org.springframework.util.ReflectionUtils | ||
import java.lang.reflect.Method | ||
import java.util.function.Supplier | ||
|
||
class DomainEventPublishingMethod( | ||
private val type: Class<*>, | ||
private val publishingMethod: Method?, | ||
private val clearingMethod: Method? = null, | ||
) { | ||
companion object { | ||
val NONE = DomainEventPublishingMethod(Any::class.java, null, null) | ||
|
||
fun of(type: Class<*>): DomainEventPublishingMethod { | ||
if (!type.superclass.isAssignableFrom(DomainAbstractAggregateRoot::class.java)) { | ||
throw IllegalArgumentException("Type must extend DomainAbstractAggregateRoot") | ||
} | ||
|
||
val result = | ||
from( | ||
type, | ||
getDetector(type, DomainEvents::class.java), | ||
Supplier { | ||
getDetector( | ||
type, | ||
AfterEventPublication::class.java, | ||
) | ||
}, | ||
) | ||
return result | ||
} | ||
} | ||
|
||
fun publishEventsFrom( | ||
aggregates: Iterable<*>, | ||
publisher: ApplicationEventPublisher, | ||
) { | ||
for (aggregateRoot in aggregates) { | ||
if (!type.isInstance(aggregateRoot)) { | ||
continue | ||
} | ||
|
||
for (event in asCollection(ReflectionUtils.invokeMethod(publishingMethod!!, aggregateRoot), null)) { | ||
publisher.publishEvent(event) | ||
} | ||
|
||
if (clearingMethod != null) { | ||
ReflectionUtils.invokeMethod(clearingMethod, aggregateRoot) | ||
} | ||
} | ||
} | ||
} | ||
|
||
private fun from( | ||
type: Class<*>, | ||
publishing: AnnotationDetectionMethodCallback<*>, | ||
clearing: Supplier<AnnotationDetectionMethodCallback<*>>, | ||
): DomainEventPublishingMethod { | ||
if (!publishing.hasFoundAnnotation()) { | ||
return NONE | ||
} | ||
|
||
val eventMethod = publishing.getMethod()!! | ||
ReflectionUtils.makeAccessible(eventMethod) | ||
|
||
return DomainEventPublishingMethod( | ||
type, | ||
eventMethod, | ||
getClearingMethod(clearing.get()), | ||
) | ||
} | ||
|
||
@Nullable | ||
private fun getClearingMethod(clearing: AnnotationDetectionMethodCallback<*>): Method? { | ||
if (!clearing.hasFoundAnnotation()) { | ||
return null | ||
} | ||
|
||
val method = clearing.getRequiredMethod() | ||
ReflectionUtils.makeAccessible(method) | ||
|
||
return method | ||
} | ||
|
||
private fun <T : Annotation> getDetector( | ||
type: Class<*>, | ||
annotation: Class<T>, | ||
): AnnotationDetectionMethodCallback<T> { | ||
val callback = AnnotationDetectionMethodCallback(annotation) | ||
ReflectionUtils.doWithMethods(type, callback) | ||
|
||
return callback | ||
} |
Oops, something went wrong.