DDD

γ€Œλ„λ©”μΈ 주도 개발 μ‹œμž‘ν•˜κΈ°γ€6μž₯ μ‘μš© μ„œλΉ„μŠ€μ™€ ν‘œν˜„ μ˜μ—­

joaa 2023. 3. 27. 04:11

🎈 6.1. ν‘œν˜„ μ˜μ—­κ³Ό μ‘μš© μ˜μ—­

μ‚¬μš©μž -> ν‘œν˜„ μ˜μ—­ -> μ‘μš© μ˜μ—­ -> 도메인 μ˜μ—­

 

ν‘œν˜„ μ˜μ—­μ€ μ‚¬μš©μžμ˜ μš”μ²­μ„ 해석함.

μ‘μš© μ˜μ—­μ€ μ‹€μ œ μ‚¬μš©ν•˜μž μ›ν•˜λŠ” κΈ°λŠ₯을 μ œκ³΅ν•¨.

 

μ‚¬μš©μžμ™€ μƒν˜Έμž‘μš©μ€ ν‘œν˜„ μ˜μ—­μ΄ μ²˜λ¦¬ν•˜κΈ° λ•Œλ¬Έμ—, μ‘μš© μ„œλΉ„μŠ€λŠ” ν‘œν˜„ μ˜μ˜μ— μ˜μ‘΄ν•˜μ§€ μ•ŠμŒ.

μ‘μš© μ˜μ—­μ€ μ‚¬μš©μžκ°€ μ›Ή λΈŒλΌμš°μ €λ₯Ό μ‚¬μš©ν•˜λŠ”μ§€ REST APIλ₯Ό ν˜ΈμΆœν•˜λŠ”μ§€, TCP μ†ŒμΌ“μ„ μ‚¬μš©ν•˜λŠ”μ§€ μ•Œ ν•„μš”κ°€ μ—†μŒ. 

 

🎈 6.2. μ‘μš© μ„œλΉ„μŠ€μ˜ μ—­ν• 

μ‘μš© μ„œλΉ„μŠ€λŠ” μ‚¬μš©μžκ°€ μš”μ²­ν•œ κΈ°λŠ₯을 싀행함. 이λ₯Ό μœ„ν•΄ λ¦¬ν¬μ§€ν„°λ¦¬μ—μ„œ 도메인 객체λ₯Ό 가져와 μ‚¬μš©ν•œλ‹€. 

 

μ‘μš© μ„œλΉ„μŠ€μ˜ ꡬ쑰

public Result doSomeFunc(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 newAgg = createSome(req);
    
    //3. 리포지터리에 μ• κ·Έλ¦¬κ±°νŠΈλ₯Ό μ €μž₯
    someAggRepository.save(newAgg);
    
    //4. κ²°κ³Όλ₯Ό 리턴
    return createSuccessResult(newAgg);
}

 

μ‘μš© μ„œλΉ„μŠ€λŠ” νŠΈλžœμž­μ…˜ μ²˜λ¦¬λ„ λ‹΄λ‹Ήν•œλ‹€. νŠΈλžœμž­μ…˜ λ²”μœ„μ—μ„œ μ‘μš© μ„œλΉ„μŠ€λ₯Ό μ‹€ν–‰ν•΄μ•Ό ν•œλ‹€. 

 

 

6.2.1. 도메인 둜직 λ„£μ§€ μ•ŠκΈ°

도메인 λ‘œμ§μ„ 도메인 μ˜μ—­κ³Ό μ‘μš© μ„œλΉ„μŠ€μ— λΆ„μ‚°ν•΄μ„œ κ΅¬ν˜„ν•˜λ©΄ μ½”λ“œ ν’ˆμ§ˆμ— λ¬Έμ œκ°€ λ°œμƒν•¨.

1. μ½”λ“œμ˜ 응집성이 떨어짐.

도메인 데이터와 κ·Έ 데이터λ₯Ό μ‘°μž‘ν•˜λŠ” 도메인 둜직이 ν•œ μ˜μ—­μ— μœ„μΉ˜ν•˜μ§€ μ•Šκ³  μ„œλ‘œ λ‹€λ₯Έ μ˜μ—­μ— μœ„μΉ˜ν•œλ‹€λŠ” 것은 도메인 λ‘œμ§μ„ νŒŒμ•…ν•˜κΈ° μœ„ν•΄ μ—¬λŸ¬ μ˜μ—­μ„ 뢄석해야 ν•œλ‹€λŠ” 것을 μ˜λ―Έν•¨.

2. μ—¬λŸ¬ μ‘μš© μ„œλΉ„μŠ€μ—μ„œ λ™μΌν•œ 도메인 λ‘œμ§μ„ κ΅¬ν˜„ν•  κ°€λŠ₯성이 높아짐. 

 

 

