[DDD&MSA] 마이크로서비스 설계
Book/DDD & MSA

[DDD&MSA] 마이크로서비스 설계

소프트웨어 설계 모듈화의 근본적인 가치는 각 모듈을 기능적으로 응집성을 높게(High Cohesion), 타 기능과의 결합도는 낮게(Low Coupling) 하는 것이다.

마이크로서비스 설계에서도 마찬가지이다.


마이크로서비스 도출 방법

마이크로서비스가 비즈니스의 변화 속도를 지원하면서 독립적으로 변경 및 배포되려면 각 마이크로서비스가 다른 서비스와 의존하지 않게 도출해야 한다. 따라서 시스템의 어떤 비즈니스 기능들을 묶어서 독립적인 마이크로서비스로 도출할 것인가를 결정하는 것이 매우 중요하다.


비즈니스 능력에 근거한 도출

크리스 치러드슨은 비즈니스 능력(capability)에 따라 서비스로 식별할 수 있다고 주장한다.

비즈니스 능력은 '비즈니스 가치를 생산하기 위해 하는 일'이라고 정의하며, 곧 '조직이 하는 일'이라고 말한다.

 

각 도메인에서는 비즈니스가 규정하는 일하는 방식과 조직, 부서 체계가 이미 정의돼 있고, 이러한 부서는 이미 업무 처리에서의 응집성을 가지고 있고 타 부서와의 의존도(결합도)는 낮을 것이다.

 

이처럼 비즈니스 부서가 가진 역할, 처리 능력을 체계적으로 분해할 수 있으며, 보통 이를 업무 기능 분해라고 한다. 업무 기능 분해는 업무 흐름에 따라 업무를 최상위에서 하위까지 대, 중, 소의 크기로 분리하고 수행하는 일들을 체계적으로 정렬한다. 그 결과, 특정 부서가 처리하는 업무 역할과 비슷해진다.

 

하지만 이러한 방식은 전체적인 대략의 비즈니스를 이해할 때는 유용하지만, 서비스 간의 관계를 파악하거나 서비스의 구체 기능과 연관된 서비스가 관리할 독립적인 데이터를 식별하기에는 미흡하다.


DDD의 바운디드 컨텍스트 기반 도출

비즈니스 능력에 따른 서비스 도출 한계를 보완하기 위해 DDD의 전략적 설계를 적용할 수 있다.

 

DDD에서는 데이터를 기능과 분리해서 식별하지 않고 문제 영역인 하위 도메인마다 별도의 도메인 모델로 정의한다. 도메인 모델은 각 업무에 특화된 유비쿼터스 언어로 정의되고, 그 업무에 특화된 개념으로 구성된다.

(분리된 도메인 모델에 의해 다른 컨텍스트와 구별되는 경계를 바운디드 컨텍스트라고 한다.)

 

이러한 특성이 독립적인 팀이 자율적으로 마이크로서비스를 개발 및 운영하는 마이크로서비스의 개념과 매우 잘 어울린다.


마이크로서비스의 크기

  • 마이크로서비스의 크기는 코드의 크기처럼 양적(Quantity)으로 판단할 것이 아니라 전체 업무 맥락에서 질적(Quality)으로 판단해야 한다.
  • 질적 판단의 요소로는 자율적으로 수행 가능한 업무의 단위, 개념의 일관성이나 기능의 응집성 등을 말하며, 이는 비즈니스 도메인이나 조직의 성숙도에 따라 상대적이다.

DDD의 전략적 설계

DDD의 전략적 설계에서는 비즈니스 응집성이 있는 컨텍스트를 구분하고, 이를 바운디드 컨텍스트라 하는데 이 단위가 마이크로서비스를 식별하기 위한 훌륭한 단위가 될 수 있다.

또한 전략적 설계를 통해 식별된 마이크로서비스의 내부 구조를 정의하고 상세히 설계하기 위해 DDD의 객체 설계 기법인 전술적 설계를 사용할 수도 있다.

 

비즈니스 변화에 빠르게 대응하기 위해 클라우드와 마이크로서비스 아키텍처를 적용했다면 비즈니스의 변화 못지 않게 빠르게 변화하는 구현 기술에도 적시에 대응할 수 있는 유연한 애플리케이션 아키텍처가 필요하다.

 

DDD의 전술적 설계가 이러한 기술 변화에 유연한 구조의 애플리케이션을 설계하도록 도와주는 기법들을 제공한다.


도메인과 서브도메인

