본문 바로가기

BackEnd

도메인 모델을 함수형 아키텍처로 - 컨텍스트 간 통신 1

반응형

https://fsharpforfunandprofit.com/books/

 

Books | F# for fun and profit

This book starts with a discussion of Domain Driven Design, and then shows to how to model a design using types. The last part shows how to implement the design using functional programming with F# (composition of functions, “railway-oriented programming

fsharpforfunandprofit.com

빠르게 변화하는 개발 주기에서 우리는 나머지 부분을 이해하기 전에 도메인의 일부를 구현하기 시작해야 하는 경우가 많습니다.

따라서 다양한 구성 요소를 구축하기 전에도 어떻게 맞출 것인가 계획을 세워야 합니다.

그리고 시스템이 전체적으로 어떻게 작동하는지 보여주는 조잡한 프로토타입을 만드는 것도 중요합니다.

구체적인 구현에 대한 조기 피드백은 지식의 격차를 발견하는 좋은 방법입니다.

 

 경계 컨텍스트 및 도메인 이벤트 와 같은 DDD 개념 이 소프트웨어로 변환되는 방법을 살펴보고,

다른 구성요소와 어떻게 결합할 것인지 스케치합니다.

 

소프트웨어 아키텍처는 그 자체로 도메인이므로 우리 자신의 조언을 따르고 그것에 대해 이야기할 때 "유비쿼터스 언어"를 사용합시다.

 

우리는 Simon Brown의 "C4" 접근 방식의 용어를 사용할 것입니다.

소프트웨어 아키텍처는 다음과 같이 설명할 수 있는 4가지 레벨로 구성됩니다.

  • "시스템 컨텍스트"는 전체 시스템을 나타내는 최상위 수준입니다.
  • 시스템 컨텍스트는 웹사이트, 웹 서비스, 데이터베이스 등과 같은 배포 가능한 단위인 다수의 "컨테이너"로 구성됩니다.
  • 각 컨테이너는 차례로 코드의 주요 구조적 구성 요소인 여러 "컴포넌트"로 구성됩니다.
  • 마지막으로, 각 "컴포넌트"는 일련의 저수준 메서드 또는 함수를 포함하는 여러 "클래스"(또는 함수형 아키텍처에서는 "모듈")로 구성됩니다.
좋은 아키텍처의 목표 중 하나는 새로운 요구 사항이 발생할 때 "변경 비용"이 최소화되도록 컨테이너, 구성 요소 및 모듈 간의 다양한 경계를 정의하는 것입니다.

바운디드 컨텍스트는 자율적인 소프트웨어 컴포넌트 입니다.

컨텍스트가 경계가 잘 정의된 자율적인 하위 시스템이라는 것이 중요합니다.

바운디드 컨텍스트가 분리되고 자율적으로 유지되는 한 논리적 디자인에서 배포 가능한 등가물로의 변환은 중요하지 않습니다.

즉 모놀리식이던, 컨테이너 기반이던 쉽게 아키텍처를 변경할 수 있습니다.

(이벤트 기반 커뮤니케이션)

 

진정으로 분리된 마이크로 서비스 아키텍처를 만드는 것은 까다롭습니다.
마이크로 서비스 중 하나를 끄고 다른 것이 중단되면 실제로 마이크로 서비스 아키텍처가 아니라 분산 모놀리스만 있는 것입니다!

바운디드 컨텍스트 간 커뮤니케이션

바운디드 컨텍스트 간에는 이벤트로 통신합니다.

워크플로는 커맨드를 입력으로 받아, event를 출력합니다.

(정확하게는 이벤트 -> 커맨드 -> 이벤트 입니다.)

바운디드 컨텍스트 간에는 이벤트로 통신합니다.

  • order-taking  컨텍스트의 Place-Order 워크플로는 OrderPlaced 이벤트를 내보냅니다.
  • OrderPlaced 이벤트는 대기열에 놓이거나 퍼블리시 됩니다.
  • Shipping 컨텍스트는 OrderPlaced 이벤트를 수신합니다. 이벤트가 수신되면 ShipOrder 커맨드가 생성됩니다.
  • ShipOrder 커맨드는 Ship-Order 워크플로를 시작합니다.
  • Ship-Order 워크플로가 성공적으로 완료되면 OrderShipped 이벤트를 내보냅니다.

 

이것은 완전히 분리된 디자인임을 알 수 있습니다.
업스트림 구성 요소(주문 접수 하위 시스템)와 다운스트림 구성 요소(배송 하위 시스템)는
서로를 알지 못하고 이벤트를 통해서만 통신합니다.
이러한 종류의 분리는 진정으로 자율적인 구성 요소를 원하는 경우 중요합니다.
 
