IDDD 4장. 아키텍처
실제 요구(도메인)가 아키텍처 스타일과 패턴의 사용을 유도해야 한다.
- 도메인이 기술 보다 먼저다.
성공한 CIO와의 인터뷰
- Server-Client Architecture
- 계층형 아키텍처
- DIP, QP, PSA, DDD-Lite
- 헥사고날 아키텍처
- To Mobile, Cloud
- CQRS
- Materialized Views
- Event-Driven Architecture
- Pipeline + Filter
- Saga 패턴
- Long-lived transaction as a sequence of subtransactions.
- In a distributed system
- 이벤트 소싱
- 모든 변경을 추적 (ex: 보안 감사 등)
이러한 아키텍처 덕분에 결국 회사에 돈이 된다
계층
그림 4.1
DDD가 적용된 전통적인 계층 아키텍처
[출처] https://ajlopez.wordpress.com/2008/09/12/layered-architecture-in-domain-driven-design/
- 도메인 모델과 비즈니스 로직을 도메인 계층에 격리
- 하위 계층에만 의존 - 아래에서 위쪽으로 직접 참조 불가 (하지만 Observer 사용 가능)
- 느슨한 연결 : UI가 애플리케이션(바로 하위) 뿐만 아니라 도메인이나 인프라에도 의존 가능
UI 계층
- View나 API 제공
- 도메인 모델이 아닌 표현 모델(DTO)를 사용 추천
애플리케이션 계층
UI로 부터 매개변수를 받아 리파지토리를 사용해 애그리게잇을 획득하고, 커맨드를 위임
- 보안과 트랜젝션 담당 (일반적으로 보안을 UI로 올리는 것 같음)
- 도메인 로직 없으며 도메인 모델(애그리게잇, 도메인 서비스 등)에 위임
- 도메인 모델에서 발행한 도메인 이벤트를 구독(subscribe)
@Transactionl
void commitBackLogItemToSpring(
String aTanantId, String aBackLogItemId, String aSprintId) {
// 재료(Aggregate) 준비
BacklogItem backlogItem =
backlogItemRepository.backlogItemOfId(aTanantId, aBacklogItemId);
Sprint sprint sprintRepository.sprintOfId(aSprintId);
// 커맨드 위임
backlogItem.commitTo(sprint);
}
DIP
- 상위 수준의 모듈은 하위 수준 모듈에 의존해선 안된다. 둘 모두는 반드시 추상화에 의존해야 한다.
- 추상화는 세부사항에 의존해선 안 된다. 세부사항은 추상화에 의존해야 한다.
하위 구현 컴포넌트가 상위 콤포넌트가 정의한 인터페이스에 의존해야한다.
어라 그러다 보니 계층이 없어진다. 여기에 대칭성을 더하면 어떻게 될까?
헥사고날 또는 포트와 어댑터
헥사고날 아키텍처
[출처] http://alistair.cockburn.us/Hexagonal+architecture
Texi handling Hexagonal Architecture
[출처] https://github.com/seongminwoo/study/blob/master/7-part_series_about_microservices.md
헥사고날의 장점
- 기능적 요구사항에 따라 애플리케이션 내부를 설계
- UI, Infra는 그 다음이다.
- 애플리케이션 내부가 캡슐화 된다.
- Adapter를 통해 기술과 분리
서비스 지향(SOA)
중요한 것은 기술 보다 비즈니스(도메인) 가치가 우선해야 한다
REST: 표현 상태 전송
[출처] https://martinfowler.com/articles/richardsonMaturityModel.html
RESTful HTTP 서버의 주요 특징
- Resource : URI
- Verbs : GET, POST, PUT, DELETE, …
- Hypermedia : 연결된 리소스를 제공. 상호작용, 클라이언트 무상태
RESTful HTTP 클라이언트의 주요 특징
Hypermedia를 바탕으로 상호 작용한다.
REST와 DDD
도메인 모델을 RESTful로 바로 노출하는 것은 좋지 않다.
- 도메인 모델의 변경이 API와 연결되기 때문에 변경에 취약해지며, 클라이언트와 호환성이 깨진다.
해결책
- 도메인 모델 -> 표현 모델을 조립 (추천)
- Assembler + DTO (PoEAA)
- 각 상황 별 공유 도메인 모델 사용
- 공유커널
왜 REST인가
느슨함
CQRS
사용자가 필요로하는 데이터 뷰를 리파지토리로 쿼리하기란 어려울 수 있다.
CQRS = 객체 설계 원칙 + CQS
- Command : 객체의 상태를 수정, Void 형
- Query : 값을 반환, 수정 X
구현
- 애그리게잇은 오직 커맨드 메소드만 가지고 있음.(커맨드 모델)
- 쿼리를 위한 뷰 전용 모델(쿼리 모델)을 생성
CQRS의 영역 살펴보기
출처 : https://docs.microsoft.com/ko-kr/azure/architecture/guide/architecture-styles/cqrs 참고 : https://docs.microsoft.com/ko-kr/azure/architecture/patterns/cqrs
클라이언트와 쿼리 처리기
쿼리 모델(읽기 모델)
- 필요한 수 만큼 뷰를 지원하기
- 실용적으로 하라
- 데이터베이스 테이블 뷰가 오버헤드의 원인이 되지 않을까?
클라이언트가 커맨트 처리를 주도한다.
UI to Argument
커맨드 처리기
- 카테고리 스타일 : 1 class n methods
- 전용 스타일 : 1 class 1 method
커맨드 모델(쓰기 모델)은 행동을 수행한다.
public void commitTo(Sprint aSprint) {
...
DomainEventPublisher
.instance()
.publish(new BacklogItemCommitted(
this.tenant(),
this.backlogItemId(),
this.sprintId()
));
}
- 어떤 경우든 쿼리 모델을 업데이트시키기 위해선 도메인 이벤트를 게시해야 한다.
이벤트 구독자가 쿼리 모델을 업데이트 한다.
- 동기도 가능하고 비동기도 가능하다.
결국은 일관성이 유지되는 쿼리 모델 다루기
Eventually consistent
지연되는 뷰 데이터 동기화를 해결해야한다.
- 낙관적 업데이트 기법!
- 클라이언트에서 publish/subscribe 기법
결국은 동기화되는 지연시간이 문제
이벤트 주도 아키텍처(EDA)
이벤트의 생산, 감지, 소비와 이벤트에 따른 응답 등을 촉진하는 소프트웨어 아키텍처
From 도메인 이벤트
파이프와 필터
$ cat phone_number.txt | grep 303 | wc -1
그림 4.8
필터를 처리하는 이벤트를 보냄으로써 파이프라인이 만들어진다.
[출처] http://zhangyi.farbox.com/post/coding/understand-scala-stack
이는 가상의 예제이자 개념적 부분을 강조했을 뿐이다. 실제 엔터프라이즈에선 큰 문제를 좀 더 작은 단계로 나누기 위해 이 패턴을 사용하며, 좀 더 쉽게 분산 처리를 이해하고 관리하도록 해준다. 또한 여러 시스템이 오직 자신이 할 일(도메인)만을 걱정하게 되게 해주기도 한다.
장기 실행 프로세스
- 컴포지트
- 애그리게잇 집합
- 이벤트
역시나 중요한 것은 결과적 일관성(Eventual Consistency)
Saga Pattern
Events/Choreography
Rollback
- referecne : https://blog.couchbase.com/saga-pattern-implement-business-transactions-using-microservices-part/
이벤트 소싱
거의 완벽한 수준의 변경 추적
[출처] https://docs.microsoft.com/ko-kr/azure/architecture/patterns/event-sourcing
Replay : 이벤트소싱에서 이벤트를 되감아서 특정 버전 상태로 되돌리는 것
- 하지만 최신 버전 Replay는 병목의 원인
- 이를 해결하기 위해서 최신 버전의 상태를 Snapshot으로 지정해서 최적화
데이터 패브릭과 그리드 기반 분산 컴퓨팅
분산 캐시
ex) hazelcast
데이터 복제
- 캐시 master/slave
- 복제를 통한 장애 극복
- 이벤트 유실 제거 (이벤트 publish/subscribe도 가능)
이벤트 주도 패브릭과 도메인 이벤트
For Domain Event
지속적 쿼리
For CQRS
분산 처리
For Saga 또는 배치 병렬 처리
마무리
- Layerd Architecture > DIP > Hexagonal Architecture
- SOA, REST, 분산 컴퓨팅
- CQRS, Event Sourcing, pipe/filter, Saga