코딩 이래요래
Sprint Mission Part-1 User ↔ Channel 구조, 양방향 의존을 UseCase로 리팩토링 본문
💡 Sprint Mission Part-1 Refactoring
UseCase Layer로 서비스 간 의존성 정리하기
이번 미션은 이전에 진행했던 User ↔ Channel 구조를 다시 바라보며, 서비스 간 양방향 의존이 필요한 상황을 어떻게 풀어야 할까? 에 대한 고민을 UseCase Layer를 도입해서 리팩토링 해보는 내용임
Sprint Mission Part-1
Sprint Mission Part-1 (Java) 요구사항 및 구현 정리본 글에서는 스프린트 미션 Part-1에서 진행한Java를 활용한 디스코드(Discord) 서비스 도메인 모델링 및 CRUD 서비스 구현 내용을 정리함✅ 미션 요구사항
kh-coding.tistory.com
🧩 느낀 의문점
User와 Channel 도메인을 설계할 때,
- User는 행위 중심으로,
- Channel은 데이터 중심으로 구조를 잡았음
그러다 보니 createChannel이라는 행위를 어디에 정의 하지? 라는 의문이 생겼음
📍 기존 호출 방식
user.createChannel(name, desc)
→ 내부에서 channel.createChannel(...) 호출
즉, 사용자가 직접 채널을 만드는 것처럼 보이게끔 유저 도메인에서 위임 처리했음
이 방식은 말이 되긴 하는데, 점점 아래와 같은 문제가 눈에 들어오기 시작했음
❗고민 - 서비스 간 책임 분리
- 채널 생성이라는 행위는 사실 Channel이라는 리소스를 생성하는 것
- 그러면 이건 ChannelService가 담당하는 게 맞는 것 같음
- 그런데 실제 흐름은 UserService도 필요하고, 두 서비스가 서로 의존하게 되는 구조가 될 수 있음
👉 이렇게 되면 서비스 간 직접 호출이 생기고, 구조적으로 순환 참조 위험이 생김 (Stack Over Flow)
🔁 해결 방향 - UseCase Layer 도입
채널 생성은 ChannelService의 책임으로 명확히 하고, UserService는 사용자 관련 책임에 집중하는 것이 좋음
현재 구현 방식(위임)도 틀린 것은 아니지만, 검색을 통해 알게 된 건, 이런 복잡한 흐름은 대부분 UseCase 또는 Application Layer를 통해 조율한다는 점이었음 그래서 하나의 흐름(사용자가 채널을 만든다)을 유스케이스로 독립시켜서 리팩토링 해보기로 했음
✅ 리팩토링 후 구조
- UseCase는 하나의 유저 시나리오 단위로 동작 함
- 도메인 로직은 그대로 두고, 흐름을 orchestrate 하는 역할만 함
- UserService ↔ ChannelService 간 직접 호출 없이, 단방향 흐름이 유지됨
📐 구조 다이어그램
[ Application ]
↓
[ CreateChannelUseCase ]
↙ ↘
[UserService] [ChannelService]
- UserService는 유저를 조회하는 역할만
- ChannelService는 채널 생성 및 저장만
- 진짜 "누가 뭘 하는지"가 명확해짐
✅ 느낀 점 정리
- 서비스 간 의존이 필요한 시나리오가 많아 보이지만, 대부분은 UseCase Layer를 통해 우회할 수 있다.
- 책임을 각 도메인(service)에 명확히 분리해두고, 실제 "행위"는 UseCase에서 orchestration 하는 게 더 깔끔한 구조가 됨
- 앞으로도 유저 중심의 액션(결제, 채널 초대, 알림 전송 등)은 UseCase 단위로 정리할 고민을 해봐야겠음
'Refactoring' 카테고리의 다른 글
AOP를 통한 서비스 계층 로그 분리 (0) | 2025.05.02 |
---|---|
Sprint Mission Part 3 File*Repository Refactoring (0) | 2025.04.30 |