프로젝트 생각 기록

도메인 간 책임 분리와 확장성을 고려한 도메인 간의 참조 방향 설계

00z11 2025. 2. 26. 20:08
상황 : User, Channel, Message 도메인을 가진 디스코드를 구현하는 프로젝트를 진행하던 중에 바이너리 데이터를 저장하고 관리하는 BinaryContent 도메인, 사용자가 특정 채널의 메시지를 어디까지 읽었는지 추적하는 기능을 제공하는 ReadStatus 도메인, 그리고 사용자가 온라인 상태인지를 확인하는 기능을 담당하는 UserStatus 도메인을 추가하면서 하게 된 고민입니다.

프로젝트 환경 : Java, SpringBoot, Gradle

 

당시 바이너리 데이터 기능을 이용하던 곳은 두 곳이었다.

 

<< 유저의 프로필 이미지메세지의 첨부 파일 >>.

 

그래서 BinaryContent 엔티티에 User 객체와 Message 객체의 ID 필드를 참조하도록 설계했으며, 유저의 프로필 이미지로 사용되는 경우 Message 필드는 null로 두는 방식으로 로직을 구현했다.

 

하지만 ... 

  • 조건부로 null을 가지게 하는 설계 자체가 '지저분하다'고 생각했다. 특히, 항상 한쪽 필드는 null이 된다는 점에서 '낭비'처럼 느껴졌다.
  • 당시에는 Optional 클래스를 자세히 알지 못해 null을 이용했는데, 막연히 null에 대한 공포가 있었다.(아무래도 NPE 때문이겠죠)
  • 지금은 User, Message 도메인에서만 BinaryContent를 참조하지만, 나중에 다른 도메인에서도 이를 필요로 한다면 어떻게 할 것인가?
  • BinaryContent 도메인 모델이 User, Message에 지나치게 의존적인 구조는 아닌가?

 

위와 같은 사고의 흐름으로 도메인 간 참조 관계를 어떻게 설정해야 할지에 대해 의문이 생겼고,

BinaryContent 도메인의 역할은 바이너리 데이터를 관리하는 것으로 다른 도메인에서 활용될 수 있도록 독립적으로 설계하는 것이 더 긍정적이다는 것을 깨달았다.

 

User와 Message 엔티티에 BinaryContent 객체의 ID 필드를 추가하는 방식으로 변경했다.

이렇게 하면 BinaryContent는 바이너리 데이터를 관리하는 것에만 집중하게 두고, 추후 바이너리 데이터 관련 기능이 추가되거나 확장되면(ex. Message에 사용자 등록 이모티콘) BinaryContent 객체의 ID를 참조해 “이 도메인은 바이너리 데이터를 활용할 수 있다”를 명시적으로도 표현할 수 있을 것이라 생각했다.

 

즉, 아래 이점이 있다고 판단하여 참조 방향을 수정했다.

 

(1) 책임의 분리

BinaryContent 도메인은 바이너리 데이터의 관리 책임에 집중하고, 다른 도메인은 자신이 처리하는 비즈니스 로직을 책임지므로 각 도메인의 책임이 명확히 분리된다. User, Message 도메인에서는 바이너리 데이터 요청 여부를 (required = false)로 설정하여 필요할 때만 BinaryContent 를 활용할 수 있으므로, User, Message 는 여전히 자신의 핵심 역할을 수행한다고 볼 수 있다.

 

(2) 유지보수성과 확장성

기능 추가될 때마다 BinaryContent 도메인 자체를 수정할 것 없이 새로 추가하는 기능의 도메인에 BinaryContent 객체의 ID 필드 추가하면 된다. 기존 코드에 끼치는 영향(BinaryContent 도메인 모델을 수정하지 않아도 된다)을 최소화하고 기능을 추가할 때 유연함을 가지므로 이 방법을 확장성이 좋다고 할 수도 있곘다. 또한 BinaryContent 객체의 ID 를 참조해 “이 도메인은 바이너리 데이터를 활용할 수 있다”를 명시적으로 표현함으로서 추후 코드를 수정해야한다면 금방 파악할 수 있기 때문에 유지보수에도 용이하다.