서비스라고하면 위의 두개로 나뉠수가 있다. 위의 내용을 하나씩 살펴보는 시간이라고 생각하면 될것같다.
값 객체나 엔티티 같은 도메인 객체에는 객체의 행동을 정의할 수 있다.
하지만 값 객체와 엔티티에 행동을 정의하는것은 어색할 수 있다.
도메인 서비스는 이런 어색함을 해결해주는 객체이다.
class User
{
private readonly UserId id;
private UserName name;
public User(UserId id, UesrName name)
{
if(id == null) throw new ArgumentNullException(nameof(id));
if(name == null) throw new ArgumentNullException(nameof(name));
this.id = id;
this.name = name;
}
// 사용자명 중복 여부 확인 코드 추가
public bool Exists(User user)
{
// 사용자명 중복을 확인하는 코드
...
}
}
그렇다고 해서 도메인 서비스에 모든 객체의 행동을 넣게되면 안된다.
도메인 빈혈 현상이 일어나게 된다.
UserService
class UserService
{
public void ChangeName(User user, UserName name)
{
if (user == null) throw new ArgumentNullException(nameof(user));
if (name == null) throw new ArgumentNullException(nameof(name));
user.Name = name;
}
}
User
class User
{
private readonly UserId id;
public User(UserId id, UserName name)
{
this.id = id;
Name = name;
}
public UserName Name {
get;
set;
}
}
모든 처리를 도메인 서비스에 구현하면 엔티티에는 게터와 세터만 남게된다.
위와 같은 코드는 도메인 규칙을 발견하기 어렵다.
그래서 사용자의 내용 정보와 사용자의 정보를 변경처리는 도메인단에서 하도록한다.
class User
{
private readonly UserId id;
private UserName name;
public User(UserId id, UserName)
{
this.id = id;
this.name = name;
}
public void ChangeUserName(UserName name)
{
if (name == null) throw new ArgumentNullException(nameof(name));
this.name = name;
}
}
도메인 객체에 구현해야 할 행위를 모두 서비스로 옮길수가 있다.
빈혈 도메인 모델이 생기지 않으려면 어떤 행위를 어디에 구현해야 할지 꼼꼼히 검토를 해야한다.
행위가 빈약한 객체는 절차적 프로그래밍으로 빠지기 쉽기 때문이다.