DDD 에서는 하나의 큰 도메인을 전략적으로 중요한 것들을 찾아 중요도에 따라 도메인을 나누고, 각 도메인을 각각 하나씩 해결하는 방법을 기본으로 삼는다.


서브도메인

시스템 개발을 통해 해결하고자 하는 비즈니스 도메인을 논리적으로 구분되는 개념으로 나누면 문제가 되는 영역을 쉽게 이해할 수 있다.

 

즉, 많은 개념들이 하나로 엮인 복잡한 비즈니스 도메인을 논리적으로 구분되는 여러 개의 하위 영역으로 분리한다는 뜻인데, 이렇게 분리된 하위 도메인을 서브도메인이라 한다.

 

서브도메인은 중요도에 따라 핵심 서브도메인, 지원 서브도메인, 일반 서브도메인의 3가지 유형으로 나뉜다.

  • 핵심 서브도메인: 다른 경쟁자와 차별화를 만들 비즈니스 영역으로, 기업의 프로젝트 목록에서 높은 우선순위를 갖는 영역이자, 소프트웨어 개발에서 전략적으로 가장 큰 투자가 필요한 영역을 말한다.
  • 지원 서브도메인: 비즈니스에 필수적이지만 핵심은 아닌 부분이지만, 핵심 도메인을 성고잇키기 위해서는 반드시 필요한 영역으로, 핵심 서브도메인 다음으로 중요한 영역이다.
  • 일반 서브도메인: 비즈니스적으로 특화된 부분은 아니지만 전체 비즈니스 솔루션에는 필요한 부분으로, 기존 제품을 구매해서 대체할 수 있다.

아마존 쇼핑몰의 추천 서비스나, 쿠팡의 로켓 배송 서비스와 같은 것이 핵심 서브도메인이라 볼 수 있을 것이다.


유비쿼터스 언어와 도메인 모델, 바운디드 컨텍스트

DDD 에서 전략적 설계를 수행하기 위해 반드시 알아야 할 개념이 유비쿼터스 언어와 바운디드 컨텍스트이다.


유비쿼터스 언어, Ubiquitous Language

  • DDD에서는 유비쿼터스 언어를 '특정 도메인에서 해당 도메인에서의 의도를 명확히 반영하고 도메인의 핵심 개념을 잘 전달할 수 있는 언어'라고 정의한다.
  • 예전 데이터 모델링에서 사용했던 표준 단어/용어 사전과는 다른 개념으로, 표준 단어/용어는 특정 프로젝트나 전체 시스템에서 하향식으로 규정됐던 개념이라면, 유비쿼터스 언어는 그보다 작은 단위의 세부 도메인에 특정 업무와 관련된 사람들 간에 자율적으로 정의되고 통용되는 개념을 나타낸다. 즉, 유비쿼터스 언어는 특정 도메인의 업무 개념을 표현하는 언어다.

도메인 모델

  • 도메인 모델은 특정 비즈니스 맥락에서 통용되는 개념들의 관계를 잘 정의한 모형이다. 도메인 모델을 보면 해당 비즈니스를 이해할 수 있도록 해야 한다.
  • 도메인 모델은 도메인과 관련된 업무를 수행하는 제품 책임자, 도메인 전문가, 개발자를 비롯한 모든 구성원들이 업무를 이해하는 기본 모형이 된다.

바운디드 컨텍스트

  • 도메인 모델들을 구성하다 보면 당연히 각 도메인 모델과 다른 도메인 모델 간의 경계가 보인다. 이곳에서 사용하는 언어와 저곳에서 사용하는 언어와 개념이 상이한 이 경계가 바운디드 컨텍스트이다.
  • 같은 컨텍스트를 다루는 이해관계자들은 도메인 모델에 정의된 언어로 업무 협의를 진행하고 개념을 발전시켜 나가며, 이 메돌의 언어를 그대로 사용해 설계 산출물을 표현하고 소스코드에도 사용한다.

 

아래 그림과 같이 이러한 도메인에 특화된 개념이 유비쿼터스 언어로 정의되고, 이 개념들은 서로 관계를 맺는다. 이 같은 관계를 표현한 모델을 도메인 모델이라 한다.


컨텍스트 매핑

바운디드 컨텍스트를 식별할 때 각 컨텍스트는 내부적으로는 응집성이 높고, 다른 컨텍스트와는 의존관계가 낮아야 한다는 원칙하에 설계한다.

 

