feat: ドメインイベントを受け取れるように

This commit is contained in:
usbharu 2024-06-27 01:00:13 +09:00
parent 0803877c8b
commit c74aba652e
Signed by: usbharu
GPG Key ID: 6556747BF94EEBC8
25 changed files with 124 additions and 74 deletions

View File

@ -0,0 +1,10 @@
package dev.usbharu.hideout.core.application.domainevent.subscribers
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
interface DomainEventSubscriber {
fun <T : DomainEventBody> subscribe(eventName: String, domainEventConsumer: DomainEventConsumer<T>)
}
typealias DomainEventConsumer<T> = (DomainEvent<T>) -> Unit

View File

@ -0,0 +1,17 @@
package dev.usbharu.hideout.core.application.domainevent.subscribers
import dev.usbharu.hideout.core.domain.event.post.PostEvent
import dev.usbharu.hideout.core.domain.event.post.PostEventBody
import org.springframework.stereotype.Component
@Component
class TimelinePostCreateSubscriber(domainEventSubscriber: DomainEventSubscriber) {
init {
domainEventSubscriber.subscribe<PostEventBody>(PostEvent.CREATE.eventName) {
val post = it.body.getPost()
val actor = it.body.getActor()
println(post.toString())
}
}
}

View File

@ -41,9 +41,9 @@ class RegisterLocalPostApplicationService(
override suspend fun internalExecute(command: RegisterLocalPost, executor: CommandExecutor): Long { override suspend fun internalExecute(command: RegisterLocalPost, executor: CommandExecutor): Long {
val actorId = ( val actorId = (
userDetailRepository.findById(command.userDetailId) userDetailRepository.findById(command.userDetailId)
?: throw IllegalStateException("actor not found") ?: throw IllegalStateException("actor not found")
).actorId ).actorId
val actor = actorRepository.findById(actorId)!! val actor = actorRepository.findById(actorId)!!

View File

@ -48,14 +48,14 @@ class GetRelationshipApplicationService(
val targetId = ActorId(command.targetActorId) val targetId = ActorId(command.targetActorId)
val target = actorRepository.findById(targetId)!! val target = actorRepository.findById(targetId)!!
val relationship = ( val relationship = (
relationshipRepository.findByActorIdAndTargetId(actor.id, targetId) relationshipRepository.findByActorIdAndTargetId(actor.id, targetId)
?: dev.usbharu.hideout.core.domain.model.relationship.Relationship.default(actor.id, targetId) ?: dev.usbharu.hideout.core.domain.model.relationship.Relationship.default(actor.id, targetId)
) )
val relationship1 = ( val relationship1 = (
relationshipRepository.findByActorIdAndTargetId(targetId, actor.id) relationshipRepository.findByActorIdAndTargetId(targetId, actor.id)
?: dev.usbharu.hideout.core.domain.model.relationship.Relationship.default(targetId, actor.id) ?: dev.usbharu.hideout.core.domain.model.relationship.Relationship.default(targetId, actor.id)
) )
val actorInstanceRelationship = val actorInstanceRelationship =
actorInstanceRelationshipRepository.findByActorIdAndInstanceId(actor.id, target.instance) actorInstanceRelationshipRepository.findByActorIdAndInstanceId(actor.id, target.instance)

View File

@ -21,7 +21,7 @@ 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
class ActorDomainEventFactory(private val actor: Actor) { class ActorDomainEventFactory(private val actor: Actor) {
fun createEvent(actorEvent: ActorEvent): DomainEvent { fun createEvent(actorEvent: ActorEvent): DomainEvent<ActorEventBody> {
return DomainEvent.create( return DomainEvent.create(
actorEvent.eventName, actorEvent.eventName,
ActorEventBody(actor), ActorEventBody(actor),

View File

@ -21,7 +21,7 @@ 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
class ActorInstanceRelationshipDomainEventFactory(private val actorInstanceRelationship: ActorInstanceRelationship) { class ActorInstanceRelationshipDomainEventFactory(private val actorInstanceRelationship: ActorInstanceRelationship) {
fun createEvent(actorInstanceRelationshipEvent: ActorInstanceRelationshipEvent): DomainEvent { fun createEvent(actorInstanceRelationshipEvent: ActorInstanceRelationshipEvent): DomainEvent<ActorInstanceRelationshipEventBody> {
return DomainEvent.create( return DomainEvent.create(
actorInstanceRelationshipEvent.eventName, actorInstanceRelationshipEvent.eventName,
ActorInstanceRelationshipEventBody(actorInstanceRelationship) ActorInstanceRelationshipEventBody(actorInstanceRelationship)

View File

@ -21,7 +21,7 @@ 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
class InstanceEventFactory(private val instance: Instance) { class InstanceEventFactory(private val instance: Instance) {
fun createEvent(event: InstanceEvent): DomainEvent { fun createEvent(event: InstanceEvent): DomainEvent<InstanceEventBody> {
return DomainEvent.create( return DomainEvent.create(
event.eventName, event.eventName,
InstanceEventBody(instance) InstanceEventBody(instance)

View File

@ -22,7 +22,7 @@ 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
class PostDomainEventFactory(private val post: Post, private val actor: Actor? = null) { class PostDomainEventFactory(private val post: Post, private val actor: Actor? = null) {
fun createEvent(postEvent: PostEvent): DomainEvent { fun createEvent(postEvent: PostEvent): DomainEvent<PostEventBody> {
return DomainEvent.create( return DomainEvent.create(
postEvent.eventName, postEvent.eventName,
PostEventBody(post, actor) PostEventBody(post, actor)
@ -30,7 +30,10 @@ class PostDomainEventFactory(private val post: Post, private val actor: Actor? =
} }
} }
class PostEventBody(post: Post, actor: Actor?) : DomainEventBody(mapOf("post" to post, "actor" to actor)) class PostEventBody(post: Post, actor: Actor?) : DomainEventBody(mapOf("post" to post, "actor" to actor)) {
fun getPost(): Post = toMap()["post"] as Post
fun getActor(): Actor? = toMap()["actor"] as Actor?
}
enum class PostEvent(val eventName: String) { enum class PostEvent(val eventName: String) {
DELETE("PostDelete"), DELETE("PostDelete"),

View File

@ -21,7 +21,7 @@ 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
class RelationshipEventFactory(private val relationship: Relationship) { class RelationshipEventFactory(private val relationship: Relationship) {
fun createEvent(relationshipEvent: RelationshipEvent): DomainEvent = fun createEvent(relationshipEvent: RelationshipEvent): DomainEvent<RelationshipEventBody> =
DomainEvent.create(relationshipEvent.eventName, RelationshipEventBody(relationship)) DomainEvent.create(relationshipEvent.eventName, RelationshipEventBody(relationship))
} }

View File

@ -25,12 +25,12 @@ value class ActorPrivateKey(val privateKey: String) {
fun create(privateKey: PrivateKey): ActorPrivateKey { fun create(privateKey: PrivateKey): ActorPrivateKey {
return ActorPrivateKey( return ActorPrivateKey(
"-----BEGIN PRIVATE KEY-----\n" + "-----BEGIN PRIVATE KEY-----\n" +
Base64 Base64
.getEncoder() .getEncoder()
.encodeToString(privateKey.encoded) .encodeToString(privateKey.encoded)
.chunked(64) .chunked(64)
.joinToString("\n") + .joinToString("\n") +
"\n-----END PRIVATE KEY-----" "\n-----END PRIVATE KEY-----"
) )
} }
} }

View File

@ -25,12 +25,12 @@ value class ActorPublicKey(val publicKey: String) {
fun create(publicKey: PublicKey): ActorPublicKey { fun create(publicKey: PublicKey): ActorPublicKey {
return ActorPublicKey( return ActorPublicKey(
"-----BEGIN PUBLIC KEY-----\n" + "-----BEGIN PUBLIC KEY-----\n" +
Base64 Base64
.getEncoder() .getEncoder()
.encodeToString(publicKey.encoded) .encodeToString(publicKey.encoded)
.chunked(64) .chunked(64)
.joinToString("\n") + .joinToString("\n") +
"\n-----END PUBLIC KEY-----" "\n-----END PUBLIC KEY-----"
) )
} }
} }

View File

@ -20,5 +20,5 @@ enum class Role {
LOCAL, LOCAL,
MODERATOR, MODERATOR,
ADMINISTRATOR, ADMINISTRATOR,
REMOTE; REMOTE
} }

View File

@ -89,12 +89,12 @@ class ActorInstanceRelationship(
override fun toString(): String { override fun toString(): String {
return "ActorInstanceRelationship(" + return "ActorInstanceRelationship(" +
"actorId=$actorId, " + "actorId=$actorId, " +
"instanceId=$instanceId, " + "instanceId=$instanceId, " +
"blocking=$blocking, " + "blocking=$blocking, " +
"muting=$muting, " + "muting=$muting, " +
"doNotSendPrivate=$doNotSendPrivate" + "doNotSendPrivate=$doNotSendPrivate" +
")" ")"
} }
companion object { companion object {

View File

@ -25,4 +25,4 @@ value class ApplicationName(val name: String) {
companion object { companion object {
const val LENGTH = 300 const val LENGTH = 300
} }
} }

View File

@ -1,9 +1,7 @@
package dev.usbharu.hideout.core.domain.model.filter package dev.usbharu.hideout.core.domain.model.filter
class FilterName(name: String) { class FilterName(name: String) {
val name = name.take(LENGTH) val name = name.take(LENGTH)
companion object { companion object {

View File

@ -37,15 +37,15 @@ class Media(
} }
override fun toString(): String { override fun toString(): String {
return "Media(" + return "Media(" +
"id=$id, " + "id=$id, " +
"name=$name, " + "name=$name, " +
"url=$url, " + "url=$url, " +
"remoteUrl=$remoteUrl, " + "remoteUrl=$remoteUrl, " +
"thumbnailUrl=$thumbnailUrl, " + "thumbnailUrl=$thumbnailUrl, " +
"type=$type, " + "type=$type, " +
"mimeType=$mimeType, " + "mimeType=$mimeType, " +
"blurHash=$blurHash, " + "blurHash=$blurHash, " +
"description=$description" + "description=$description" +
")" ")"
} }
} }

View File

@ -28,23 +28,23 @@ import java.util.*
* @property body ドメインイベントのボディ * @property body ドメインイベントのボディ
* @property collectable trueで同じドメインイベント名でをまとめる * @property collectable trueで同じドメインイベント名でをまとめる
*/ */
data class DomainEvent( data class DomainEvent<out T : DomainEventBody>(
val id: String, val id: String,
val name: String, val name: String,
val occurredOn: Instant, val occurredOn: Instant,
val body: DomainEventBody, val body: T,
val collectable: Boolean = false val collectable: Boolean = false
) { ) {
companion object { companion object {
fun create(name: String, body: DomainEventBody, collectable: Boolean = false): DomainEvent = fun <T : DomainEventBody> create(name: String, body: T, collectable: Boolean = false): DomainEvent<T> =
DomainEvent(UUID.randomUUID().toString(), name, Instant.now(), body, collectable) DomainEvent<T>(UUID.randomUUID().toString(), name, Instant.now(), body, collectable)
fun reconstruct( fun <T : DomainEventBody> reconstruct(
id: String, id: String,
name: String, name: String,
occurredOn: Instant, occurredOn: Instant,
body: DomainEventBody, body: T,
collectable: Boolean collectable: Boolean
): DomainEvent = DomainEvent(id, name, occurredOn, body, collectable) ): DomainEvent<T> = DomainEvent(id, name, occurredOn, body, collectable)
} }
} }

View File

@ -17,6 +17,6 @@
package dev.usbharu.hideout.core.domain.shared.domainevent package dev.usbharu.hideout.core.domain.shared.domainevent
@Suppress("UnnecessaryAbstractClass") @Suppress("UnnecessaryAbstractClass")
abstract class DomainEventBody(val map: Map<String, Any?>) { abstract class DomainEventBody(private val map: Map<String, Any?>) {
fun toMap(): Map<String, Any?> = map fun toMap(): Map<String, Any?> = map
} }

View File

@ -1,5 +1,5 @@
package dev.usbharu.hideout.core.domain.shared.domainevent package dev.usbharu.hideout.core.domain.shared.domainevent
interface DomainEventPublisher { interface DomainEventPublisher {
suspend fun publishEvent(domainEvent: DomainEvent) suspend fun publishEvent(domainEvent: DomainEvent<*>)
} }

View File

@ -18,13 +18,13 @@ package dev.usbharu.hideout.core.domain.shared.domainevent
@Suppress("UnnecessaryAbstractClass") @Suppress("UnnecessaryAbstractClass")
abstract class DomainEventStorable { abstract class DomainEventStorable {
private val domainEvents: MutableList<DomainEvent> = mutableListOf() private val domainEvents: MutableList<DomainEvent<*>> = mutableListOf()
protected fun addDomainEvent(domainEvent: DomainEvent) { protected fun addDomainEvent(domainEvent: DomainEvent<*>) {
domainEvents.add(domainEvent) domainEvents.add(domainEvent)
} }
fun clearDomainEvents() = domainEvents.clear() fun clearDomainEvents() = domainEvents.clear()
fun getDomainEvents(): List<DomainEvent> = domainEvents.toList() fun getDomainEvents(): List<DomainEvent<*>> = domainEvents.toList()
} }

View File

@ -1,7 +0,0 @@
package dev.usbharu.hideout.core.domain.shared.domainevent
interface DomainEventSubscriber {
fun subscribe(eventName: String, domainEventConsumer: DomainEventConsumer)
}
typealias DomainEventConsumer = (DomainEvent) -> Unit

View File

@ -54,7 +54,7 @@ class ExposedActorInstanceRelationshipRepository(override val domainEventPublish
query { query {
ActorInstanceRelationships.deleteWhere { ActorInstanceRelationships.deleteWhere {
actorId eq actorInstanceRelationship.actorId.id and actorId eq actorInstanceRelationship.actorId.id and
(instanceId eq actorInstanceRelationship.instanceId.instanceId) (instanceId eq actorInstanceRelationship.instanceId.instanceId)
} }
} }
update(actorInstanceRelationship) update(actorInstanceRelationship)
@ -68,7 +68,7 @@ class ExposedActorInstanceRelationshipRepository(override val domainEventPublish
.selectAll() .selectAll()
.where { .where {
ActorInstanceRelationships.actorId eq actorId.id and ActorInstanceRelationships.actorId eq actorId.id and
(ActorInstanceRelationships.instanceId eq instanceId.instanceId) (ActorInstanceRelationships.instanceId eq instanceId.instanceId)
} }
.singleOrNull() .singleOrNull()
?.toActorInstanceRelationship() ?.toActorInstanceRelationship()

View File

@ -24,7 +24,7 @@ import org.springframework.stereotype.Component
@Component @Component
class SpringFrameworkDomainEventPublisher(private val applicationEventPublisher: ApplicationEventPublisher) : class SpringFrameworkDomainEventPublisher(private val applicationEventPublisher: ApplicationEventPublisher) :
DomainEventPublisher { DomainEventPublisher {
override suspend fun publishEvent(domainEvent: DomainEvent) { override suspend fun publishEvent(domainEvent: DomainEvent<*>) {
applicationEventPublisher.publishEvent(domainEvent) applicationEventPublisher.publishEvent(domainEvent)
} }
} }

View File

@ -0,0 +1,29 @@
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.DomainEventSubscriber
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
import org.springframework.context.event.EventListener
import org.springframework.stereotype.Component
@Component
class SpringFrameworkDomainEventSubscriber : DomainEventSubscriber {
val map = mutableMapOf<String, MutableList<DomainEventConsumer<*>>>()
override fun <T : DomainEventBody> subscribe(eventName: String, domainEventConsumer: DomainEventConsumer<T>) {
map.getOrPut(eventName) { mutableListOf() }.add(domainEventConsumer as DomainEventConsumer<*>)
}
@EventListener
fun onDomainEventPublished(domainEvent: DomainEvent<*>) {
map[domainEvent.name]?.forEach {
try {
it.invoke(domainEvent)
}
catch (e: Exception) {
}
}
}
}

View File

@ -70,11 +70,11 @@ class HideoutUserDetails(
override fun toString(): String { override fun toString(): String {
return "HideoutUserDetails(" + return "HideoutUserDetails(" +
"password='$password', " + "password='$password', " +
"username='$username', " + "username='$username', " +
"userDetailsId=$userDetailsId, " + "userDetailsId=$userDetailsId, " +
"authorities=$authorities" + "authorities=$authorities" +
")" ")"
} }
companion object { companion object {