애그리거트와 트랜잭션 한 주문 애그리거트에 대해 운영자는 배송 상태로 변경할 때 사용자는 배송지 주소를 변경하면 어떻게 될까? 그림을 보면 운영자와 고객이 동시에 한 주문 애그리거트를 수정하는 과정을 보여준다. 트랜잭션마다 리포지터리는 새로운 애그리거트 객체를 생성하므로 운영자 스레드와 고객 스레드는 같은 주문 애그리거트를 나타내는 다른 객체를 구하게 된다. 운영자 스레드와 고객 스레드는 개념적으로 동일한 애그리거트지만 물리적으로 서로 다른 애그리거트 객체를 사용한다. 때문에 운영자 스레드가 주문 애그리거트 객체를 배송 상태로 변경하더라도 고객 스레드가 사용하는 주문 애그리거트 객체에는 영향을 주지 않는다. 문제점은 운영자는 기존 배송지 정보를 활용해 배송 상태로 변경했는데 그 사이 고객은 배송지 정보를..
여러 애그리거트가 필요한 기능 도메인 영역의 코드를 작성하다 보면, 한 애그리거트로 기능을 구현할 수 없을 때가 있다. 결제 금액 계산 로직을 생각해보자. 우선 구매하려는 상품의 가격이 필요하므로 상품 애그리거트가 필요하다. 또 상품 별로 몇 개를 구매할 것인지 알아야 하므로 주문 애그리거트가 필요하다. 또 할인 금액이나 비율, 제약 조건 등이 필요하므로 할인 애그리거트가 필요하고, 회원 등급에 따라 또 할인 등급이 나뉘어진다면 회원 애그리거트도 필요할 것이다. 해당 상황에서 실제 결제 금액을 계산하는 주체는 누구일까? 주문 애그리거트가 주체라 가정해보자. 그렇다면 의문이 생긴다. 결제 금액 계산 로직이 주문 애그리거트의 책임에 들어갈까? 예를 들어 특별 감사 세일로 전 품목에 대해 한 달간 2% 추가 ..
표현 영역과 응용 영역 도메인이 제 기능을 하려면 사용자와 도메인을 연결해주는 매개체가 필요하다. 표현 영역과 응용 영역이 사용자와 도메인을 연결해 주는 매개체 역할을 한다. 표현 영역은 사용자의 요청을 해석한다. 웹 브라우저는 요청 파라미터를 포함한 HTTP 요청을 표현 영역에 전달한다. 요청을 받은 표현 영역은 URL, 요청 파라미터, 쿠키, 헤더 등을 이용해서 사용자가 실행하고 싶은 기능을 판별하고 그 기능을 제공하는 응용 서비스를 실행한다. 실제 사용자가 원하는 기능을 제공하는 것은 응용 영역에 위치한 서비스다. 응용 서비스는 기능을 실행하는 데 필요한 입력 값을 메서드 인자로 받고 실행 결과를 리턴한다. 응용 서비스의 메서드가 요구하는 파라미터와 표현 영역이 사용자로부터 전달받은 데이터는 형식이..
검색을 위한 스펙 검색 조건이 고정되어 있고, 단순하다면 특정 조건으로 조회하는 기능을 만들면 된다. public interface OrderDataDao { Optional findById(OrderNo id); List findByOrderer(String ordererId, Date fromDate, Date toDate); ... } 그런데 목록 조회와 같은 기능은 다양한 검색 조건을 조합해야할 때가 있다. 물론 필요한 조합마다 find 메서드를 만드는 것도 방법일 수는 있지만, 좋은 방법은 아니다. (조합이 증가할수록 정의해야할 find 메서드도 함께 증가하기 때문) 이렇게 검색 조건을 다양하게 조합해야 할 때 사용할 수 있는 것이 스펙(Specification)이다. 스펙은 애그리거트가 특정 ..
매핑 구현 애그리거트 루트는 엔티티이므로 @Entity로 매핑 설정한다. 한 테이블에 엔티티와 밸류 데이터가 같이 있다면 밸류는 @Embeddable로 매핑 설정한다. 밸류 타입 프로퍼티는 @Embedded로 매핑 설정한다. @Entity @Table(name = "purchase_order") public class Order { @Embedded private Orderer orderer; ... } @Embeddable public class Orderer { // memberId에 정의된 칼럼 이름을 변경하기 위해 @AttributeOverride 사용 @Embedded @AttributeOverrides( @AttributeOverride(name = "id", column = @Column(n..
spring batch를 통해 데이터를 가공하고 저장하는 업무가 있었다. 당시 나는 for문을 통해 단순히 한 건마다 save를 진행해서 코드를 작성했는데 생각보다 처리 시간이 너무나 오래 걸려 이를 최적화할 방법을 찾다 saveAll을 통해 어느정도 시간을 단축한 경험이 있다. 당시에는 그냥 saveAll을 쓰니 단축이 되네? 정도로만 하고 넘어갔지 실제 이유는 모른채 넘어갔기에 이참에 정리해보려한다. @Transactional @DisplayName("save 성능") @Test void saveTest(){ //Given long start = System.currentTimeMillis(); //When for(int i = 0; i < 1000000; i++) { Member member = n..
애그리거트 애그리거트를 활용하면 복잡한 도메인을 이해하고 관리하기 쉬운 단위로 만들려면 상위 수준에서 모델을 조망할 수 있다. 수많은 객체를 애그리거트로 묶어서 바라보면 상위 수준에서 도메인 모델 간의 관계를 파악할 수 있다. 애그리거트는 모델을 이해하는데 도움을 줄 뿐만 아니라 일관성을 관리하는 기준도 된다. 에그리거트 단위로 일관성을 관리하기 때문에, 애그리거트는 복잡한 도메인을 단순한 구조로 만들어준다. 애그리거트는 관련된 모델을 하나로 모았기 때문에, 한 애그리거트에 속하는 객체는 유사하거나 동일한 라이프 사이클을 갖는다. 즉, 애그리거트에 속한 구성 요소 대부분은 함께 생성하고 함께 제거한다. 경계를 설정할 때 기본이 되는 것은 도메인 규칙과 요구사항이다. 도메인 규칙에 따라 함께 생성되는 구성..
네 개의 영역 표현 : 사용자의 요청을 받아 응용 영역에 전달하고 응용 영역의 처리 결과를 사용자에게 보여주는 역할 응용 : 사용자에게 제공할 기능 구현. 로직을 직접 수행하기 보다는 도메인 모델에 로직 수행 책임 위임 도메인 : 도메인의 핵심 로직 구현 인프라 : 논리적 개념 표현 보다는 실제 구현 다룸 (RDBMS 연동, 메세지 큐 송수신, 데이터 연동..) 계층 구조 아키텍처 아키텍처는 기본적으로 계층 구조이다. 계층 구조는 특성상 상위 계층에서 하위 계층으로의 의존만 존재하고, 하위 계층은 상위 계층에 의존하지 않는다. 하지만 구현의 편리함을 위해 때로는 계층 구조를 유연하게 적용하기도 한다. 도메인과 응용 계층이 현재 인프라 계층에 의존하고 있다. 하지만 의존한다라는 것은, 달리 표현해 말하자면..