하나의 큰 도메인을 여러 개의 바운디드 컨텍스트로 식별하면 비즈니스 수행을 위해 여러 개의 컨텍스트가 연계해야 하는 경우가 발생한다. 이러한 컨택스트 간의 의존관계를 DDD에서는 컨텍스트 매핑이라 하고, 연관관계에 있는 두 컨텍스트 사이에 선을 그려서 표시한다.

시스템을 구성하는 컨텍스트 간의 매핑 관계를 표시한 다이어그램을 컨텍스트 맵이라고 하며, 이 컨텍스트 맵을 그리려면 다양한 컨텍스트 매핑 패턴을 이해해야 한다.


주요 컨텍스트 매핑 관계

공유 커널(Shared kernel)

  • 공유 커널은 바운디드 컨텍스트 사이에 공통적인 모델을 공유하는 관계로, 두 개 이상의 팀에서 작지만 공통의 모델을 공유하는 관계를 나타낸다. 각 팀은 공유하는 모델에 서로 합의해야 한다.
  • 보통 공통 라이브러리 등이 여기에 해당하는데, 이 부분이 변경되면 여러 관련 컨텍스트에 영향을 미치므로 공유하는 모델의 코드 빌드를 관리하고 테스트하는 것은 한 팀이 맡아 수행해야 한다.


소비자와 공급자(Customer-Supplier)

  • 공급하는 컨텍스트는 상류(Upstream)로, 소비하는 컨텍스트는 하류(Downstream)로 표시한다.
  • 데이터의 흐름은 상류에서 하류로 흐른다. 반대는 가능하지 않기에 상류의 변화가 있으면 하류에서 변화를 따라야 한다. 즉, 공급자는 소비자가 원하는 기능을 제공해야 한다.


준수자(Confirmist)

  • 준수자는 소비자와 유사하지만, 상류 팀이 하류 팀의 요구를 지원하지 않거나 못하는 경우 사용한다. 이런 상황에서는 하류팀은 상류팀에서 제공하는 모델을 그대로 사용한다.


충돌 방지 계층(ACL; Anti-Corruption Layer)

  • 충돌 방지 계층은 하류 팀이 상류 팀의 모델에 영향을 받을 때, 하류 팀의 고유 모델을 지키기 위한 번역 계층을 만드는 것이다.
  • 이 계층은 둘 사이의 차이를 번역하며, 하류 모델의 독립성을 유지한다. 즉, 상류 모델의 변경 없이 하위 모델과 통합하기 위해 데이터를 변환하는 메커니즘을 구현한 것이라 할 수 있다.
  • 특히 클라우드 기반의 마이크로서비스 아키텍처를 적용하는 새로운 시스템을 레거시 시스템과 통합하기 위해 주로 사용한다.


공개 호스트 서비스(OHS; Open Host Service)

  • 공개 호스트 서비스는 바운디드 컨텍스트에 대한 접근을 제공하는 프로토콜이나 인터페이스를 정의한다.
  • 이 프로토콜은 하류의 컨텍스트가 상위 컨텍스트에서 제공하는 기능을 용이하게 사용할 수 있도록 공개돼 있다.
  • 보통 다른 컨텍스트에서 사용할 수 있는 공유된 API가 여기에 해당한다.


발행된 언어(PL; Published Language)

  • 발행된 언어는 하류의 컨텍스트가 상류 컨텍스트가 제공하는 기능을 사용하게 하기 위한 간단한 사용과 번역을 가능케 하는 문서화된 정보 교환 언어이다.
  • XML이나 JSON 스키마로 표현될 수 있으며, 주로 공개 호스트 서비스와 짝을 이뤄 사용된다.


컨텍스트 맵(Context Map)

컨텍스트 맵은 하나의 큰 도메인을 여러 개의 바운디드 컨텍스트로 식별하고 이들 간의 관계를 표현한 그림이다.

 

아래 그림은 간단한 컨텍스트 맵의 예시이다.

전략적 설계로 도출된 컨텍스트를 기반으로 컨텍스트 매핑 관계를 표현한 개념적인 컨텍스트 맵은 아직 매핑을 위한 구체적인 기술 등이 정의되지 않은 상태이다.

위 그림은 핵심 서브도메인이 동작하기 위해 지원 서브도메인과 일반 서브도메인의 정보를 활용하고, 지원 서비스 도메인 역시 동작을 위해 일반 서브도메인을 활용한다는 것을 보여준다.

 

