블로그

[도서 정리] 도메인 주도 개발 시작하기 - 최범균

최범균님의 도메인 주도 설계 관련 도서를 읽고 내용을 정리했다.도메인 주도 DDD Start! 도메인 주도 설계 구현과 핵심 개념 익히기 (2016년)도메인 주도 개발 시작하기 (2022년)DDD Start를 보완하여 출판한 도서이기 때문에 도메인 주도 개발 시작하기를 보면 된다 새롭게 알게 된 개념애그리거트같은 생명주기를 공유하는 도메인들을 의미하며, 팩토리 메서드 등으로 루트엔티티에서 로직의 일관성을 지키며 관리하도록 한다도메인 서비스기존에도 서비스를 분리하거나 필요한 유틸들을 생성하여 사용했었지만 도메인 로직을 서비스에서 관리한다는 개념 자체는 단순하지만 생각하지 못했던 부분이었던 것 같다.도메인 모델 로직을 한없이 늘리거나 응용 계층에서 일반 로직과 섞어서 복잡하게 짜지말고 도메인 서비스를 사용하는 것이 좋을 것 같다인프라스트럭처계층 아키텍처 기준으로 repository는 영속성 계층이었지만 도메인 주도 설계에서는 repository는 도메인 계층이며 실제 repository의 기능을 구현한 구현체는 인프라스트럭처 계층으로 나뉘었다도메인 주도 설계에서는 고수준 모듈과 저수준 모듈을 구분하고 고수준은 interface 로직을 호출할 뿐 저수준 구현체와는 아무런 관계를 하지 않는다  도메인 주도 설계란, 도메인을 중심으로 설계하는 것이다도메인이란 소프트웨어로 해결하고자 하는 문제 영역이다쉽게 말해서 쇼핑몰 서비스라 하면 상품, 주문, 배송 등이 해당 서비스에서 해결하고자하는 문제 영역이라 할 수 있다 도메인과 엔티티는 다르다도메인 모델 엔티티는 테이블만이 기준이 아니라 같은 단위의 속성도 묶는다엔티티의 특징은 식별자를 갖는다는 것이다Value의 특징은 값들의 묶음이라는 것이다테이블이 있다하더라도 모두 엔티티는 아니다한 엔티티에 포함되지만 DB 정규화에서 나눠진 테이블들이 있다객체로 속성을 관리하게 되면 가독성이 높아지고 상태 관리의 일관성을 높일 수 있다 애그리거트운명공동체인 객체의 묶음이다루트 엔티티에서 기능을 제공한다다른 객체에서 변경할 수 있도록 하면 일관성이 깨지고 중복 로직이 발생한다루트 엔티티가 변경 주체이지만 실제 변경은 대상 객체에게 위임할 수 있다트랜잭션 범위는 작을수록 좋으며, 한 트랜잭션에서 두 개 이상의 애그리거트를 건들지 않도록 한다서비스에서 서로 다른 애그리거트를 호출하여 변경하도록 한다JPA의 지연로딩의 개념과 같다. 즉시로딩은 객체 간의 결합도가 높아진다N+1이 발생한다면 한 번에 호출하는 join 쿼리를 생성하여 조회하도록 한다N:M 관계에서는 중간 테이블을 두어 다루는 것이 좋다논리적인 하나의 도메인은 나열식으로 생성하기보다 하나의 애그리거트에서 팩토리 메서드로 함께 생성하는 것이 좋다  도메인 주도 설계에서의 계층표현 계층, 응용 계층, 도메인 계층, 인프라 스트럭처 계층 고수준 모듈은 응용 계층과 도메인 계층이며, 저수준 모듈은 인프라스트럭처 계층이다interface가 호출되는 곳은 고수준 모듈이어야한다표현 계층, 응용 계층에 도메인 로직은 넣지 않도록 한다응용 계층에서 중복 로직이 발생한다면 메서드로 분리할 필요가 있으며, 서비스 로직이 커진다면 서비스 클래스 수준에서 분리가 필요하다응용 계층은 트랜잭션을 관리한다응용 계층(서비스 계층)은 표현 계층에 필요한 데이터만 전달한다계층 간 필요한 정보만 교환하고 서로 의존성을 줄인다보통 표현 계층에서는 값을 검증하고, 응용 계층에서는 논리적 검증/존재 검증을 한다리포지토리는 domain 계층이며, 리포지토리를 구현한 클래스는 infrastructure 계층이다infrastructure 계층 : 영속성을 구현하거나 외부와 통신하는 기능을 제공하는 레이어도메인 계층에는 도메인 모델과 도메인 서비스가 있다저수준을 추상화하는 것을 경계해야한다!저수준을 추상화하면 도메인이 구현체에 의존하게 된다 핵심 서비스 로직에서 -> 도메인 모델 로직과 부가 서비스 로직을 호출한다주문과 할인 도메인 로직이 있을 때 주문에 할인된 금액 정보가 필요하다면 해당 로직을 주문 애그리거트에 할당하지 않아야한다도메인 서비스를 생성하여 기능을 구현하는 것이 맞다 => 주문 도메인 서비스에서 할인금액을 계산하도록 한다할인 로직이 달라지는 경우, 로직을 추가하기보다 interface로 구현체를 조립할 수 있다 Bounded Context; 모델은 컨텍스트(문맥)에서 결정된다상품 모델은 카탈로그 상품, 주문 상품, 배송 상품 등 다양한 상품이 있다각 컨텍스트에서의 상품이 각각의 모델이 된다 이벤트이벤트가 발생할 때 후처리 관련한 트리거를 발생시키거나 데이터 동기화 작업을 한다예를 들어 결제는 외부 서비스를 이용하는 경우가 많은데 외부 서비스에 문제가 생겼을 때 롤백을 할 것인지, 추후에 재시도할 것인지를 결정해야한다이러한 경우 트랜잭션이나 여러 경우를 고려하여 로직을 짜게 되면 복잡한 로직으로 이어지게 되기 때문에 이벤트를 발생시켜 처리하도록 한다spring에서 ApplicationEventPublisher와 @EventListener를 사용할 수 있다비동기 처리는 다음과 같은 방식이 있다코드 수준에서 @Async를 사용메세지 큐 사용이벤트 저장소 DB 및 별도 프로그램 구축재처리 정책이나 멱등성 등을 고려하여 설계하도록 한다 CQRSCommand and Query Responsibility Segregation명령과 조회의 책임 분리DB에 대한 Write와 Read가 분리되는 것이다데이터 변경이 있을 시 -> 메세지 이벤트 발생 -> 연관 데이터 변경   

