🧐TIL
[DDD] 도메인 서비스, Domain Service
date
Feb 27, 2023
thumbnail
slug
ddd-domainservice
author
status
Public
summary
“도메인 주도 설계 철저 입문”을 읽으며 공부한 내용을 정리합니다.
type
Post
category
🧐TIL
tags
TIL
DDD
Architecture
updatedAt
Jun 18, 2023 05:57 AM
서비스의 정의
일상 생활에서 말하는 ‘서비스(service)’는 보통 하나의 대상이 다른 대상에게 무언가를 해주는 것을 의미한다. 여기서 서비스를 제공하는 대상을 ‘서브(serve)를 하는 자’라 하여 ‘서버(server)’라고 하고 서비스를 제공 받는 대상은 손님이라 하여 ‘클라이언트(client)’라고 부른다. 그리고 이 개념은 프로그래밍에서도 그대로 사용된다.
그런데 문제는 ‘무언가를 해준다’라는 개념이 워낙 포괄적이다보니 서비스라는 단어가 여기저기에서 다 쓰여서 프로그래밍 내에서 정확히 서비스의 개념을 정하고 경계를 나누기가 어렵다.
그래서 도메인 주도 설계에서는 서비스를 크게 둘로 나눠서 정의하는데 하나는 도메인을 위한 서비스인 도메인 서비스고 다른 하나는 이용자의 목적에 부응하는 프로그램을 말하는 ‘애플리케이션’을 위한 서비스, 즉 애플리케이션 서비스다.
여기서는 먼저 도메인 서비스에 대한 개념부터 공부해보도록 한다.
도메인 서비스
앞서 서비스란 ‘무언가를 하는 것’이라고 정의했는데, 서비스 객체는 곧 그 무언가를 하는 행동 자체를 객체로 표현한 것이라고 볼 수 있다.
그런데 애초에 객체란 속성값과 행동을 갖는다. 값 객체나 엔티티 같은 도메인 객체에 행동을 정의할 수 있는데 굳이 서비스 객체가 필요한 이유는 무엇일까?
그 이유는 시스템상 값 객체나 엔티티로 구현하기 부자연스러운 행동들이 존재하기 때문이다. 이런 부자연스러움을 해결해주는 것이 도메인 서비스다.
값 객체나 엔티티에 정의하기 어색한 행동
‘도메인 주도 설계 철저 입문’에서는 중복검사를 예시로 든다.
User 라는 엔티티가 있을 때, 새로운 User 객체를 만드는 과정에서 객체 동일성을 유지하고 객체 간 구별을 위해서는 이미 동일한 User 객체가 존재하는지 확인하기 위해 id 중복검사가 필요하다.
이때 id의 중복여부를 검사하는 행동을 User 엔티티에 정의하게 될 경우 User가 스스로 중복인지 아닌지를 검증해서 결과를 반환해야 한다. 불가능한 것은 아니지만 회원가입 과정에서 고객에게 “당신은 기존 고객입니까?”를 물어보면 고객이 스스로 전체 회원 리스트를 보면서 답을 찾아야 하게 되거나, 대답을 해도 그게 참인지 거짓인지 시스템은 알 수 없다.
그리고 이런 점은 id의 속성 값으로 UserId 라는 값 객체를 정의했다 해도 마찬가지다. UserId 값 객체에 중복여부를 검사하는 행동을 정의하려면 UserId 객체마다 전체 회원 id 리스트를 속성으로 갖도록 하거나 UserId가 다른 어딘가에 저장되어 있는 id 리스트 직접 조회하게 해야한다.
이런 부자연스러움들이 UserService 라는 도메인 서비스 객체를 정의하고 UserService의 행동으로 중복검사를 부여함으로써 해결할 수 있다.
도메인 서비스는 남용하기 쉽다
중요한 것은 도메인 서비스는 값 객체나 엔티티에 정의하기 부자연스러운 행동들을 처리하는 목적으로만 사용되어야 한다는 것이다.
자칫하면 값 객체나 엔티티에 정의되어야 할 행동을 모두 도메인 서비스로 만들어버릴 수도 있는데, 코드 상으로는 문제가 없어보이지만 정작 값 객체나 엔티티에 있어야 할 행동 정보들이 파편화 되어 코드를 읽고 도메인 규칙을 발견하기 어렵게 될 수 있다.
이렇게 되면 도메인 객체가 원래 포함했어야 할 지식이나 처리 내용을 모두 서비스 객체에게 빼앗기게 되는데 이처럼 제공할 정보가 없는 도메인 객체를 빈혈 도메인 모델(anemic domain model)이라고 한다.
빈혈 도메인 모델은 데이터와 행위를 함께 모아 놓는다는 객체 지향 설계의 기본원칙과 지식을 문서화하기 위한 도메인 주도 설계의 목적과 정반대 된다.
+ 그러면 남용을 어떻게 피할 것인가?
책에서는 “도메인 서비스를 사용하는 것은 가능한 한 피하라”고 한다. 최대한 값 객체나 엔티티에 행동을 정의하고 부자연스러움을 해결하는 목적으로만 사용하라는 기준을 제시해주긴 했지만 읽으면서 여전히 막연한 감이 들었다.
유즈케이스를 보면서 개인적으로 생각한 것은 일단 설계 단계에서는 미리 도메인 서비스를 구상해놓지 않는 것이 좋겠다는 것이었다. 유즈케이스 소개에서도 도메인 서비스의 구현은 작업 순서의 마지막에 등장하는데, 직접적으로 어떻게 하라고 글로 표현되어 있지는 않았지만 최대한 도메인 모델 내에 행동을 정의하는 방식으로 우선 구현을 하다가 부자연스러움이 발견되었을 때에서야 도메인 서비스를 분리해내는 것이 남용을 피할 방법이란 생각이 들었다.