🎈 6.3. μ‘μš© μ„œλΉ„μŠ€μ˜ κ΅¬ν˜„

6.3.1. μ‘μš© μ„œλΉ„μŠ€μ˜ 크기

- ν•œ μ‘μš© μ„œλΉ„μŠ€ ν΄λž˜μŠ€μ— νšŒμ› λ„λ©”μΈμ˜ λͺ¨λ“  κΈ°λŠ₯ κ΅¬ν˜„ν•˜λŠ” 방법

   λ™μΌν•˜λ‚˜ λ‘œμ§μ„ μœ„ν•œ μ½”λ“œ 쀑볡을 μ œκ±°ν•˜κΈ° μ‰½λ‹€λŠ” μž₯점, μ„œλΉ„μŠ€ 클래슀의 크기가 μ»€μ§„λ‹€λŠ” 단점. 

- κ΅¬λΆ„λ˜λŠ” κΈ°λŠ₯ λ³„λ‘œ μ„œλΉ„μŠ€ 클래슀λ₯Ό κ΅¬ν˜„ν•˜λŠ” 방법

   ν΄λž˜μŠ€ 개수 λ§Žμ•„μ§€μ§€λ§Œ μ½”λ“œ ν’ˆμ§ˆμ„ 일정 μˆ˜μ€€μœΌλ‘œ μœ μ§€ν•˜λŠ” 데 도움이 됨.

   λ™μΌν•œ λ‘œμ§μ„ κ΅¬ν˜„ν•  경우 별도 ν΄λž˜μŠ€μ— λ‘œμ§μ„ κ΅¬ν˜„ν•΄μ„œ μ½”λ“œ 쀑볡을 λ°©μ§€.

 

6.3.2. μ‘μš© μ„œλΉ„μŠ€μ˜ μΈν„°νŽ˜μ΄μŠ€μ™€ 클래슀

μ‘μš© μ„œλΉ„μŠ€λ₯Ό κ΅¬ν˜„ν•  λ•Œ μΈν„°νŽ˜μ΄μŠ€κ°€ ν•„μš”ν•œκ°€?

μΈν„°νŽ˜μ΄μŠ€κ°€ λͺ…ν™•ν•˜κ²Œ ν•„μš”ν•˜κΈ° μ „κΉŒμ§€λŠ” μ‘μš© μ„œλΉ„μŠ€μ— λŒ€ν•œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μž‘μ„±ν•˜λŠ” 것이 쒋은 선택이라고 λ³Ό μˆ˜λŠ” μ—†λ‹€. 

ν…ŒμŠ€νŠΈ 주도 κ°œλ°œμ„ 즐겨 ν•œλ‹€λ©΄ 인트페이슀λ₯Ό μ΄μš©ν•΄μ„œ 컨트둀러의 κ΅¬ν˜„μ„ μ™„μ„±ν•΄ λ‚˜κ°ˆ 수 μžˆλ‹€. 

 

6.3.3. λ©”μ„œλ“œ νŒŒλΌλ―Έν„°μ™€ κ°’ 리턴

6.3.4. ν‘œν˜„ μ˜μ—­μ— μ˜μ‘΄ν•˜μ§€ μ•ŠκΈ°

μ‘μš© μ„œλΉ„μŠ€μ˜ νŒŒλΌλ―Έν„° νƒ€μž…μ„ ν‘œν˜„ μ˜μ—­κ³Ό κ΄€λ ¨λœ νƒ€μž…μ„ μ‚¬μš©ν•˜λ©΄ μ•ˆ λœλ‹€. 

예λ₯Ό λ“€μ–΄ HttpServletRequestλ‚˜ HttpSession을 μ‘μš© μ„œλΉ„μŠ€μ— νŒŒλΌλ―Έν„°λ‘œ μ „λ‹¬ν•˜λ©΄ μ•ˆ 됨.

6.3.5. νŠΈλžœμž­μ…˜ 처리

@Transactional이 적용된 λ©”μ„œλ“œκ°€ RuntimeException을 λ°œμƒμ‹œν‚€λ©΄ νŠΈλžœμž­μ…˜μ„ λ‘€λ°±ν•˜κ³  κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ μ»€λ°‹ν•˜λ―€λ‘œ 이 κ·œμΉ™μ— 따라 μ½”λ“œλ₯Ό μž‘μ„±ν•˜λ©΄ νŠΈλžœμž­μ…˜ 처리 μ½”λ“œλ₯Ό κ°„κ²°ν•˜κ²Œ μœ μ§€ν•  수 μžˆλ‹€. 

 