일반 서브도메인이 핵심 서브도메인, 지원 서브도메인과 공급자/소비자 관계를 맺고 있으며, 일반 서브도메인이 공개 호스트 서비스로 일반 서브도메인을 사용할 수 있도록 프로토콜/인터페이스를 제공하면서 발행된 언어를 다른 컨텍스트에 제공한다.

 

또한 하류의 두 컨텍스트는 충돌 방지 계층을 통해 상류 모델을 번역해서 하류에서 사용할 수 있다. 즉, 핵심 서브도메인에 포함되는 컨텍스트는 일반 서브도메인과 지원 서브도메인에 속하는 컨텍스트를 사용하고, 지원 서브도메인에 포함된 컨텍스트는 일반 서브도메인의 컨텍스트들을 사용한다.


이벤트 스토밍을 통한 마이크로서비스 도출

이벤트 스토밍(Event Storming)은 이벤트 중심으로 이해관계자들이 모여 브레인 스토밍하는 워크샵을 의미한다.

 

이벤트 스토밍은 모든 이해관계자가 모여 서로가 가지고 있는 각 관점을 논의하며, 그 차이점을 이해하고 공유할 수 있다는 점에서, 기존 방법론에서 장기간 단절하며 수행했던 요구사항, 프로세스 모델링, 설계를 진행하는 과정을 뛰어넘는 민첩성과 효율성을 보여준다.

 

또한 쉽고 간편한 도구를 사용해 빠른 시간 내에 지식 공유를 통한 협업을 가속화하고 시각화함으로써 서로간의 학습 및 탐색을 촉진한다.

위 그림처럼 시스템의 액터(사용자 또는 역할자)는 원하는 바를 얻기 위해 시스템을 조작(커맨드)하고, 이 조작은 시스템을 동작(도메인 이벤트)하게 한다. 이 동작을 통해 사용자의 요청에 해당하는 데이터(읽기 모델)를 만들고, 이 정보는 간단한 스케치 형태의 UI(사용자 인터페이스) 형태로 액터에서 제공된다. 액터는 이 정보를 바탕으로 다시 시스템을 조작하고, 또 시스템이 동작하는 절차를 반복하게 된다.


이벤트 스토밍 워크샵 진행

이벤트 스토밍 워크샵의 진행 시간은 최대 3시간을 넘지 않도록 하고, 만약 시간 내에 완료되지 않는 경우 하루나 이틀 후 추가 워크샵을 진행하는 것이 좋다.

 

이벤트 스토밍은 다음과 같은 순서로 진행한다.

  1. 도메인 이벤트 찾기
  2. 외부 시스템/외부 프로세스 찾기
  3. 커맨드 찾기
  4. 핫스팟 찾기
  5. 액터(사용자/역할) 찾기
  6. 애그리거트 정의하기
  7. 바운디드 컨텍스트 정의하기
  8. 컨텍스트 매핑하기

1. 도메인 이벤트 찾기

  • 시간의 흐름에 따라 시스템의 동작을 의미하는 도메인 이벤트를 도출한다. 데이터나 데이터의 구조가 아닌 비즈니스 흐름에서 발생한 이벤트에 초점을 두는 것이 중요하다.
  • 이벤트는 왼쪽에서 오른쪽으로 시간 흐름순으로 붙이되 이벤트가 연쇄적으로 발생하는 경우 바로 옆에 붙인다. 또한 같은 시점에 비즈니스 조건에 따라 대체적으로 발생할 수 있는 이벤트는 세로로 아래쪽에 같은 선상에 붙인다.
  • 도메인 이벤트는 비즈니스의 어떤 상태를 생성, 변경, 삭제하는 요소다. 시스템의 화면을 연상하지 말고 비즈니스가 흘러감에 따라 비즈니스를 구성하는 요소들의 상태가 어떻게 변경되는지 생각한다.

 

2. 외부 시스템 도출

  • 이벤트를 도출하면서 레거시 시스템이나 외부 시스템과의 연계를 통해 업무의 흐름이 진행될 때는 이 프로세스의 이름을 스티커에 작성해서 이벤트의 오른쪽 상단에 붙이고 화살표를 그려 이 외부 시스템을 호출한다는 것을 표시한다.
  • 시스템 구현 범위에 있는 기능이 아니더라도 시스템의 기능 구현을 위해 연계가 필요한 시스템들은 모두 도출한다.

 

