채팅방 기능을 개발할 때 ChatRoomService에서 좀 동떨어진 Invitation 관련 기능들을 많이 이용하게 되었는데
그게 ChatRoomService에 있으면 좀 의미가 맞지 않는 것 같아서 모두 InvitationRepository로 분리해서 코드를 구축했다
@Service
class ChatRoomService(
private val chatRoomRepository: ChatRoomRepository,
private val invitationRepository: InvitationRepository,
) {
fun getChatRoomInvitation(
chatRoomId: String,
userId: String,
): InvitationResponse {
verifyChatRoomAccess(chatRoomId, userId)
val code = invitationRepository.getInvitationCode(userId, chatRoomId)
return InvitationResponse(code)
}
}
@Component
class InvitationRepository(
private val redisTemplate: RedisTemplate<String, String>,
private val propertyConfig: PropertyConfig,
) {
fun getInvitationCode(
userId: String,
chatRoomId: String,
): String {
val key = generateKey(userId, chatRoomId)
var value = redisTemplate.opsForValue().get(key)
if (value == null) {
value = setInvitation(key, chatRoomId)
}
return generateCode(key, value)
}
fun setInvitation(
key: String,
chatRoomId: String,
): String {
val value = generateValue()
val ops: ValueOperations<String, String> = redisTemplate.opsForValue()
ops.set(key, value, propertyConfig.getExpiration())
return value
}
fun getExpiration(
userId: String,
chatRoomId: String,
): Long {
val key = generateKey(userId, chatRoomId)
val expiration = redisTemplate.getExpire(key)
return expiration
}
fun generateCode(
key: String,
value: String,
): String {
val combine = "$key,$value"
val digest = MessageDigest.getInstance("SHA-256")
val hash = digest.digest(combine.toByteArray(StandardCharsets.UTF_8))
return Base64.getEncoder().encodeToString(hash)
}
private fun generateKey(
userId: String,
chatRoomId: String,
): String {
return "$userId:$chatRoomId"
}
private fun generateValue(): String {
return UUID.randomUUID().toString()
}
}
@Repository
interface ChatRoomRepository : MongoRepository<ChatRoom, String> {
fun existsByIdAndMembersContaining(
roomId: String,
userId: String,
): Boolean
}
이렇게 구성되어 있었다
✨개선 : Invitation Service 로직 분리
Invitation 관련 Service 로직이 ChatRoomService에 있는 것이 이상해서 Invitation Repository에 넣었었는데
다시 생각해보면 Service가 Service를 의존하지 않아야 한다는 법칙은 없다
꼭 Repository만 들어가지 않아도 괜찮을 것 같아서 Service에 로직을 분리해 보았다
@Service
class ChatRoomService(
private val chatRoomRepository: ChatRoomRepository,
private val invitationService: InvitationService,
) {
fun getChatRoomInvitation(
chatRoomId: String,
userId: String,
): InvitationResponse {
verifyChatRoomAccess(chatRoomId, userId)
var code = invitationService.getInvitation(userId, chatRoomId)
if (code == null) {
code = invitationService.setInvitation(userId, chatRoomId)
}
val encodedCode = invitationService.generateKeyAndCode(userId, chatRoomId, code)
return InvitationResponse(encodedCode)
}
}
@Service
class InvitationService(
private val invitationRepository: InvitationRepository,
private val chatRoomProperty: ChatRoomProperty,
) {
fun getInvitation(
userId: String,
chatRoomId: String,
): String? {
val key = generateKey(userId, chatRoomId)
return invitationRepository.getValue(key)
}
fun setInvitation(
userId: String,
chatRoomId: String,
): String {
val key = generateKey(userId, chatRoomId)
val value = generateValue()
invitationRepository.setValueAndExpiration(key, value, chatRoomProperty.getExpiration())
return value
}
fun generateKeyAndCode(
userId: String,
chatRoomId: String,
code: String,
): String {
val key = generateKey(userId, chatRoomId)
return generateCode(key, code)
}
private fun generateCode(
key: String,
value: String,
): String {
val combinedString = "$key,$value"
val digest = MessageDigest.getInstance("SHA-256")
val hash = digest.digest(combinedString.toByteArray(StandardCharsets.UTF_8))
return Base64.getEncoder().encodeToString(hash)
}
private fun generateKey(
userId: String,
chatRoomId: String,
): String {
return "$userId:$chatRoomId"
}
private fun generateValue(): String {
return UUID.randomUUID().toString()
}
}
@Component
class InvitationRepository(
private val redisTemplate: RedisTemplate<String, String>,
) {
fun getValue(key: String): String? {
return redisTemplate.opsForValue().get(key)
}
fun setValueAndExpiration(
key: String,
value: String,
expiration: Duration,
) {
val ops: ValueOperations<String, String> = redisTemplate.opsForValue()
ops.set(key, value, expiration)
}
fun getExpiration(key: String): Long {
return redisTemplate.getExpire(key)
}
}
이렇게 3단 분리를 했다
'Journal' 카테고리의 다른 글
Spatial Index 전환기 (0) | 2024.08.01 |
---|---|
한 시간 후 만료되는 초대 링크 개발기 : DB 구성에 대한 고민 (0) | 2024.06.20 |
깃허브 프로필 꾸며보기!! (0) | 2024.02.09 |
[팀프로젝트][모아요이츠] Post domain 개발일지 (1) | 2024.01.09 |
크롬에서 백그라운드에 재생되고있는 미디어 뒤로 감기, 앞으로 감기, 정지/재생하기 (0) | 2023.11.10 |