Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DRAW-347 WebSocket / Core & Domain 의존성 분리 리팩터링 Base #99

Open
wants to merge 57 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
6e4cbe4
Draw-348 refactor: SessionUseCase 이관
comforest Sep 16, 2024
a0d9be9
DRAW-348 refactor: Request Dto 이관
comforest Sep 16, 2024
50dee78
DRAW-348 refactor: Message Broker 이관
comforest Sep 16, 2024
e5e595b
DRAW-348 refactor: Domain에 있는 객체 Core로 임시 이관
comforest Sep 16, 2024
e54a267
DRAW-350 fix: Package 수정
comforest Sep 16, 2024
d6cdb52
DrAW-351 refactor: SessionEventListener에서 Session 객체 제거
comforest Sep 17, 2024
7596e60
DrAW-351 refactor: MafiaGameUseCase에서 Session 객체 제거
comforest Sep 17, 2024
40efd96
DRAW-351 refactor: Session 객체 WebSocket으로 이관
comforest Sep 17, 2024
9637ae3
DRAW-352 refactor: WaitingQueueSessionEventListener에서 Session 객체 제거
comforest Sep 17, 2024
0e4bdb5
DRAW-352 refactor: Session에서 RoomId를 제거
comforest Sep 17, 2024
53e94af
DRAW-352 refactor: WatingQueueSession 을 Session 으로 통일하기
comforest Sep 17, 2024
84d4934
DRAW-352 refactor: 대기열 Service
comforest Sep 17, 2024
c3a690c
DRAW-353 fix: 웹소켓 메트릭 SessionManager에 직접 연결
comforest Sep 17, 2024
7e2803e
DRAW-353 feat: Event 모듈 추가
comforest Sep 17, 2024
270896b
DRAW-353 refactor: 세션 종료 처리
comforest Sep 17, 2024
caab484
DRAW-353 refactor: 세션 시작 처리
comforest Sep 17, 2024
aa7092e
DRAW-353 fix: MDC roomId 추가
comforest Sep 17, 2024
d5ab146
Merge branch 'develop' into feature/DRAW-348
comforest Sep 17, 2024
64dbf7b
Merge branch 'feature/DRAW-348' into feature/DRAW-350
comforest Sep 17, 2024
e0aa95b
Merge branch 'feature/DRAW-350' into feature/DRAW-351
comforest Sep 17, 2024
297ef05
Merge branch 'feature/DRAW-351' into feature/DRAW-352
comforest Sep 17, 2024
6ce3521
Merge branch 'feature/DRAW-352' into feature/DRAW-353
comforest Sep 17, 2024
c96ceef
DRAW-351 fix: 코드리뷰 반영
comforest Sep 18, 2024
156891a
DRAW-352 fix: 코드리뷰 반영
comforest Sep 18, 2024
2e9a883
Merge pull request #92 from YAPP-Github/feature/DRAW-348
comforest Sep 19, 2024
0b49543
Merge pull request #93 from YAPP-Github/feature/DRAW-350
comforest Sep 19, 2024
2aedf01
Merge pull request #94 from YAPP-Github/feature/DRAW-351
comforest Sep 19, 2024
471c026
Merge pull request #95 from YAPP-Github/feature/DRAW-352
comforest Sep 19, 2024
33fecbc
DRAW-353 fix: 빠른 매칭 구분하기
comforest Sep 19, 2024
939c650
DRAW-287 chore: redis 세팅
SunwoongH Sep 22, 2024
ce15502
DRAW-287 feat: job 저장소 구현
SunwoongH Sep 22, 2024
d27d6a6
DRAW-287 refactor: timer repository 의존하도록 리팩터링
SunwoongH Sep 22, 2024
3abeb8f
DRAW-287 refactor: job key 의존하도록 리팩터링
SunwoongH Sep 22, 2024
c46fdf5
DRAW-287 chore: redis 의존성 추가
SunwoongH Sep 22, 2024
b1ff41e
DRAW-287 chore: core 모듈 redis 의존성 추가
SunwoongH Sep 22, 2024
9150e09
DRAW-287 feat: 마피아 게임 레디스 adapter 구현
SunwoongH Sep 22, 2024
f779021
DRAW-287 feat: 레디스 마피아 게임 정보 저장 클래스 구현
SunwoongH Sep 22, 2024
6d317d9
DRAW-287 refactor: 레디스 마피아 게임 정보 저장 클래스 분리
SunwoongH Sep 22, 2024
1d67ea0
DRAW-287 refactor: 코드 정리
SunwoongH Sep 22, 2024
3f68b3b
Merge branch 'refs/heads/develop' into feature/DRAW-287
SunwoongH Sep 22, 2024
3d3c061
DRAW-287 chore: redis 설정 추가
SunwoongH Sep 22, 2024
3b5f7fa
DRAW-287 fix: ktlint 오류 수정
SunwoongH Sep 22, 2024
6a11440
Merge remote-tracking branch 'origin/feature/DRAW-347' into feature/D…
SunwoongH Sep 23, 2024
0a8f698
DRAW-347 chore: scheduler 설정
SunwoongH Sep 23, 2024
c81077c
DRAW-347 feat: session ping 프로퍼티 추가
SunwoongH Sep 23, 2024
3936a25
DRAW-347 feat: 웹소켓 ping 요청 로직 구현
SunwoongH Sep 23, 2024
d2c1c03
DRAW-347 feat: 웹소켓 ping manager 구현
SunwoongH Sep 23, 2024
4e8fcb8
Merge pull request #97 from YAPP-Github/feature/DRAW-353
comforest Sep 23, 2024
5f4ddfb
DRAW-287 refactor: 코드 리뷰 반영
SunwoongH Sep 27, 2024
433fcaf
Merge branch 'refs/heads/feature/DRAW-347' into feature/DRAW-287
SunwoongH Sep 27, 2024
c83bcc8
DRAW-287 fix: merge conflict
SunwoongH Sep 27, 2024
d3d1045
DRAW-334 refactor: 코드 리뷰 반영
SunwoongH Sep 27, 2024
b35b348
Merge branch 'refs/heads/feature/DRAW-347' into feature/DRAW-334
SunwoongH Sep 27, 2024
44066b8
DRAW-334 refactor: 코드 리뷰 반영
SunwoongH Sep 27, 2024
d95d035
Merge pull request #101 from YAPP-Github/feature/DRAW-287
comforest Sep 28, 2024
53f118b
Merge pull request #102 from YAPP-Github/feature/DRAW-334
comforest Sep 28, 2024
32d1ae0
Merge branch 'develop' into feature/DRAW-347
comforest Sep 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 40 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Trouble Painter 🪄
# Trouble Painter 🪄