3. 커맨드 도출

  • 도메인 이벤트를 찾은 후에 이 이벤트를 동작하게 하는 커맨드(Command)를 찾는다. 커맨드명은 도메인 이벤트를 동작하게 하는 것으로 명령형, 즉 동사 형태로 작성한다. 커맨드는 앞에서 식별한 도메인 이벤트를 보면 쉽게 유추할 수 있다.
  • 하나의 커맨드에 의해 여러 개의 이벤트가 동시 또는 연속해서 발생할 수 있으며, 조건에 따라 하나의 커맨드에 여러 개의 다른 이벤트가 발생할 수 있음에 유의한다.

 

4. 핫스팟 도출

  • 핫스팟은 워크샵을 진행하는 과정에서 의문 사항이 생기거나 참여하는 사람들이 결정하기 힘든 사항, 다른 부서나 외부에 문의할 필요가 있는 사항과 같이 워크샵 중에 해결하거나 정의할 수 없는 것들이다.
  • 이러한 내용들은 잊지 않고 기록하기 위해 가정, 경고, 질문, 미결정 사항 등을 작성해서 문제가 되는 위치에 붙인다.

 

5. 액터 도출

  • 액터(Actor)는 사용자 또는 조직, 역할자를 의미한다. 액터는 추상적으로 식별하지 않고, 비즈니스를 수행하는 구체적인 역할을 고려해서 도출한다. 즉, 단순히 모든 업무에서 보편적으로 사용되는 회원이나 관리자로 정의하지 않고, 특정 비즈니스를 실제로 수행하는 판매자, 구매자같은 명확한 역할자를 도출하려고 노력해야 한다.
  • 액터를 도출하면서 이전에 식별하지 못했던 커맨드와 도메인 이벤트가 추가로 도출된다면, 추가로 식별되는 사항들을 모델링 공간에 붙인다.

 

6. 애그리거트 정의

  • 애그리거트는 커맨드와 도메인 이벤트가 영향을 주는 데이터 요소로, 도메인의 실체 개념을 표현하는 객체인 엔티티가 된다.
  • 액터와 마찬가지로 애그리거트도 구체적인 표현으로 도출하는 것이 좋다.

 

7. 바운디드 컨텍스트 그리기

  • 이름이 같거나 유사한 애그리거트를 완전히 다른 애그리거트와 구분해서 경계를 그려야한다. 또한 그동안 도출했던 도메인 이벤트, 커맨드, 액터, 애그리거트를 모두 고려해서 경계를 식별한다. 이 경계를 바운디드 컨텍스트라고 한다.
  • 컨텍스트의 이름은 바운디드 컨텍스트 내의 애그리거트 이름으로 정의하며, 컨택스트 내에 여러 개의 애그리거트 이름이 있는 경우 전체를 아우를 수 있는 대표 이름을 정한다.

 

8. 컨텍스트 매핑

  • 대략의 바운디드 컨텍스트가 식별되고 파악된 각 바운디드 컨텍스트 간의 관계를 바탕으로 별도의 컨텍스트 맵을 표현할 수 있다. 컨텍스트 관계를 작성할 때는 호출 관계의 방향을 고려해야 한다.
  • 또한 호출할 때 호출 방식을 고려해야 하는데, 컨텍스트 간에 항상 일관된 데이터가 필요한 관계는 동기 호출로 표현하고 결과적 일관성으로 충분히 처리 가능한 관계는 비동기 방식의 호출로 표현한다.

마이크로서비스 상세설계

마이크로서비스 도출, 서비스 간 연관관계 식별, 마이크로서비스별 스펙이 완성되면 이제 프론트엔드 서비스, 백엔드 서비스로 나누어 설계를 해야 한다.


프론트엔드 모델링

프론트엔드 영역의 최소한의 설계 영역은 간단히 다음과 같다.

  • 프론트 아키텍처 정의
  • 표준 레이아웃 정의
  • UI 레이아웃 설계
  • UI 디자인 및 UI 레이아웃 반영
  • 이벤트 설계

백엔드 모델링

백엔드 마이크로서비스를 위한 설계는 헥사고날 아키텍처를 적용해 외부 영역과 내부 영역으로 구분되어 진행된다.

 

이벤트 스토밍 결과를 반영해서 백엔드 마이크로서비스의 내부를 헥사고날 구조로 정의하고 매핑할 수 있으며, 이를 기반으로 발전시켜 나갈 수 있다. 즉, 이벤트 스토밍의 '커맨드'는 헥사고날의 인바운드 어댑터의 하나인 REST API가 되고, '애그리거트'는 헥사고날의 내부 영역인 도메인 모델이 되며, '도메인 이벤트'는 헥사고날 외부 영역의 아웃바운드 메시지 처리 어댑터의 처리 대상이 되고, '외부 시스템'은 마찬가지로 아웃바운드 어댑턱 ㅏ호출해야 할 외부 연계 시스템으로 매핑된다.

 

