: 응용 서비스는 사용자가 요청한 기능을 실행한다. 이런 요청을 수행하기 위해 리포지터리에서 도메인 객체를 가져와 사용한다.
: 응용 서비스의 주요 역할은 도메인 객체를 사용해서 사용자의 요청을 처리하는 것이므로 표현 영역 입장에서 보았을 때 응용 서비스는 도메인 영역과 표현 영역을 연결해주는 창구 역할을 한다.
: 응용 서비스는 주로 도메인 객체 간의 흐름을 제어하기 때문에 다음과 같은 단순한 형태를 갖는다.
public Result doSomeFunc(SomeReq req) {
// 1. 리포지터리에서 애그리거트를 구한다.
SomeAgg agg = someAggRepository.findById(req.getId());
checkNull(agg);
// 2. 애그리거트의 도메인 기능을 실행한다.
agg.doFunc(req.getValue());
// 3. 결과를 리턴한다.
return createSuccessResult(agg);
}
Java
복사
: 새로운 애그리거트를 생성하는 응용 서비스 역시 간단하다.
public Result doSomeCreation(CreateSomeReq req) {
// 1. 데이터 중복 등 데이터가 유효한지 검사한다.
validate(req);
// 2. 애그리거트를 생성한다.
SomeAgg newAgg = createSome(req);
// 3. 리포지터리에 애그리거트를 저장한다.
someAggRepository.save(newAgg);
// 4. 결과를 리턴한다.
return createSuccessResult(newAgg);
}
Java
복사
: 만약 응용 서비스가 복잡하다면 응용 서비스에서 도메인 로직의 일부를 구현하고 있을 가능성이 높다. 응용 서비스가 도메인 로직을 일부 구현하면 코드 중복, 로직 분산 등 코드 품질이 저하될 수 있다.
: 응용 서비스는 트랜잭션 처리도 담당한다. 응용 서비스는 도메인의 상태 변경을 트랜잭션으로 처리해야 한다.
: 응용 서비스의 주요 역할로는 트랜잭션 외에도 접근 제어와 이벤트 처리가 있다.
1. 도메인 로직 넣지 않기
: 도메인 로직은 도메인 영역에 위치하고 응용 서비스는 도메인 로직을 구현하지 않는다고 했다. 암호 변경 기능을 예로 들었을 때, 암호 변경 기능을 위한 응용 서비스는 Member 애그리거트와 관련 리포지터리를 이용해서 다음 코드와같이 도메인 객체 간의 실행 흐름을 제어할 뿐이다.
public class ChangePasswordService {
public void changePassword(String memberId, String oldPw, String newPw) {
Member member = memberRepository.findById(memberId);
checkMemberExists(member);
member.changePassword(oldPw, newPw);
}
...
Java
복사
: Member 애그리거트는 암호를 변경하기 전 기존 암호를 올바르게 입력했는지 확인하는 로직을 구현해야한다.
public class Member {
public void changePassword(String oldPw, String newPw) {
if (!matchPassword(oldPw)) {
throw new BadPasswordException();
}
setPassword(newPw);
}
public boolean matchPassword(String pwd) {
return passwordEncoder.matches(pwd);
}
private void setPassword(String newPw) {
if (isEmpty(newPw))
throw new IllegalArgumentException("no new password");
this.password = newPw;
}
}
Java
복사
: 기존 암호를 올바르게 입력했는지 확인하는 것은 도메인의 핵심 로직이기 때문에 응용 서비스에서 이 로직을 구현하면 안 된다.
: 도메인 로직을 도메인 영역과 응용 서비스에 분산해서 구현하면 코드 품질에 문제가 발생한다.
•
첫 번째 문제는 코드의 응집성이 저하된다는 것, 도메인 데이터와 그 데이터를 조작하는 도메인 로직이 하나의 영역에 분리되지 않고 서로 다른 영역에 위치한다는 것은 도메인 로직을 파악하고자 여러 영역을 분석해야하는 수고를 발생시킨다.
•
두 번째 문제는 여러 응용 서비스에서 동일한 도메인 로직을 구현할 가능성이 높아진다는 것, 비정상적인 계정 정지를 막기 위해 암호를 확인한다고 할 때, 이 경우 계정 정지 기능을 구현하는 응용 서비스는 암호를 확인하는 코드를 구현해야 한다.
: 코드 중복을 방지하기 위해 응용 서비스 영역에 별도의 보조 클래스를 만들 수 있지만 애초에 도메인 영역에 암호 확인 기능을 추가한다면 응용 서비스는 해당 기능을 사용하기만 하면 된다.
: 일부 도메인 로직이 응용 서비스에 구현되면 발생하는 두 가지 문제는 결과적으로 코드 변경을 어렵게 만든다. 소프트웨어가 가져야 할 중요한 가치 중 하나는 변경 용이성인데, 변경이 어렵다는 것은 그만큼 소프트웨어의 가치가 떨어진다는 것을 의미한다.
: 소프트웨어의 가치를 높이려면 도메인 로직을 도메인 영역에 모아서 코드 중복을 줄이고 응집도를 높여야 한다.