Skip to content
This repository has been archived by the owner on May 19, 2024. It is now read-only.

Commit

Permalink
[WEAV-000] 미팅매칭시 이벤트 메시지 발송 리펙터링 (#245)
Browse files Browse the repository at this point in the history
  • Loading branch information
waterfogSW authored Apr 17, 2024
1 parent 997e289 commit 8ef193a
Show file tree
Hide file tree
Showing 12 changed files with 185 additions and 68 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.studentcenter.weave.application.chat.port.inbound

import com.studentcenter.weave.domain.meeting.entity.Meeting
import com.studentcenter.weave.domain.meeting.event.MeetingCompletedEvent

interface CreateChatRoom {

fun invoke(meeting: Meeting)
fun invoke(meetingCompletedEvent: MeetingCompletedEvent)

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ package com.studentcenter.weave.application.chat.service
import com.studentcenter.weave.application.chat.port.inbound.CreateChatRoom
import com.studentcenter.weave.application.chat.port.outbound.ChatRoomRepository
import com.studentcenter.weave.domain.chat.entity.ChatRoom
import com.studentcenter.weave.domain.meeting.entity.Meeting
import com.studentcenter.weave.domain.meeting.event.MeetingCompletedEvent
import org.springframework.stereotype.Service

@Service
class CreateChatRoomService (
private val chatRoomRepository: ChatRoomRepository
): CreateChatRoom {
class CreateChatRoomService(
private val chatRoomRepository: ChatRoomRepository,
) : CreateChatRoom {

override fun invoke(meeting: Meeting) {
override fun invoke(meetingCompletedEvent: MeetingCompletedEvent) {
ChatRoom
.create(meeting)
.create(meetingCompletedEvent.entity)
.also { chatRoomRepository.save(it) }
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.studentcenter.weave.application.meeting.port.inbound

import com.studentcenter.weave.domain.meeting.event.MeetingCompletedEvent

interface NotifyMeetingEvent {

fun notifyMeetingCompleted(meetingCompletedEvent: MeetingCompletedEvent)

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.studentcenter.weave.application.meeting.port.outbound

import com.studentcenter.weave.application.meeting.vo.MeetingMatchingEvent

fun interface MeetingEventPort {
fun interface MeetingEventMessagePort {

fun sendMeetingIsMatchedMessage(meetingMatchingEvent: MeetingMatchingEvent)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,15 @@ package com.studentcenter.weave.application.meeting.service.application
import com.studentcenter.weave.application.common.exception.MeetingExceptionType
import com.studentcenter.weave.application.common.security.context.getCurrentUserAuthentication
import com.studentcenter.weave.application.meeting.port.inbound.CreateMeetingAttendance
import com.studentcenter.weave.application.meeting.port.outbound.MeetingEventPort
import com.studentcenter.weave.application.meeting.port.outbound.MeetingEventPublisher
import com.studentcenter.weave.application.meeting.service.domain.MeetingAttendanceDomainService
import com.studentcenter.weave.application.meeting.service.domain.MeetingDomainService
import com.studentcenter.weave.application.meeting.vo.MeetingMatchingEvent
import com.studentcenter.weave.application.meetingTeam.port.inbound.GetMeetingTeam
import com.studentcenter.weave.domain.meeting.entity.Meeting
import com.studentcenter.weave.domain.meeting.entity.MeetingAttendance
import com.studentcenter.weave.domain.meeting.event.MeetingCompletedEvent.Companion.createCompletedEvent
import com.studentcenter.weave.support.common.exception.CustomException
import com.studentcenter.weave.support.lock.distributedLock
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.springframework.stereotype.Service
import java.time.LocalDateTime
import java.util.*
Expand All @@ -26,14 +21,13 @@ class CreateMeetingAttendanceService(
private val meetingDomainService: MeetingDomainService,
private val meetingAttendanceDomainService: MeetingAttendanceDomainService,
private val getMeetingTeam: GetMeetingTeam,
private val meetingEventPort: MeetingEventPort,
private val meetingEventPublisher: MeetingEventPublisher,
) : CreateMeetingAttendance {

override fun invoke(
meetingId: UUID,
attendance: Boolean,
): Unit = distributedLock("${this.javaClass.simpleName}:$meetingId") {
) = distributedLock("${this.javaClass.simpleName}:$meetingId") {
val meeting = getByIdAndValidate(meetingId)

val requestingTeam = getMeetingTeam.getById(meeting.requestingTeamId)
Expand Down Expand Up @@ -82,46 +76,21 @@ class CreateMeetingAttendanceService(
val countAttend = meetingAttendanceDomainService.countByMeetingIdAndIsAttend(meeting.id)

if (countAttend == memberCount) {
updateMeetingStateToComplete(
meeting = meeting,
memberCount = memberCount,
)
updateMeetingStateToComplete(meeting)
}
}

private fun updateMeetingStateToCancel(meeting: Meeting) {
meetingDomainService.save(meeting.cancel())
}

private fun updateMeetingStateToComplete(
meeting: Meeting,
memberCount: Int,
) {
private fun updateMeetingStateToComplete(meeting: Meeting) {
meeting
.complete()
.also {
meetingDomainService.save(it)
meetingEventPublisher.publish(it.createCompletedEvent())
}

val matchedMeetingCount = meetingDomainService.countByStatusIsCompleted()

CoroutineScope(Dispatchers.IO).launch {
val requestingMeetingTeamMemberSummary =
getMeetingTeam.getMeetingTeamMemberSummaryByMeetingTeamId(meeting.requestingTeamId)
val receivingMeetingTeamMemberSummary =
getMeetingTeam.getMeetingTeamMemberSummaryByMeetingTeamId(meeting.receivingTeamId)

meetingEventPort.sendMeetingIsMatchedMessage(
MeetingMatchingEvent(
meeting = meeting,
memberCount = memberCount,
matchedMeetingCount = matchedMeetingCount,
requestingMeetingTeamMbti = requestingMeetingTeamMemberSummary.teamMbti,
receivingMeetingTeamMbti = receivingMeetingTeamMemberSummary.teamMbti,
)
)
}
}

private fun getByIdAndValidate(meetingId: UUID): Meeting {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.studentcenter.weave.application.meeting.service.application

import com.studentcenter.weave.application.meeting.port.inbound.NotifyMeetingEvent
import com.studentcenter.weave.application.meeting.port.outbound.MeetingEventMessagePort
import com.studentcenter.weave.application.meeting.service.domain.MeetingDomainService
import com.studentcenter.weave.application.meeting.vo.MeetingMatchingEvent
import com.studentcenter.weave.application.meetingTeam.port.inbound.GetMeetingTeam
import com.studentcenter.weave.domain.meeting.entity.Meeting
import com.studentcenter.weave.domain.meeting.event.MeetingCompletedEvent
import com.studentcenter.weave.domain.meetingTeam.entity.MeetingTeamMemberSummary
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class NotifyMeetingEventService(
private val meetingDomainService: MeetingDomainService,
private val meetingEventMessagePort: MeetingEventMessagePort,
private val getMeetingTeam: GetMeetingTeam,
) : NotifyMeetingEvent {

@Transactional
override fun notifyMeetingCompleted(meetingCompletedEvent: MeetingCompletedEvent) {
val meeting: Meeting = meetingCompletedEvent.entity
val matchedMeetingCount: Int = meetingDomainService.countByStatusIsCompleted()

val requestingMeetingTeamMemberSummary: MeetingTeamMemberSummary =
getMeetingTeam.getMeetingTeamMemberSummaryByMeetingTeamId(meeting.requestingTeamId)
val receivingMeetingTeamMemberSummary: MeetingTeamMemberSummary =
getMeetingTeam.getMeetingTeamMemberSummaryByMeetingTeamId(meeting.receivingTeamId)

MeetingMatchingEvent(
meeting = meeting,
memberCount = getMeetingMemberCount(meeting),
matchedMeetingCount = matchedMeetingCount,
requestingMeetingTeamMbti = requestingMeetingTeamMemberSummary.teamMbti,
receivingMeetingTeamMbti = receivingMeetingTeamMemberSummary.teamMbti,
).also {
meetingEventMessagePort.sendMeetingIsMatchedMessage(it)
}
}

private fun getMeetingMemberCount(meeting: Meeting): Int {
val requestingTeam = getMeetingTeam.getById(meeting.requestingTeamId)
val receivingTeam = getMeetingTeam.getById(meeting.receivingTeamId)

return requestingTeam.memberCount + receivingTeam.memberCount
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.studentcenter.weave.application.chat.outbound.ChatRoomRepositorySpy
import com.studentcenter.weave.domain.chat.entity.ChatRoom
import com.studentcenter.weave.domain.meeting.entity.MeetingFixtureFactory
import com.studentcenter.weave.domain.meeting.enums.MeetingStatus
import io.kotest.assertions.throwables.shouldThrow
import com.studentcenter.weave.domain.meeting.event.MeetingCompletedEvent
import io.kotest.core.annotation.DisplayName
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.matchers.shouldBe
Expand All @@ -26,9 +26,10 @@ class CreateChatRoomServiceTest : DescribeSpec({
it("채팅방을 생성한다") {
// arrange
val meeting = MeetingFixtureFactory.create(status = MeetingStatus.COMPLETED)
val event = MeetingCompletedEvent(meeting)

// act
sut.invoke(meeting)
sut.invoke(event)

// assert
val savedChatRooms: List<ChatRoom> = chatRoomRepositorySpy.findAll()
Expand All @@ -40,20 +41,6 @@ class CreateChatRoomServiceTest : DescribeSpec({
savedChatRoom.receivingTeamId shouldBe meeting.receivingTeamId
}
}

MeetingStatus.entries.filter { it != MeetingStatus.COMPLETED }.forEach { status ->
context("매칭되지 않은 미팅 정보가 주어졌을때 - $status") {
it("예외를 발생시킨다") {
// arrange
val meeting = MeetingFixtureFactory.create(status = status)

// act & assert
shouldThrow<IllegalArgumentException> {
sut.invoke(meeting)
}
}
}
}
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.studentcenter.weave.application.meeting.service.application

import com.studentcenter.weave.application.meeting.outbound.MeetingEventMessagePortSpy
import com.studentcenter.weave.application.meeting.outbound.MeetingRepositorySpy
import com.studentcenter.weave.application.meeting.service.domain.impl.MeetingDomainServiceImpl
import com.studentcenter.weave.application.meetingTeam.port.inbound.GetMeetingTeam
import com.studentcenter.weave.domain.meeting.entity.MeetingFixtureFactory
import com.studentcenter.weave.domain.meeting.enums.MeetingStatus
import com.studentcenter.weave.domain.meeting.event.MeetingCompletedEvent
import com.studentcenter.weave.domain.meetingTeam.entity.MeetingTeamFixtureFactory
import com.studentcenter.weave.domain.meetingTeam.entity.MeetingTeamMemberSummaryFixtureFactory
import io.kotest.core.annotation.DisplayName
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.matchers.shouldBe
import io.mockk.every
import io.mockk.mockk

@DisplayName("NotifyMeetingEventService")
class NotifyMeetingEventServiceTest : DescribeSpec({

val meetingRepository = MeetingRepositorySpy()
val meetingDomainService = MeetingDomainServiceImpl(meetingRepository)
val meetingEventMessagePort = MeetingEventMessagePortSpy()
val getMeetingTeamMock = mockk<GetMeetingTeam>()

val sut = NotifyMeetingEventService(
meetingDomainService = meetingDomainService,
meetingEventMessagePort = meetingEventMessagePort,
getMeetingTeam = getMeetingTeamMock,
)

describe("notifyMeetingCompleted") {
it("미팅 완료 이벤트 발생시 알림 메시지를 전송한다") {
// arrange
val requestMeetingTeam = MeetingTeamFixtureFactory.create()
val receivingMeetingTeam = MeetingTeamFixtureFactory.create()

val requestingMeetingTeamMemberSummary = MeetingTeamMemberSummaryFixtureFactory.create(
meetingTeamId = requestMeetingTeam.id,
)
val receivingMeetingTeamMemberSummary = MeetingTeamMemberSummaryFixtureFactory.create(
meetingTeamId = receivingMeetingTeam.id,
)

val meeting = MeetingFixtureFactory.create(
requestingTeamId = requestMeetingTeam.id,
receivingTeamId = receivingMeetingTeam.id,
status = MeetingStatus.COMPLETED
)
meetingRepository.save(meeting)

val event = MeetingCompletedEvent(meeting)

every { getMeetingTeamMock.getMeetingTeamMemberSummaryByMeetingTeamId(requestMeetingTeam.id) } returns requestingMeetingTeamMemberSummary
every {
getMeetingTeamMock.getMeetingTeamMemberSummaryByMeetingTeamId(
receivingMeetingTeam.id
)
} returns receivingMeetingTeamMemberSummary
every { getMeetingTeamMock.getById(requestMeetingTeam.id) } returns requestMeetingTeam
every { getMeetingTeamMock.getById(receivingMeetingTeam.id) } returns receivingMeetingTeam

// act
sut.notifyMeetingCompleted(event)

// assert
meetingEventMessagePort.count() shouldBe 1
}
}

})
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.studentcenter.weave.application.meeting.outbound

import com.studentcenter.weave.application.meeting.port.outbound.MeetingEventMessagePort
import com.studentcenter.weave.application.meeting.vo.MeetingMatchingEvent

class MeetingEventMessagePortSpy: MeetingEventMessagePort {

private val meetingMatchingEvents = mutableListOf<MeetingMatchingEvent>()

override fun sendMeetingIsMatchedMessage(meetingMatchingEvent: MeetingMatchingEvent) {
meetingMatchingEvents.add(meetingMatchingEvent)
}

fun count(): Int {
return meetingMatchingEvents.size
}

fun clear() {
meetingMatchingEvents.clear()
}

}
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
package com.studentcenter.weave.bootstrap.meeting.controller

import com.studentcenter.weave.application.chat.port.inbound.CreateChatRoom
import com.studentcenter.weave.application.meeting.port.inbound.NotifyMeetingEvent
import com.studentcenter.weave.domain.meeting.event.MeetingCompletedEvent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.springframework.context.event.EventListener
import org.springframework.stereotype.Controller

@Controller
class MeetingEventHandler(
private val createChatRoom: CreateChatRoom,
private val notifyMeetingEvent: NotifyMeetingEvent,
) {

@EventListener
fun handleMeetingEvent(meetingCompletedEvent: MeetingCompletedEvent) {
meetingCompletedEvent
.entity
.also { createChatRoom.invoke(it) }
CoroutineScope(Dispatchers.Default).launch {
launch { createChatRoom.invoke(meetingCompletedEvent) }
launch { notifyMeetingEvent.notifyMeetingCompleted(meetingCompletedEvent) }
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ class MeetingEventHandlerTest(
val meetingFixture = MeetingFixtureFactory.create(status = MeetingStatus.COMPLETED)
val meetingCompletedEvent = meetingFixture.createCompletedEvent()

every { createChatRoom.invoke(meetingCompletedEvent.entity) } just runs
every { createChatRoom.invoke(meetingCompletedEvent) } just runs

// act
applicationEventPublisher.publishEvent(meetingCompletedEvent)

// assert
verify { createChatRoom.invoke(meetingCompletedEvent.entity) }
verify { createChatRoom.invoke(meetingCompletedEvent) }
}
}

Expand Down
Loading

0 comments on commit 8ef193a

Please sign in to comment.