백엔드의 외부 영역 설계는 프론트엔드와 연계되는 'API 설계'로, 내부 영역은 비즈니스 로직을 구현하는 '도메인 모델링', '데이터 모델링'으로 구체화되어 진행된다.


API 설계

마이크로서비스 팀은 서비스가 제공하는 기능에 대한 프론트엔드와 백엔드의 구현을 모두 책임져야 하므로, 프론트엔드 엔지니어와 백엔드 엔지니어가 하나의 팀에서 긴밀하게 협업해야 한다.

이러한 협력을 위해서 프론트엔드와 백엔드의 연계를 위한 계약이 필요한데, 이것이 API 설계다.

API는 백엔드 서비스에 존재하지만 프론트엔드의 요구사항을 충족하도록 정의해야 한다.

 

API 영역은 헥사고날의 외부 영역이며, 인바운드 어댑터로써 어떠한 호출 방식도 허용되는 유연한 공간이다.

 

최근의 추세는 HTTP 프로토콜과 JSON 포맷을 사용하는 REST API가 표준처럼 사용되고 있다. 이는 REST API 방식이 대중적인 HTTP 프로토콜과 취급하기 쉬운 JSON 포맷, 간단한 HTTP 메소드 형식 등의 특징 때문에 쉽고 명확하게 누구나 이해할 수 있기 때문이다.


REST API 의 개념

REST(REpresentational State Transfer) API는 HTTP 프로토콜을 사용하는 네트워크 기반 아키텍처 스타일이다.

 

아키텍처를 표현하는 구성요소로는 자원(Resource)행위(Verb), 표현(Representations)이 있다.

 

예를 들어 "ABC라는 사용자를 생성한다." 라는 API가 필요하다면 자원은 "사용자", 행위는 "생성한다", 표현은 "ABC"이다. 이를 REST API로 표현하면 다음과 같다.

HTTP POST http://example.com/users/
{
    "users": {
        "name": "ABC"
    }
}

즉, 자원은 http://example.com/users/ 라는 URI로 표현되고, 생성을 위한 HTTP 메소드인 POST를 사용하였고, JSON 문서의 형태로 구체적인 사용자로 표시(전달)된 것이다.

 

이처럼 REST API는 직접적인 사용의 주체가 되는 요소를 자원이라 하며, URI 형식으로 표현한다. 자원은 명사를 사용하며, 세부 항목을 표시할 때는 뒤에 ID를 붙여 정의한다. 사용자의 리소스 형태가 /users/이고 ABC의 식별자 ID가 '01'이라면 ABC는 /users/01로 표현된다.

 

다음 예시처럼 이러한 API를 사용하기 위한 입출력 데이터로 JSON 포맷을 활용한다. 행위를 표현하기 위해서는 HTTP 메소드를 사용한다.

  • 메소드: GET, 의미: 전체 리소스 조회
    • 입력 예시(메소드, 자원, 표현 방식)
    HTTP GET http://example.com/users/
    • 출력 예시
    {
        "users":[
        {
          "id": 01,
          "name": "ABC"
        },
        {
          "id": 02,
          "name": "ABD"
        }
      ]
    }
  • 메소드: POST, 의미: 리소스 추가
    • 입력 예시(메소드, 자원, 표현 방식)
    HTTP POST http://example.com/users/
    {
        "users":{
            "name": "QWE"
        }
    }
    • 출력 예시
    {
        "users":[
        {
          "id": 03,
          "name": "QWE"
        }
      ]
    }
  • 메소드: PUT, 의미: 리소스 수정
    • 입력 예시(메소드, 자원, 표현 방식)
    HTTP PUT http://example.com/users/01
    {
        "users":{
            "name": "AAA"
        }
    }
    • 출력 예시
    {
        "users":[
        {
          "id":01,
            "name":"AAA"
        }
    
      ]
    }
  • 메소드: DELETE, 의미: 리소스 삭제
    • 입력 예시(메소드, 자원, 표현 방식)
    HTTP DELETE http://example.com/users/01
    • 출력 예시
    삭제됨

이처럼 REST API의 구성요소만 보고 API의 내용을 직관적으로 이해할 수 있으므로, 최소한의 문서로써 쉽게 사용할 수 있다.


REST API 성숙도