두 컴포넌트 간의 연결은 API 호출 혹은 메세지 큐 방식을 사용합니다. (모놀리식에서도 가능합니다. - 바운디드 컨텍스트로 분리되어 있다면요!)
 
이벤트를 커맨드로 바꾸는 데는 seperate router 패턴 (분리된 메세지 큐 사용 - 카프카?), process manager (사가 오케스트레이터) 패턴을 사용합니다.
 
 
 
 

바운디드 컨텍스트 간에 메세지 전송

일반적으로 컨텍스트 간의 통신에 사용되는 이벤트는 단순한 신호가 아니라 다운스트림 구성 요소가 이벤트를 처리하는 데 필요한 모든 데이터를 포함합니다.
 
예를 들어 OrderPlaced 이벤트에는 완료된 주문이 포함될 수 있습니다.
그러면 해당 ShipOrder 명령을 구성하는 데 필요한 모든 정보가 배송 컨텍스트에 제공됩니다.
(데이터가 너무 커서 이벤트에 포함되지 않는 경우 공유 데이터 저장 위치에 대한 일종의 참조가 대신 전송될 수 있습니다.)
전달되는 데이터 개체는 바운디드 컨텍스트 내부에 정의된 개체(도메인 개체라고 함)와 표면적으로 유사할 수 있지만 동일하지 않습니다.
인터컨텍스트 인프라스트럭처의 일부로 직렬화 및 공유되도록 특별히 설계되었습니다.
 
우리는 이러한 객체를 데이터 전송 객체 또는 DTO라고 부를 것입니다.
즉, OrderPlaced 이벤트에 포함된 OrderDTO는 Order 도메인 개체와 거의 동일한 정보를 포함하지만 목적에 맞게 다르게 구성됩니다. 
 
그러면 업스트림 컨텍스트의 경계에서 도메인 개체가 DTO로 변환되고, DTO는 차례로 JSON, XML 또는 기타 직렬화 포맷으로 직렬화됩니다.
DTO는 업스트림 컨텍스트의 인프라에 속합니다.

다운스트림 컨텍스트에서 프로세스는 다른 방향으로 반복됩니다.

JSON 또는 XML은 DTO로 역직렬화되고, DTO는 그림과 같이 차례로 도메인 개체로 변환됩니다.

DTO는 다운스트림 컨텍스트의 인프라에 속합니다.

실무에서, 직렬화되는 최상위 DTO는 일반적으로 이벤트 DTO이며, 여기에는 차례로 하위 DTO(예: Order용 DTO)가 포함되고 여기에는 추가 하위 DTO(예: OrderLines를 나타내는 DTO 목록)가 포함됩니다. (주 Order의 필드 OrderLines)

 

 

Trust Boundaries and Validation(신뢰 경계와 유효성 검사)

주 : 비즈니스 조건과 유효성 조건은 다릅니다. 코드 테이블 같은건 유효성 검사입니다.

바운디드 컨텍스트의 경계는 "신뢰 경계" 역할을 합니다.
바운디드 컨텍스트 내부의 모든 것은 신뢰할 수 있고 유효하지만 바운디드 컨텍스트 외부의 모든 것은 신뢰할 수 없고 유효하지 않을 수 있습니다.
따라서 신뢰할 수 있는 도메인과 신뢰할 수 없는 외부 세계 사이에서 중개자 역할을 하는 워크플로의 시작과 끝에 "게이트"를 추가합니다.
입력 게이트에서 도메인 모델의 제약 조건을 준수하는지 확인하기 위해 항상 입력의 유효성을 검사합니다.
예를 들어, Order의 특정 속성은 null이 아니어야 하고 50자 미만이어야 한다고 가정합니다.
들어오는 OrderDTO에는 이러한 제약 조건이 없으며 아무 것도 포함할 수 없지만 입력 게이트에서 유효성 검사를 수행한 후에는,
Order 도메인 개체가 유효한지 확신할 수 있어야 합니다.
유효성 검사가 실패하면 나머지 워크플로가 무시되고 오류가 생성됩니다.
 
출력 게이트의 작업은 다릅니다.
출력 게이트의 역할은 컨텍스트 간의 우발적인 연결을 방지하고 보안상의 이유로 개인 정보가 경계 컨텍스트 밖으로 누출되지 않도록 하는 것입니다.
예를 들어 배송 컨텍스트에서 주문 결제에 사용된 신용 카드 번호를 알 필요가 없습니다.
이를 수행하기 위해 출력 게이트는 종종 도메인 개체를 DTO로 변환하는 과정에서 정보(예: 카드 번호)를 의도적으로 "손실"합니다.
반응형