도메인 로직을 도메인 영역과 응용 서비스에 무분별하게 구현하면 생기는 문제점
- 코드의 응집성이 떨어진다.
- 여러 응용서비스에서 동일한 도메인 로직을 구현할 가능성이 높아진다.
응용서비스는 다음과 같은 형태를 가진다.
public Result doSomeFunction(SomeReq req){
// 1. 리포지터리에서 애그리거트를 구한다.
SomeAgg agg = someAggRepository.findById(req.getId());
checkNull(agg);
// 2. 애그리거트의 도메인 기능을 실행한다.
agg.doFunc(req.getValue());
// 3. 결과를 리턴한다.
return createSuccessResult(agg);
}
새로운 애그리거트를 생성하는 응용서비스는 다음과 같은 형태를 가진다.
public Result doSomeCreation(CreateSomeReq req){
// 1. 데이터 중복 등 데이터가 유효한지 검사한다.
validate(req);
// 2. 애그리거트를 생성
SomeAgg agg = createSome(req);
// 3. 리포지터리에 애그리거트를 저장
someAggRepository.save(
// 4. 결과를 리턴한다.
return createSuccessResult(newAgg);
}
예시코드로 확인해보자.
🔻응용서비스
public class ChangePasswordService {
public void changePassword(String memberId, String oldPw, String newPw){
Member member = memberRepository.findById(memberId);
checkMemberExists(member);
member.changePassword(oldPw, newPw);
}
}
🔻도메인
기존 암호를 올바르게 입력했는지 확인하는 로직은 도메인에 있어야 한다.
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("lno new password");
this.password = newPw;
}
}
응용 서비스를 구현하는 방식
예를 들어, authenticate 메서드가 서비스 단에 있다고 하면 파라미터를 다음처럼 사용하면 안된다.
public void authenticate(HttpServletRequest reqest){
//생략
}
@Transactional
을 사용하면 된다. @Transactional
이 적용된 메서드가 RuntimeException
을 발생시키면 트랜잭션 롤백, 아니면 커밋답은 없다!!
권한 검사를 위해 주로 Spring Security를 사용하는데, 상황에 따라 시스템에 맞는 권한 검사 기능을 선택하는 것이 유지보수에 유리하다.
어떤 프레임워크를 사용하는가를 떠나서 보통 다음 세 곳에서 권한 검사를 수행한다.
- 표현 영역
- 응용 서비스
- 도메인
@PreAuthorize
어노테이션을 사용하면 서비스 메서드에 대한 권한 검사가 가능하다컨트롤러
-> 조회 응용 서비스
-> 조회 전용 기능(DAO/리포지터리)
의 순서로 접근하는 것이 아니라컨트롤러
-> 조회 전용 기능(DAO/리포지터리)
의 순서로 접근하는 것도 가능하다는 뜻이다.