도서DDD

객체 지향 프로그래밍 입문(최범균) 1 - 객체지향, 캡슐화

좋은 코드란, 낮은 비용으로 변화할 수 있는 코드이다 이것은 1. 캡슐화 2. 추상화(다형성 지향)로 이루어낼 수 있다.   절차지향적 코드는 진행될수록 여러 조건문으로 복잡해질 수 있다. 객체지향적 코드는 객체가 제공하는 기능(메서드)이 중심이 되어 설계하는 것이다.  - 호출, 리턴, 익셉션 등의 메세지의 교환 - 데이터 클래스(VO, DTO)는 객체가 아니다. 객체의 기능이 없이 값에만 접근하기 때문이다.   캡슐화는 데이터와 관련된 기능을 묶는 것이다. 데이터의 상세 내용을 외부에 감추고, 외부와 무관하게 객체 내부의 구현 변경이 가능하다. 객체의 기능을 비즈니스 로직이 아닌 객체 내부의 메서드로 구현하면, 기능에 변화가 요구될 때 해당하는 내부 기능을 변경하면 캡슐화를 사용한 곳에 별도의 수정이 필요하지 않다.   캡슐화의 규칙 1. 데이터를 요구하는 것이 아닌 데이터의 처리를 요구할 것if(member.getAge() > 19) Xif(member.isAdult()) O 2. 메서드에서 생성한 객체의 메서드만 호출할 것파라미터로 받은 객체의 메서드만 호출할 것필드로 참조하는 객체의 메서드만 호출할 것 >> 연속적인 메서드 호출이 아닌 객체에 있는 하나의 메서드로 처리member.isAdult() + member.isVIP() + member.addCoupon()으로 하나씩 처리하는 것보다member.receiveBenefits()로 위 세 개 기능 묶기     객체는 속성과 기능으로 구성되어있다. 객체의 여러 기능을 참조하고 묶어서 새로운 기능에 사용하는 것은 객체 지향적인 방식이다.        

java객체지향최범균강의캡슐화DDD