바람직한 REST API 설계를 위해 아래 그림의 리처드슨이 정의한 REST API 성숙도 모델을 참고할 수 있다.

REST API의 사용을 암묵적으로 가능하게 하는 레벨 2 정도를 지향하는 것이 좋다.

  • 레벨 0은 REST API의 메커니즘을 전혀 사용하지 않고 전통적인 원격 프로시저 호출(Remote Procedure Call) 방식으로 HTTP 프로토콜만 사용한 것이다. 개발자가 백엔드 내부에서 비즈니스 로직을 통해 어떠한 결정을 하는지 사용자는 API를 보고는 알 수 없기 때문에 API 사용을 위한 명세가 필요하다.
  • 레벨 1은 URI에 개별적인 자원을 표현하는 것이다. 여러 기능을 사용하기 위해 하나의 URI에 요청하지 않고 요청이 필요한 대상을 특정한다.
  • 레벨 2는 서비스의 기능을 처리하기 위해 약속된 HTTP 메소드들을 사용하는 것이다. 레벨 0, 레벨 1에서 HTTP 메소드를 사용했지만 GET, POST로 모든 것을 처리하는 식으로, 어떤 방식을 사용할지는 개발자에 따라 달랐다. 레벨 2에서는 가능한 한 약속된 HTTP 사용법에 가깝게 사용한다. 조회, 추가, 수정, 삭제 기능을 각각 HTTP 메소드인 GET, POST, PUT, DELETE로 각각 처리한다. API 사용자는 리소스에 어떠한 메소드를 사용했을 때 어떠한 행위가 발생할지 인지할 수 있다.
  • 레벨 3은 특정 요청을 하게 됐을 때, 반환값에 기대했던 결과에 덧붙여 추가로 사용자가 그다음에 무엇을 할 수 있는지와 그것을 하기 위해 다룰 수 있는 URI 값을 보내준다. 즉, 사용자에게 좀 더 리소스를 탐색해서 활용할 수 있는 가능성을 제공한다.

API 설계 문서화

API 설계는 프론트엔드 엔지니어와 백엔드 엔지니어의 협업 차원에서 중요하므로, 설계 요소의 공유를 위해 문서화가 필요하다.

 

협업 시스템이 있다면 간단히 위키(Wiki) 형태의 문서로 작성할 수도 있다. 공식적인 문서 형식이 필요하다면 엑셀 형태로 정리하는 것이 편하다. 어떤 형태든 최소한 다음 항목은 포함하도록 한다.

  • 서비스명, API 명, 리소스(URI)
  • 요청 매개변수, 요청 샘플
  • 응답 매개변수, 응답 샘플

도메인 모델링

도메인 모델링은 본래 백엔드 모델링의 한 부분이다.

마이크로서비스의 내부 구조는 폴리글랏(Polyglot)하게 접근할 수 있다. 이는 애플리케이션을 구현하는 언어나 데이터를 저장하는 저장소를 서비스마다 다양하게 활용할 수 있다는 의미인 동시에 내부 아키텍처 구조를 서비스 특성에 맞게 다양하게 수립할 수 있다는 의미이다.

따라서 서비스의 내부 영역의 구조를 도메인 모델 중심으로 만들 수도 있고, 트랜잭션 스크립트 형태로 만들 수도 있다.

 

아래 그림은도메인 모델 중심의 구조다. 이 경우 도메인 모델을 중심으로 모델링을 수행해야 하며, 서비스가 모든 로직을 처리하지 않고 비즈니스 로직이 도메인 모델로 위임되어 적절히 분산될 것이다.

아래 그림은 도메인 모델이 없는 트랜잭션 스크립트의 구조이다. DTO는 데이터 묶음으로써의 역할만 수행할 뿐 서비스가 많은 로직을 보유하게 됨으로써 시스템이 복잡해질수록 비대해질 수 있다.

단순한 로직인 경우에는 트랜잭션 스크립트 구조로 만들어도 무방하지만, 비즈니스가 복잡해질수록 비즈니스 개념들을 잘 구조화할 수 있는 도메인 모델 구조가 효과적이다. 도메인 모델 구조는 복잡함을 다루어 쉽게 표현할 수 있는 구조를 제공하기 때문이다.


DDD의 전술적 설계(도메인 모델링 구성요소)

기존 객체 모델링 방식은 자유도가 높아 문제 영역을 파고들수록 여러 층의 복잡한 계층 구조를 만들게 될 가능성이 높다. 이를 정리하기 위해 객체들의 역할에 따른 유형을 저의하고, 이러한 규칙에 따라 모델링하면 단순하고 이해하기가 수월해지는데, 이러한 설계 기법을 DDD의 전술적 설계에서 제공한다.


