애그리거트 애그리거트는 여러 객체로 구성된다. 그렇기 때문에 한 객체만 상태가 정상이면 안되고, 애그리거트에 속한 모든 객체가 정상 상태를 가져야 도메인 규칙을 지킬 수 있다. [예시] 주문 애그리거트를 보자. Order 엔티티 - 총 금액 totalAmounts를 갖고있다. OrderLine 밸류 - 개별 구매 상품의 개수인 quantity와 금액인 price를 갖고있다. 구매할 상품의 개수를 변경하면 한 OrderLine의 quantity를 변경하고 더불어 Order의 totalAmounts도 변경해야한다. 그렇지 않으면 다음 도메인 규칙을 어기고 데이터 일관성이 깨진다. 루트 애그리거트 애그리거트에 속한 모든 객체가 일관성 상태를 유지하려면 애그리거트 전체를 관리할 주체가 필요한데, 이 책임을 지는..
Mono vs Flux 객체 차이 설명 Mono 0 ~ 1 개의 데이터 전달 Reactive Streams 의 Publisher 인터페이스를 구현하는 구현체 Flux 0 ~ N 개의 데이터 전달 Reactive Streams 의 Publisher 인터페이스를 구현하는 구현체- 하나의 데이터를 전달할 때마다 onNext() 이벤트를 발생한다.- Flux 내의 모든 데이터의 전달 처리가 완료되면 onComplete() 이벤트가 발생한다. 'Mono 예제 맛보기' 포스팅 바로가기 https://devfunny.tistory.com/882 Flux 예제 맛보기 Event.java @Data @AllArgsConstructor public class Event { long id; String value; } Mo..
Mono vs Flux 객체 차이 설명 Mono 0 ~ 1 개의 데이터 전달 Reactive Streams 의 Publisher 인터페이스를 구현하는 구현체 Flux 0 ~ N 개의 데이터 전달 Reactive Streams 의 Publisher 인터페이스를 구현하는 구현체 - 하나의 데이터를 전달할 때마다 onNext() 이벤트를 발생한다. - Flux 내의 모든 데이터의 전달 처리가 완료되면 onComplete() 이벤트가 발생한다. 'Flux 예제 맛보기' 포스팅 바로가기 https://devfunny.tistory.com/883 gradle 설정 implementation 'org.springframework.boot:spring-boot-starter-webflux' Mono 예제 맛보기 1) ..
CompletableFuture Java5에 등장한 Future의 단점을 보완한 java8의 클래스다. Future은 get()을 호출하여 작업을 완료하고, get()은 비동기 작업의 결과를 가져올때까지 블로킹 상태가 된다. 여러 Future을 조합할 수 없고, 예외 처리도 불가능하여 이를 보완한 CompletableFuture을 사용하여 비동기 작업을 직접 완료할 수 있다. public class CompletableFuture implements Future, CompletionStage { CompletableFuture는 CompletionStage 인터페이스를 구현한다. CompletionStage는 비동기 연산을 체이닝으로 연속해서 작업들을 중첩시킬 수 있다. public interface C..
애그리거트가 필요한 이유 아래 그림을 보면, 주문이 회원, 상품, 결제와 관련된 것임을 쉽게 알 수 있다. 상위 수준 모델을 개별 객체 단위로 다시 그려보자. 개별 객체 수준에서 모델을 바라보면 상위 수준에서 관계를 파악하기 어렵다. 도메인 객체 모델이 복잡해지면 개별 구성요소 위주로 모델을 이해하게 되고, 전반적인 구조나 큰 수준에서 도메인 간의 관계를 파악하기 어려워진다. 주요 도메인을 파악하기 어렵다는 것은 코드를 변경하고 확장하는 것이 어려워진다는 것을 의미한다. 상위 수준에서 모델이 어떻게 엮여 있는지 알아야 전체 모델을 망가뜨리지 않으면서 추가 요구사항을 모델에 반영할 수 있는데, 세부적인 모델만 이해한 상태로는 코드를 수정하는 것이 꺼려지기 때문에 코드 변경을 최대한 회피하는 쪽으로 요구사항..
비동기 구현 예제 1) Runnable @FunctionalInterface public interface Runnable { public abstract void run(); } 매개변수, 리턴값이 없는 로직 ExecutorService es = Executors.newCachedThreadPool(); /** 별도의 스레드로 실행해보자. */ es.execute(() -> { // 매개변수, 리턴값이 없는 Runnable 구현 try { Thread.sleep(2000); } catch (InterruptedException e) { throw new RuntimeException(e); } log.info("Async"); // return "Hello"; // Runnable 은 리턴이 없다. }..
subscribe()의 과정을 별도 스레드로 수행 1) publisher 생성 Publisher pub = sub -> { sub.onSubscribe(new Subscription() { @Override public void request(long n) { log.debug("request()"); sub.onNext(1); sub.onNext(2); sub.onNext(3); sub.onNext(4); sub.onNext(5); sub.onComplete(); } @Override public void cancel() { } }); }; // pub 2) Subscriber 생성 subOnPub.subscribe(new Subscriber() { @Override public void onSubsc..
Pub/Sub 1) Publisher 생성 Publisher pub = iterPub(Stream.iterate(1, a -> a + 1) .limit(10) .collect(Collectors.toList())); iterPub() private static Publisher iterPub(List iter) { Publisher pub = new Publisher() { // Publisher 의 구현해야하는 메서드 @Override public void subscribe(Subscriber
Iterable 리스트를 생성해보자. // 리스트로 받는 방법 List list = Arrays.asList(1, 2, 3, 4, 5); List 객체는 Collection을 상속한다. public interface List extends Collection { Collection 객체는 Iterable을 상속한다. 그러므로 List 타입은 Iterable의 서브타입이라고 할 수 있다. public interface Collection extends Iterable { Iterable은 아래와 같이 for-each 문에서 데이터를 순회할 수 있다. for (Integer i : list) { // for-each System.out.println(i); } Iterable 객체를 직접 만들어보자. ▶ 1..
응용 서비스 사용자 입장에서 봤을때 웹 애플리케이션이나 데스크톱 애플리케이션과 같은 소프트웨어는 기능을 제공한다. 사용자가 애플리케이션에 기능 실행을 요청하면 그 요청을 처음 받는 영역은 표현 영역이다. 스프링 MVC를 사용해서 웹 애플리케이션을 구현했다면 컨트롤러가 사용자의 요청을 받아 처리하게 된다. 표현 영역은 사용자가 전송한 데이터 형식이 올바른지 검사하고 문제가 없다면 데이터를 이용해서 응용 서비스에 기능 실행을 위임한다. 이때 표현 영역은 사용자가 전송한 데이터를 응용 서비스가 요구하는 형식으로 변환해서 전달한다. 웹 브라우저를 이용해서 기능 실행을 요청하면 표현 영역에 해당하는 컨트롤러는 과정 1.1에서 HTTP 요청 파라미터를 응용 서비스가 필요로 하는 데이터로 변환해서 응용 서비스를 실행..
구성요소 도메인 영역은 도메인의 핵심 모델을 구현한다. 도메인 영역의 모델은 도메인의 주요 개념을 표현하며 핵심 로직을 구현한다. 도메인 영역을 구성하는 요소 요소 설명 엔티티 ENTITY 고유의 식별자를 갖는 객체로 자신의 라이프 사이클을 갖는다. 주문, 회원, 상품과 같이 도메인의 고유한 개념을 표현한다. 도메인 모델의 데이터를 포함하며 해당 데이터와 관련된 기능을 함께 제공한다. 밸류 VALUE 고유의 식별자를 갖지 않는 객체로 주요 개념적으로 하나의 값을 표현할때 사용된다. 배송지 주소를 표현하기 위한 주소나 구매 금액을 위한 금액와 같은 타입이 밸류 타입이다. 엔티티의 속성으로 사용할 뿐만 아니라 다른 밸류 타입의 속성으로도 사용할 수 있다. 애그리거트 AGGREAGATE 애그리거트는 연관된 엔..
인프라스트럭처 의존성 문제점 표현 계층, 응용 계층, 도메인 계층이 상세한 구현 기술을 다루는 인프라스트럭처 계층에 의존성이 존재하는 경우의 문제점 https://devfunny.tistory.com/872 [Domain Driven Design] 4. 계층 구조 아키텍처 (표현, 응용, 도메인, 인프라스트럭처) 네 개의 영역 '표현', '응용', '도메인', '인프라스트럭쳐' 는 아키텍처를 설계할 때 출현하는 전형적인 네가지 영역이다. 표현 영역(또는 UI 영역)은 사용자의 요청을 받아 응용 영역에 전달하고 devfunny.tistory.com 문제 상황 가격 할인 계산을 하려면 고객 정보를 구해야하고, 구한 고객 정보와 주문 정보를 이용해서 룰을 실행해야한다. 여기서 CalculateDiscountS..
네 개의 영역 '표현', '응용', '도메인', '인프라스트럭쳐' 는 아키텍처를 설계할 때 출현하는 전형적인 네가지 영역이다. 표현 영역(또는 UI 영역)은 사용자의 요청을 받아 응용 영역에 전달하고 응용 영역의 처리 결과를 다시 보여주는 역할을 한다. 웹 애플리케이션을 개발할때 많이 사용하는 스프링 MVC 프레임워크가 표현 영역을 위한 기술에 해당한다. 웹 애플리케이션에서 표현 영역의 사용자는 웹 브라우저를 사용하는 사람일 수도 있고, REST API를 호출하는 외부 시스템일 수도 있다. 웹 애플리케이션의 표현 영역은 HTTP 요청을 응용 영역이 필요로 하는 형식으로 변환해서 응용 영역에 전달하고 응용 영역의 응답을 HTTP 응답으로 변환하여 전송한다. 표현 영역을 통해 사용자의 요청을 전달받는 응용 ..
엔티티와 밸류 도출한 모델은 크게 엔티티(Entity)와 밸류(Value)로 구분할 수 있다. 앞서 요구사항 분석 과정에서 만든 모델은 엔티티도 존재하고 밸류도 존재한다. 앤티티와 밸류를 제대로 구분해야 도메인을 올바르게 설계하고 구현할 수 있기 때문에 이 둘의 차이를 명확하게 이해하는 것은 도메인을 구현하는 데 있어 중요하다. 엔티티 엔티티는 식별자를 가진다. 식별자는 엔티티 객체마다 고유해서 각 엔티티는 서로 다른 식별자를 갖는다. 주문에서 배송지 주소가 바뀌거나 상태가 바뀌더라도 주문번호(식별자)가 바뀌지 않는 것처럼 엔티티의 식별자는 바뀌지 않는다. 엔티티를 생성하고 속성을 바꾸고 삭제할 떄까지 식별자는 유지된다. 엔티티의 식별자는 바뀌지 않고 고유하기 때문에 두 엔티티 객체의 식별자가 같으면 두..
도메인 모델 도메인 모델은 특정 도메인을 개념적으로 표현한 것이다. 예시로 '주문 도메인'을 보자. 주문을 하기위해 상품을 몇 개 살지 선택하고 배송지를 입력한다. 선택한 상품 가격을 이용해서 총 지불 금액을 계산하고, 금액 지불을 위한 결제 수단을 선택한다. 주문한 뒤에도 배송 전이면 배송지 주소를 변경하거나 주문을 취소할 수 있다. 위 그림은 객체를 이용한 도메인 모델이다. 도메인을 이해하려면 도메인이 제공하는 기능과 도메인의 주요 데이터 구성을 파악해야 하는데, 이런 면에서 기능과 데이터를 함께 보여주는 객체 모델은 도메인을 모델링하기에 적합하다. 도메인 모델을 객체로만 모델링할 수 있는 것은 아니다. 상태 다이어그램을 이용해서 주문의 상태 전이를 모델링하고 있다. 이 다이어그램을 보면 상품 준비 ..
* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.