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

[Feat]: 채팅방 메세지를 조회할 수 있다 #14

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package gloddy.groupChat.dto.command

import gloddy.util.CursorRequest
import java.util.*

data class GroupChatGetMessageCommand(
val userId: Long,
val chatId: UUID,
val cursorRequest: CursorRequest
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package gloddy.groupChat.service

import gloddy.groupChat.GroupChatMessage
import gloddy.groupChat.dto.command.GroupChatGetMessageCommand
import gloddy.port.`in`.GetGroupChatMessageUseCase
import gloddy.port.out.GetGroupChatMessagePort
import gloddy.util.CursorRequest
import gloddy.util.PageCursor

class GroupChatGetMessageCommander(private val getGroupChatMessagePort: GetGroupChatMessagePort) : GetGroupChatMessageUseCase {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

조회에 대한 도메인 서비스 이름을 ~Commander라고 지은 이유가 뭔가요??
Commander는 CUD에 대한 도메인 서비스를 가리키는 의도로 지었는데 여기서는 GroupChatReader라는 클래스 명이 맞지 않을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CUD에 대한 서비스가 아닌 단순히 '요청(명령)을 한다'라는 의미로 이해하여 네이밍을 하게되었습니다. 말씀해주신대로 GroupChatReader로 변경하고 앞으로 맞춰서 작성하겠습니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그럼 혹시 dto 패키지 하위의 command들도 CUD를 위한 dto인가요??


override fun getPosts(command: GroupChatGetMessageCommand): PageCursor<GroupChatMessage> {
var posts = findAllBy(command)
var nextKey = posts.stream()
.mapToLong(GroupChatMessage::sequenceId)
.min()
.orElse(CursorRequest.NONE_KEY)
return PageCursor(command.cursorRequest.next(nextKey), posts.size, posts)
}

private fun findAllBy(command: GroupChatGetMessageCommand): List<GroupChatMessage> {
return if (command.cursorRequest.hasKey()) {
getGroupChatMessagePort.findWithKey(command)
} else {
getGroupChatMessagePort.findWithoutKey(command)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package gloddy.port.`in`

import gloddy.groupChat.GroupChatMessage
import gloddy.groupChat.dto.command.GroupChatGetMessageCommand
import gloddy.util.PageCursor

interface GetGroupChatMessageUseCase {

fun getPosts(groupChatGetMessageCommand: GroupChatGetMessageCommand): PageCursor<GroupChatMessage>
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

도메인 서비스에 대한 추상화는 필요 없을 거라는 판단하에 GroupChatCommander 또한 추상화를 하지 않았기 때문에 해당 클래스는 삭제 부탁드립니다~

10 changes: 10 additions & 0 deletions domain/src/main/kotlin/gloddy/port/out/GetGroupChatMessagePort.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package gloddy.port.out

import gloddy.groupChat.GroupChatMessage
import gloddy.groupChat.dto.command.GroupChatGetMessageCommand

interface GetGroupChatMessagePort {
fun findWithoutKey(groupChatGetMessageCommand: GroupChatGetMessageCommand): List<GroupChatMessage>

fun findWithKey(groupChatGetMessageCommand: GroupChatGetMessageCommand): List<GroupChatMessage>
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Domain 모듈의 영속성 추상클래스 <-(implement) Storage 모듈의 영속성 클래스의 이름 규칙을
~Repository <- ~AdapterRepository 로 Command 로직을 짤 때 정했기 때문에
GroupChatQueryRepository <- GroupChatQueryAdapterRepository로 수정부탁합니다~

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확인했습니다 앞으로 반영하겠습니다🫡

18 changes: 18 additions & 0 deletions domain/src/main/kotlin/gloddy/util/CursorRequest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package gloddy.util


data class CursorRequest(
val key: Long?
) {
companion object {
const val NONE_KEY: Long = -1L
}

fun hasKey(): Boolean {
return key != null
}

fun next(key: Long?): CursorRequest {
return CursorRequest(key)
}
}
7 changes: 7 additions & 0 deletions domain/src/main/kotlin/gloddy/util/PageCursor.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package gloddy.util

data class PageCursor<T>(
val nextCursorRequest: CursorRequest,
val size: Int,
val body: List<T>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package gloddy.adapter

import gloddy.groupChat.GroupChatMessage
import gloddy.groupChat.dto.command.GroupChatGetMessageCommand
import gloddy.persistence.repository.GroupChatMessageRepository
import gloddy.port.out.GetGroupChatMessagePort

class GroupChatMessageCommandAdapter(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

조회용 Adapter Repository니깐 GroupChatQueryAdapterRepository가 맞는 표현이겠죠??
수정부탁드립니다~

그리고 GroupChat 하위 도메인들(GroupChatMessage, GroupChatUser) 또한 그에 맞는 새로운 레포지토리를 파는게 아닌 GroupChatRepository 내부에서 처리하는게 관리가 용이할 것 같애서 위처럼 GroupChatQueryAdapterRepository로 수정 부탁드립니다~!

Copy link
Contributor Author

@Hwanvely Hwanvely May 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GroupChatRepository 내부에서 처리하는게 관리가 용이할 것 같다는 말씀이 GroupChat(Query, Command) Repository이런 방식을 말씀하시는거죠??
수정했습니다!

private val repository: GroupChatMessageRepository)
: GetGroupChatMessagePort {

override fun findWithoutKey(command: GroupChatGetMessageCommand): List<GroupChatMessage> {
return repository.findAllByMemberIdAndOrderByIdDesc(
command.userId,
command.chatId
)
}

override fun findWithKey(command: GroupChatGetMessageCommand): List<GroupChatMessage> {
return repository.findAllByLessThanIdMemberIdAndOrderByIdDesc(
command.cursorRequest.key,
command.userId,
command.chatId
)
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package gloddy.persistence.repository

import gloddy.groupChat.GroupChatMessage
import gloddy.groupChat.vo.MessageType
import org.springframework.jdbc.core.RowMapper
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
import java.time.LocalDateTime
import java.util.*

class GroupChatMessageRepository(private val namedParameterJdbcTemplate: NamedParameterJdbcTemplate) {

Copy link
Member

@jihwan2da jihwan2da May 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QueryDsl이나 JPARepository가 아닌 NamedParameterJdbcTemlate를 사용한 이유가 뭔가요??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일단 쿼리를 직접짜야겠다는 생각을 하였고 보다 익숙한 NamedParameterJdbcTemplate을 사용했습니다. 추후에 리팩토링을 해보겠습니다.

companion object {
private const val TABLE = "GroupChatMessage"
private val ROW_MAPPER: RowMapper<GroupChatMessage> = RowMapper { rs, rowNum ->
GroupChatMessage(
userId = rs.getLong("userId"),
chatId = rs.getObject("chatId", UUID::class.java),
messageType = MessageType.valueOf(rs.getString("messageType")),
content = rs.getString("content"),
createdAt = rs.getObject("createdAt", LocalDateTime::class.java) ?: LocalDateTime.now(),
deleted = rs.getBoolean("deleted"),
deletedAt = rs.getObject("deletedAt", LocalDateTime::class.java),
id = rs.getObject("id", UUID::class.java),
sequenceId = rs.getLong("sequenceId"))
}
}

fun findAllByMemberIdAndOrderByIdDesc(userId: Long, chatId: UUID): List<GroupChatMessage> {
val sql = """
SELECT *
FROM $TABLE as message
INNER JOIN GroupChatUser AS user
ON user.chatId = message.chatId
WHERE chatId = :chatId and user.createdAt < message.createdAt
ORDER BY sequenceId desc
""".trimIndent()
val params = MapSqlParameterSource()
.addValue("chatId", chatId)

return namedParameterJdbcTemplate.query(sql, params, ROW_MAPPER)
}

fun findAllByLessThanIdMemberIdAndOrderByIdDesc(sequenceId: Long?, userId: Long, chatId: UUID): List<GroupChatMessage> {
val sql = """
SELECT *
FROM $TABLE
WHERE chatId = :chatId and sequenceId < :sequenceId
ORDER BY sequenceId desc
""".trimIndent()
val params = MapSqlParameterSource()
.addValue("sequenceId", sequenceId)
.addValue("chatId", chatId)

return namedParameterJdbcTemplate.query(sql, params, ROW_MAPPER)
}

}
Loading