🎈 6.4. ν‘œν˜„ μ˜μ—­

μ±…μž„

1. μ‚¬μš©μžκ°€ μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•  수 μžˆλŠ” 흐름(ν™”λ©΄)을 μ œκ³΅ν•˜κ³  μ œμ–΄

2. μ‚¬μš©μžμ˜ μš”μ²­μ„ μ•Œλ§žμ€ μ‘μš© μ„œλΉ„μŠ€μ— μ „λ‹¬ν•˜κ³  κ²°κ³Όλ₯Ό μ‚¬μš©μžμ—κ²Œ 제곡

3. μ‚¬μš©μžμ˜ μ„Έμ…˜μ„ 관리

 

//ν”„λ ˆμž„μ›Œν¬ 제곡 κΈ°λŠ₯ μ‚¬μš©ν•˜λ©΄ νŒŒλΌλ―Έν„°λ₯Ό ChangePasswordRequest chPwdReq으둜 μžλ°” 객체λ₯Ό λ°”λ‘œ 생성할 μˆ˜λ„ 있음
@PostMapping()
public String changePassword(HttpServletRequest request, Errors errors){
	//ν‘œν˜„ μ˜μ—­μ€ μ‚¬μš©μž μš”μ²­μ„ μ‘μš© μ„œλΉ„μŠ€κ°€ μš”κ΅¬ν•˜λŠ” ν˜•μ‹μœΌλ‘œ λ³€ν™˜ν•¨.
    String curPw = request.getParameter("curPw");
    String newPw = request.getParameter("newPw");
    String memberId = SecurityContext.getAuthentication().getId();
    ChangePasswordRequest chPwdReq = 
    	new ChangePasswordRequest(memberId, curPw, newPw);
        
    try{
    	//μ‘μš© μ„œλΉ„μŠ€ μ‹€ν–‰
        changePasswordService.changePassword(chPwdReq);
        return successView;
    }catch(BacPasswordException | NoMemberException ex){
    	//μ‘μš© μ„œλΉ„μŠ€μ˜ 처리 κ²°κ³Όλ₯Ό μ•Œλ§žμ€ μ‘λ‹΅μœΌλ‘œ λ³€ν™˜
        errors.reject("idPasswordNotMatch");
        return formView;
    }
}

 

+ μ„œλΉ„μŠ€μ˜ μ‹€ν–‰ κ²°κ³Όλ₯Ό μ‚¬μš©μžμ—κ²Œ μ•Œλ§žμ€ ν˜•μ‹μœΌλ‘œ μ œκ³΅ν•˜λŠ” 것도 ν‘œν˜„ μ˜μ—­μ˜ λͺ«

+ μ‚¬μš©μžμ˜ μ—°κ²° μƒνƒœμΈ μ„Έμ…˜ 관리

 

🎈 6.5. κ°’ 검증

μ›μΉ™μ μœΌλ‘œ λͺ¨λ“  값에 λŒ€ν•œ 검증은 μ‘μš© μ„œλΉ„μŠ€μ—μ„œ 처리.

 

μ‚¬μš©μžκ°€ μ—¬λŸ¬ 값을 μž…λ ₯ν•΄μ•Ό ν•  λ•Œ, μ‘μš© μ„œλΉ„μŠ€μ—μ„œ μ—λŸ¬ 처리λ₯Ό 해버리면 첫 번재 값이 μ˜¬λ°”λ₯΄μ§€ μ•Šμ•„ μ΅μ…‰μ…˜μ΄ λ°œμƒν•˜λ©΄ λ‚˜λ¨Έμ§€ ν•­λͺ©μ— λŒ€ν•΄μ„œλŠ” κ²€μ‚¬ν•˜μ§€ μ•Šκ²Œ 됨. -> 잘λͺ»λœ λͺ¨λ“  값을 확인할 수 μžˆλ„λ‘ ν•˜λŠ” 방법.

