6.1 표현 영역과 응용 영역
∘ 표현 영역 : 사용자의 요청을 해석
∘ 사용자가 실행하고 싶은 기능을 판별하고 해당 기능의 응용 서비스를 실행
∘ 응용 영역 : 사용자가 실행하고 싶은 서비스가 위치한 영역
∘ 사용자 → 표현 영역 → 응용 영역
∘ 사용자의 요청 데이터를 바탕으로 응용 서비스가 원하는 형식의 객체를 생성하는 과정 필요
∘ 사용자 ← 표현 영역 ← 응용 영역
∘ 응용 서비스의 실행한 결과를 표현 영역에서 사용자에게 알맞은 형식으로 답해줌
∘ 응용 서비스는 표현 영역에 의존하지 않음
6.2 응용 서비스의 역할
∘ 응용서비스는 리포지터리에서 도메인 객체를 가져와서 사용자의 요청을 처리
public Result doSomeFunc(SomeReq req){
SomeAgg agg = someAggRepository.findById(req.getId()); //리포지터리에서 애그리거트 구하기
agg.doFunc(req.getValue()); //애그리거트의 도메인 기능을 실행
return createSuccessResult(agg); //결과 리턴
}
∘ 새로운 애그리거트를 생성하는 응용 서비스
public Result doSomeCreation(CreateSomeReq req){
validate(req); //데이터가 유효한지
SomeAgg newAgg = createSome(req); //애그리거트 생성
someAggRepository.save(newAgg); //리포지터리에 애그리거트 저장
return createSuccessResult(newAgg); //결과 리턴
}
∘ 응용 서비스가 복잡하다면 응용 서비스에서 도메인 로직의 일부를 구현하고 있을 가능성이 높음
∘ 응용 서비스는 트랜잭션 범위에서 실행되어야 함
∘ 변경 사항을 DB에 반영하는 도중에 문제가 발생하는 것에 대비
6.2.1 도메인 로직 넣지 않기
∘ 일부 도메인 로직을 응용 서비스에서 구현할 때 발생하는 문제
- 코드의 응집도가 떨어짐
- 여러 응용 서비스에서 동일한 도메인 로직을 중복해서 작성할 가능성 높아짐
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;
}
public class ChangePasswordService {
public void changePassword(String memberId, String oldPw, String newPw){
Member member = memberRepository.findById(memberId);
checkMemberExists(member);
if(!passwordEncoder.matches(oldPw, member.getPassword(){
throw new BadPasswordException();
}
member.setPassword(newPw);
}
6.3 응용 서비스의 구현
6.3.1 응용 서비스의 크기
응용 서비스 구현 방식
1. 한 서비스 클래스에 도메인의 모든 기능 구현
∘ 각 기능에서 동일한 로직에 대한 코드가 중복되는 것을 막을 수 있음
∘ 연관성이 적은 코드끼리 붙어 있게 되어 코드 이해가 어려움
2. 기능별로 서비스 클래스를 따로 구현
∘ 클래스의 개수 많아짐
∘ 코드 품질 일정 수준 유지 가능
∘ 클래스끼리 서로의 코드에 영향을 주지 않음
∘ 로직의 중복 구현 문제는 공통 로직을 제공하는 메서드를 만들어서 해결할 수 있음
6.3.2 응용 서비스의 인터페이스와 클래스
- 인터페이스가 필요한지 명확하지 않은데 인터페이스를 작성하는 것은 좋지 않은 선택
6.3.3 메서드 파라미터와 값 리턴
- 응용 서비스의 메서드는 도메인을 이용해서 기능을 실행하는 데 필요한 값을 파라미터로 전달받음
- 개별 파라미터로 전달받음
public class ChangePasswordService{ public void changePassword(String memberId, String curPw, StringnewPw){ ... }
- 별도 데이터 클래스를 이용해 전달받음 (파라미터 개수가 2개 이상이면 이 방법이 좋음)
public class ChangePasswordRequest{ private String memberId; private String currentPassword; private String newPassword; ... }
- 표현 영역에서 응용 서비스의 결과를 필요로 하면 응용 서비스가 필요한 데이터를 리턴하도록 함 ex. 주문 번호
- 애그리거트 객체 자체를 리턴할 수도 있음 → 도메인 로직을 응용 서비스, 표현 영역에서 실행할 수 있게 되어 코드 응집도 낮아짐
- 응용 서비스가 표현 영역이 필요로 하는 데이터만 리턴하도록 하는 것이 좋음
6.3.4 표현 영역에 의존하지 않기
- 응용 서비스의 파라미터 타입으로 표현 영역과 관련된 타입을 사용하면 안 됨
- 표현 영역과 관련된 타입을 사용할 경우 응용 서비스가 표현 영역의 역할을 대신하는 문제가 발생할 수 있음
6.3.5 트랜잭션 처리
- 프레임워크가 제공하는 트랜잭션 기능을 이용하면 메서드가 정상적으로 실행되면 커밋하고 exception이 발생하면 rollback하게 됨
- 프레임워크를 이용하면 간결한 코드로 트랜잭션 관리 가능
6.4 표현 영역
- 표현 영역의 책임
- 사용자가 시스템을 사용할 수 있는 화면을 제공하고 제어한다.
- 사용자의 요청을 알맞은 응용 서비스에 전달하고 결과를 사용자에게 제공한다.
- 사용자의 세션을 관리한다.
6.5 값 검증
- 원칙적으로 모든 값 검증은 응용 서비스에서 처리함
- 여러 개의 파라미터 값이 올바른지 검사하는 방법
- 응용 영역에서 각 값이 유효한지 따지는 익셉션을 각각 작성
- → 사용자가 첫 번째 에러만 확인하게 되고 다른 값들이 유효한지 아는 데에 번거로움이 있음
- 각 값마다 쓴 익셉션 코드를 하나의 익셉션 코드로 모음
- → 익셉션에서 에러 목록을 가져와서 표현 영역에서 사용
- 표현 영역은 필수 값, 값의 형식, 범위 등을 검증하고 응용 서비스는 논리적 오류를 검증
6.6 권한 검사
- 권한 검사 : ‘사용자 U가 기능 F를 실행할 수 있는지’ 확인하는 것
- 시스템마다 권한의 복잡도가 다름
- 권한 검사를 수행할 수 있는 영역 : 표현 영역, 응용 서비스, 도메인
- 표현 영역 : 인증된 사용자인지 아닌지
- 응용 서비스 : URL만으로 접근 제어가 불가할 때 메서드 단위로 검사
- 도메인 : 객체 단위의 권한 검사 로직을 직접 구현하거나 프레임워크 이용
6.7 조회 전용 기능과 응용 서비스
- 단일 쿼리만 실행하는 조회라면 서비스 없이 표현 영역에서 조회 전용 기능을 사용해도 됨
- 사용자가 요청한 기능의 실행에 응용 서비스가 기여하는 게 없으면 표현 영역에서 응용 서비스 없이 조회 기능으로 접근해도 됨
'Back-End > 도메인 주도 개발 시작하기' 카테고리의 다른 글
[DDD] 도메인 주도 개발 시작하기 Ch8 (0) | 2024.04.01 |
---|---|
[DDD] 도메인 주도 개발 시작하기 Ch7 (0) | 2024.04.01 |
[DDD] 도메인 주도 개발 시작하기 Ch5 (0) | 2024.03.25 |
[DDD] 도메인 주도 개발 시작하기 Ch4 (0) | 2024.03.18 |
[DDD] 도메인 주도 개발 시작하기 Ch3 (0) | 2024.03.18 |