엔티티(Entity)

엔티티는 다른 엔티티와 구별할 수 있는 식별자를 가진 도메인의 실체 개념을 표현하는 객체다.

 

식별자는 고유하되 엔티티의 속성 및 상태는 계속 변할 수 있다. 도메인에서 개별성(Individuality)이 있는 개념을 엔티티로 식별하며, 고유 식별자와 변화 가능성(Mutability)이 엔티티와 값 객체(VO; Value Object)를 구분하는 차이점이다.


값 객체(VO; Value Object)

값 객체(Value Object)는 각 속성이 개별적으로 변화하지 않는 개념적 완전성을 모델링한다. 따라서 값 객체는 속성과 속성의 합에 의해 전체 개념이 부여되며, 개별 속성이 별개로 수정되지 않고 전체 객체가 한 번에 생성되거나 삭제되는 객체다.

개념적 완전성은 값 객체를 구성하는 하나 이상의 특성들이 서로 연관되어 전체 의미를 이루는 것을 말한다.

 

엔티티와 같이 식별자의 차이에 따라 구별되지 않고 속성과 속성으로 이뤄진 값의 비교에 의해 동일함이 결정된다.

반 버논(Vaughn Vernon)은 값 객체의 특성을 다음과 같이 정의한다.

  • 도메인 내의 어떤 대상을 측정하고, 수량화하고, 설명한다.
  • 관련 특징을 모은 필수 단위로 갠며적 전체를 모델링한다.
  • 측정이나 설명이 변경될 댄 완벽히 대체 가능하다.
  • 다른 값과 등가성을 사용해 비교할 수 있다.
  • 값 객체는 일단 생성되면 변경할 수 없다.

표준 타입

표준 타입은 대상의 타입을 나타내는 서술적 객체다.

 

엔티티나 값 객체의 속성을 구분하는 용도로 사용된다.


애그리거트

엔티티와 값 객체로 모델링하게 되면 자연스럽게 객체 간의 계층구조가 만들어지는데, 이때 연관된 엔티티와 값 객체들의 묶음이 애그리거트이다.

 

애그리거트는 1~2개의 엔티티, 값 객체, 표준 타입 등으로 구성되는데, 이들 간에는 비즈니스 의존관계를 맺고 있으며, 비즈니스 정합성을 맞출 필요가 있다. 따라서 이 애그리거트 단위가 트랜잭션의 기본 단위가 된다.

 

애그리거트 내에 있는 엔티티 중 가장 상위의 엔티티를 애그리거트 루트로 정하고, 이 애그리거트 루트를 통해서만 애그리거트 내의 엔티티나 값 객체를 변경할 수 있다.

 

보통 하나의 컨텍스트에 하나의 애그리거트가 식별되나 하나의 컨텍스트 안에 여러 개의 애그리거트가 존재할 수 있다. 이 경우 다른 애그리거트를 참조해야 할 필요가 있다면 직접 참조하지 않고 아래 그림처럼 참조할 애그리거트 루트의 식별자를 통해 참조하게 한다. 직접 참조하는 경우 애그리거트 단위의 트랜잭션 처리도 힘들고 의존관계가 점점 복잡해질 것이다.

각 애그리거트는 각각의 단일 트랜잭션으로 일관성을 유지하지만, 다른 애그리거트 사이의 일관성이 필요하다면, 다음 그림과 같이 도메인 이벤트를 통한 결과적 일관성을 사용하여 다른 애그리거트를 갱신해서 일관성을 유지한다.


도메인 서비스

도메인 서비스는 도메인의 비즈니스 로직 처리가 특정 엔티티나 값 객체에 속하지 않을 때 단독 객체를 만들어서 처리하게하는 서비스이다.

 

도메인 서비스에서는 상태를 관리하지 않고 행위만 존재한다. 따라서 도메인 로직을 처리할 때 엔티티나 값 객체와 함께 특정 작업을 처리하고 상태를 본인이 가지고 있지 않고 엔티티나 값 객체에 전달한다.


도메인 이벤트

도메인 이벤트는 DDD 및 이벤트 스토밍에서 말하는 도메인 이벤트의 구현 객체다.

 

서비스 간 정합성을 일치시키기 위해 단위 애그리거트의 주요 상태 값을 담아 전달되도록 모델링한다.