![1](https://github.com/user-attachments/assets/e39f0b35-511d-4499-92a2-043c2239f872)

Expand All @@ -13,7 +13,7 @@
## Architecture ✨

<div align=center>

<img width="700" src="https://github.com/user-attachments/assets/6a16defc-611a-4671-8c98-1998a461344a">

</div>
Expand All @@ -22,18 +22,18 @@

## Teck Stack ✨

| IDE | IntelliJ |
|:---|:---|
| Language | Kotlin |
| Framework | Spring Boot 3.2.5, Gradle |
| IDE | IntelliJ |
|:---------------|:-----------------------------------------------|
| Language | Kotlin |
| Framework | Spring Boot 3.2.5, Gradle |
| Authentication | Spring Security, JSON Web Tokens, Opaque Token |
| Orm | Spring Data JPA |
| Database | MySQL |
| External | Nginx, Docker, Redis, Kubernetes, ELK |
| Monitoring | Prometheus, Grafana, Sentry |
| CI/CD | ArgoCD, Github Action |
| API Docs | Notion, Swagger |
| Other Tool | Jira, Discord, Postman, Figma |
| Orm | Spring Data JPA |
| Database | MySQL |
| External | Nginx, Docker, Redis, Kubernetes, ELK |
| Monitoring | Prometheus, Grafana, Sentry |
| CI/CD | ArgoCD, Github Action |
| API Docs | Notion, Swagger |
| Other Tool | Jira, Discord, Postman, Figma |

<br>

Expand Down Expand Up @@ -68,9 +68,9 @@
- 일단 포트 역할하는 인터페이스도 이 모듈에 담는다.
```

| | app | adapter | core | support | domain |
|-------------|-----|---------|------|---------|--------|
| 사용가능한 모듈 여부 | - | - | - | - | - |
| | app | adapter | core | event | support | domain |
|-------------|-----|---------|------|-------|---------|--------|
| 사용가능한 모듈 여부 | - | - | - | - | - | - |

### support: 서포트 모듈

Expand All @@ -79,9 +79,21 @@
- TimeUtis 등
```

| | app | adapter | core | support | domain |
|-------------|-----|---------|------|---------|--------|
| 사용가능한 모듈 여부 | - | - | - | - | - |
| | app | adapter | core | event | support | domain |
|-------------|-----|---------|------|-------|---------|--------|
| 사용가능한 모듈 여부 | - | - | - | - | - | - |

### event: 이벤트 모듈

```text
- 이벤트 모듈
- 객체간 연결을 느슨하게 하기 위한 객체
- 이벤트 발생 및 전달을 주로 한다
```

| | app | adapter | core | event | support | domain |
|-------------|-----|---------|------|-------|---------|--------|
| 사용가능한 모듈 여부 | - | Runtime | - | - | O | O |

### core: 코어 모듈

Expand All @@ -90,10 +102,9 @@
- 웹 통신 / DB 관련 객체는 가급적 사용을 피한다.
```

| | app | adapter | core | support | domain |
|-------------|---|---------|------|---------|--------|
| 사용가능한 모듈 여부 | - | Runtime | - | O | O |

| | app | adapter | core | event | support | domain |
|-------------|-----|---------|------|-------|---------|--------|
| 사용가능한 모듈 여부 | - | Runtime | - | O | O | O |

### adapter : 외부 통신 모듈

Expand All @@ -102,9 +113,9 @@
- JPA / Kafka Producer / Http 통신 등이 해당 된다.
```

| | app | adapter | core | support | domain |
|-------------|---|---|------|---------|--------|
| 사용가능한 모듈 여부 | - | - | - | O | O |
| | app | adapter | core | event | support | domain |
|-------------|-----|---------|------|-------|---------|--------|
| 사용가능한 모듈 여부 | - | - | - | O | O | O |

### app : 요청 Receive 모듈

Expand All @@ -113,6 +124,6 @@
- Web Controller / Kafka Consumer 등이 해당 된다.
```

| | app | adapter | core | support | domain |
|-------------|---|---|------|---------|--------|
| 사용가능한 모듈 여부 | - | - | O | O | O |
| | app | adapter | core | event | support | domain |
|-------------|-----|---------|------|-------|---------|--------|
| 사용가능한 모듈 여부 | - | - | O | O | O | O |
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import com.google.firebase.messaging.Message
import com.google.firebase.messaging.Notification
import com.xorker.draw.i18n.MessageService
import com.xorker.draw.notification.PushMessageUseCase
import com.xorker.draw.websocket.WaitingQueueSession
import java.util.Locale
import org.springframework.stereotype.Service

Expand All @@ -14,11 +13,7 @@ class FcmService(
private val messageService: MessageService,
) : PushMessageUseCase {

override fun quickStart(session: WaitingQueueSession) {
quickStart(session.locale, session.user.name)
}

fun quickStart(localeStr: String, nickname: String) {
override fun quickStart(localeStr: String, nickname: String) {
val locale = Locale(localeStr)

val notification = Notification.builder()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,63 +0,0 @@
package com.xorker.draw.mafia

import com.xorker.draw.room.Room
import com.xorker.draw.room.RoomId
import com.xorker.draw.room.RoomRepository
import com.xorker.draw.support.metric.MetricManager
import com.xorker.draw.user.UserId
import java.util.concurrent.ConcurrentHashMap
import org.springframework.stereotype.Component

@Component
internal class MafiaGameAdapter(
private val metricManager: MetricManager,
) : MafiaGameRepository, RoomRepository {
private val data = ConcurrentHashMap<RoomId, MafiaGameInfo>()
private val userData = ConcurrentHashMap<UserId, RoomId>()

override fun saveGameInfo(gameInfo: MafiaGameInfo) {
val room = gameInfo.room
if (room.isEmpty()) {
removeGameInfo(gameInfo)
} else {
if (data.containsKey(room.id).not()) {
metricManager.increaseGameCount()
}
data[room.id] = gameInfo
room.players.forEach {
userData[it.userId] = room.id
}
}
}

override fun removeGameInfo(gameInfo: MafiaGameInfo) {
metricManager.decreaseGameCount()
gameInfo.room.players
.map { it.userId }
.forEach { removePlayer(it) }

val phase = gameInfo.phase
if (phase is MafiaPhaseWithTimer) {
phase.job.cancel()
}

data.remove(gameInfo.room.id)
}

override fun getGameInfo(roomId: RoomId): MafiaGameInfo? {
return data[roomId]
}

override fun getRoom(roomId: RoomId): Room<MafiaPlayer>? {
return data[roomId]?.room
}

override fun getGameInfo(userId: UserId): MafiaGameInfo? {
val roomId = userData[userId] ?: return null
return data[roomId]
}

override fun removePlayer(userId: UserId) {
userData.remove(userId)
}
}
Original file line number Diff line number Diff line change
@@ -1,48 +1,31 @@
package com.xorker.draw.mafia

import com.xorker.draw.exception.UnSupportedException
import com.xorker.draw.mafia.event.MafiaGameRandomMatchingEvent
import com.xorker.draw.notification.PushMessageUseCase
import com.xorker.draw.websocket.WaitingQueueSession
import com.xorker.draw.user.User
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentLinkedQueue
import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Component

@Component
internal class MafiaGameWaitingQueueAdapter(
private val eventPublisher: ApplicationEventPublisher,
private val pushMessageUseCase: PushMessageUseCase,
) : MafiaGameWaitingQueueRepository {
private val waitingQueue: ConcurrentHashMap<String, ConcurrentLinkedQueue<WaitingQueueSession>> = ConcurrentHashMap()
internal class MafiaGameWaitingQueueAdapter : MafiaGameWaitingQueueRepository {
private val waitingQueue: ConcurrentHashMap<String, ConcurrentLinkedQueue<User>> = ConcurrentHashMap()

override fun enqueue(size: Int, session: WaitingQueueSession) {
val queue = waitingQueue.getOrPut(session.locale) { ConcurrentLinkedQueue() }

queue.add(session)

synchronized(this) {
if (queue.size >= size) {
val players = mutableListOf<WaitingQueueSession>()
override fun size(locale: String): Int {
return waitingQueue[locale]?.size ?: 0
}

(0 until size).forEach { _ ->
queue.poll().let {
players.add(it)
}
}
override fun enqueue(user: User, locale: String) {
val queue = waitingQueue.getOrPut(locale) { ConcurrentLinkedQueue() }
queue.add(user)
}

val event = MafiaGameRandomMatchingEvent(players)
override fun dequeue(locale: String): User {
val queue = waitingQueue[locale] ?: throw UnSupportedException

eventPublisher.publishEvent(event)
} else {
pushMessageUseCase.quickStart(session)
}
}
return queue.poll()
}

override fun dequeue(session: WaitingQueueSession) {
val queue = waitingQueue[session.locale] ?: throw UnSupportedException

queue.remove(session)
override fun remove(user: User, locale: String) {
waitingQueue[locale]?.remove(user)
}
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,45 @@
package com.xorker.draw.timer

import com.xorker.draw.exception.UnSupportedException
import com.xorker.draw.mafia.event.JobWithStartTime
import com.xorker.draw.room.RoomId
import java.time.Duration
import java.time.LocalDateTime
import java.util.concurrent.ConcurrentHashMap
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Component

@Component
internal class TimerAdapter(
private val eventPublisher: ApplicationEventPublisher,
) : TimerRepository {
internal class TimerAdapter : TimerRepository {
private val jobs = ConcurrentHashMap<RoomId, JobWithStartTime>()

override fun <T : Any> startTimer(interval: Duration, event: T): JobWithStartTime {
override fun startTimer(roomId: RoomId, interval: Duration, callback: () -> Unit): JobWithStartTime {
val job = JobWithStartTime()
CoroutineScope(Dispatchers.IO + job).launch {
delay(interval.toMillis())
eventPublisher.publishEvent(event)
}
return job
}

override fun startTimer(interval: Duration, callback: () -> Unit): JobWithStartTime {
val job = JobWithStartTime()
saveJob(roomId, job)

CoroutineScope(Dispatchers.IO + job).launch {
delay(interval.toMillis())
callback.invoke()
}
return job
}

override fun cancelTimer(roomId: RoomId) {
jobs[roomId]?.cancel()
jobs.remove(roomId)
}

override fun getTimerStartTime(roomId: RoomId): LocalDateTime {
val job = jobs[roomId] ?: throw UnSupportedException

return job.startTime
}

private fun saveJob(roomId: RoomId, job: JobWithStartTime) {
jobs[roomId] = job
}
}
17 changes: 17 additions & 0 deletions adapter/redis/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import org.springframework.boot.gradle.tasks.bundling.BootJar

plugins {
kotlin("plugin.spring")
}

dependencies {
implementation(project(":domain"))
implementation(project(":support:metric"))

implementation("org.springframework.boot:spring-boot-starter-data-redis:${Versions.SPRING_BOOT}")
}

tasks {
withType<Jar> { enabled = true }
withType<BootJar> { enabled = false }
}
Loading
Loading