@Transactional
public OrderNo placeOrder(OrderRequest orderRequest){
	List<ValidationError> errors = new ArrayList<>();
    if(orderRequest ==  null){
    	errors.add(ValidationError.of("empty");
    } else {
    	if(orderRequest.getOrdererMemberId() == null)
        	errors.add(ValidationError.of("ordererMemberId", "empty"));
        if(orderRequest.getOrderProducts() == null)
        	errors.add(ValidationError.of("orderProducts", "empty"));
        if(orderRequest.getOrderProducts().isEmpty())
        	errors.add(ValidationError.of("orderProducts", "empty"));
    }
    //μ‘μš© μ„œλΉ„μŠ€κ°€ μž…λ ₯ 였λ₯˜λ₯Ό ν•˜λ‚˜μ˜ μ΅μ…‰μ…˜μœΌλ‘œ λͺ¨μ•„μ„œ λ°œμƒ
    if(!errors.isEmpty()) throw new ValidationErrorException(errors);
    ...
}

 

ν‘œν˜„ μ˜μ—­μ—μ„œ ν•„μˆ˜ κ°’κ³Ό κ°’μ˜ ν˜•μ‹μ„ κ²€μ‚¬ν•˜λ©΄ μ‹€μ§ˆμ μœΌλ‘œ μ‘μš© μ„œλΉ„μŠ€λŠ” id 쀑볡 여뢀와 같은 논리적 였λ₯˜λ§Œ κ²€μ‚¬ν•˜λ©΄ λœλ‹€.

 

🎈 6.6. κΆŒν•œ 검사

보톡 λ‹€μŒ μ„Έ κ³³μ—μ„œ κΆŒν•œ 검사λ₯Ό μˆ˜ν–‰ν•  수 μžˆλ‹€. 

- ν‘œν˜„ μ˜μ—­

- μ‘μš© μ„œλΉ„μŠ€

- 도메인

 

1. ν‘œν˜„ μ˜μ—­μ—μ„œ ν•  수 μžˆλŠ” 기본적인 κ²€μ‚¬λŠ” 인증된 μ‚¬μš©μžμΈμ§€ μ•„λ‹Œμ§€ κ²€μ‚¬ν•˜λŠ” 것.

url을 μ²˜λ¦¬ν•˜λŠ” μ»¨νŠΈλ‘€λŸ¬μ— μ›Ή μš”μ²­μ„ μ „λ‹¬ν•˜κΈ° 전에 인증 μ—¬λΆ€λ₯Ό κ²€μ‚¬ν•΄μ„œ 인증된 μ‚¬μš©μžμ˜ μ›Ή μš”μ²­λ§Œ μ»¨νŠΈλ‘€λŸ¬μ— μ „λ‹¬ν•œλ‹€, 인증된 μ‚¬μš©μž 아닐 경우 둜그인 ν™”λ©΄μœΌλ‘œ λ¦¬λ‹€μ΄λ ‰νŠΈν•œλ‹€. 

-> μ„œλΈ”λ¦Ώ ν•„ν„° Servlet Filter. μŠ€ν”„λ§ μ‹œνλ¦¬ν‹°λ„ 이와 μœ μ‚¬ν•œ λ°©μ‹μœΌλ‘œ ν•„ν„°λ₯Ό μ΄μš©ν•΄ 인증 정보 μƒμ„±ν•˜κ³  μ›Ή 접근을 μ œμ–΄.

 

2. URL 만으둜 μ ‘κ·Ό μ œμ–΄λ₯Ό ν•  수 μ—†λŠ” 경우 μ‘μš© μ„œλΉ„μŠ€μ—μ„œ λ©”μ„œλ“œ λ‹¨μœ„λ‘œ κΆŒν•œ 검사.

예λ₯Ό λ“€λ©΄ μŠ€ν”„λ§ μ‹œνλ¦¬ν‹°μ˜ @PreAuthorize μ• λ„ˆν…Œμ΄μ…˜.

 

3. κ°œλ³„ 도메인 객체 λ‹¨μœ„λ‘œ κΆŒν•œ 검사. 

 

이해도가 λ†’μ§€ μ•Šμ•„ ν”„λ ˆμž„μ›Œν¬ ν™•μž₯을 μ›ν•˜λŠ” μˆ˜μ€€μœΌλ‘œ ν•  수 μ—†λ‹€λ©΄ ν”„λ ˆμž„μ›Œν¬λ₯Ό μ‚¬μš©ν•˜λŠ” λŒ€μ‹  도메인에 λ§žλŠ” κΆŒν•œ 검사 κΈ°λŠ₯을 직접 κ΅¬ν˜„ν•˜λŠ” 것이 μ½”λ“œ μœ μ§€ λ³΄μˆ˜μ— μœ λ¦¬ν•˜λ‹€. 

 

🎈 6.7. 쑰회 μ „μš© κΈ°λŠ₯κ³Ό μ‘μš© μ„œλΉ„μŠ€

쑰회λ₯Ό μœ„ν•œ μ‘μš© μ„œλΉ„μŠ€κ°€ 단지 쑰회 μ „μš© κΈ°λŠ₯을 μ‹€ν–‰ν•˜λŠ” μ½”λ“œλ°–μ— μ—†λ‹€λ©΄ μ‘μš© μ„œλΉ„μŠ€λ₯Ό μƒλž΅ν•΄λ„ 무방함.