도메인 주도 개발 시작하기를 읽고..

‘도메인 주도 개발 시작하기를 읽고…’

그림1

사내 스터디를 통해서 “도메인 주도 개발 시작하기” 라는 교재를 읽게 되었습니다.

사실 책을 읽은지는 조금 되었는데, 정리하면서 내용을 전체적으로 다시 한번 훑어보는 시간을 갖게되었습니다.

목차별 내용 요약

1장 도메인 모델 시작하기

주로 도메인이란 무엇인가? 도메인 모델은 어떻게 점직적으로 만들어지는가? 엔티티와 벨류는 무엇인가? 에 대해서 설명합니다.

도메인이란 소프트웨어로 해결하고 싶은 영역을 의미합니다. 개발자가 올바르게 코딩하기 위해서는 도메인에 대한 높은 이해가 필요하며, 이를 얻기 위해서 도메인 전문가와 직접 소통하는것이 좋습니다.

도메인은 소프트웨어 모델로 구현할때, 엔티티 또는 벨류로 만들어집니다. 엔티티와 벨류의 가장 큰 차이점은 식별자가 있냐 없냐 입니다.

엔티티는 식별자로 동등성을 비교하게됩니다.

도메인 모델이 캡슐화가 잘 이뤄질 수 있도록 get, set 메소드의 사용은 지양하는게 좋습니다.

2장 아키텍처 개요

클린 아키텍처 구조와 도메인 영역의 구성요소에 대해서 설명합니다.

전형적인 계층구조의 아키텍처에서는 도메인 모델이 인프라 영역에 의존해야합니다. 이렇게 구현하면 인프라 코드의 변경이 발생했을때 도메인 코드도 전부 수정해야합니다.

이를 해결하기 위해서 DIP 패턴을 사용하게 되는데요.

도메인과 같은 고수준 모듈에는 인터페이스를 선언하고 인프라와 같은 저수준 모듈에서 인터페이스에 대한 구현체를 생성함으로써 의존성이 역전되는 패턴을 DIP라고 부릅니다.

흔히 하는 실수로, 저수준 모듈에 인터페이스와 구현체를 같이 위치시키는점을 주의해야합니다.

도메인 영역은 엔티티, 벨류, 에그리거트, 리포지터리, 도메인 서비스로 이루어집니다.

도메인(엔티티, 벨류)들이 지속적으로 늘어나면 전체 구조를 파악하기 어렵기 때문에, 애그리거트라는 상위 개념으로 한번 더 묶어주게됩니다.

레포지토리는 도메인 모델 관점에서 영속화하는데 필요한 기능을 추상화한 고수준 모듈(도메인 모듈)의 구성요소입니다.

3장 애그리거트

DDD에서 다루는 애그리거트의 특징은 무엇인가에 대해서 설명합니다.

객체 단위로 시스템을 바라보면 이해하기가 어렵기 때문에, 애그리거트라는 개념이 탄생합니다.

또한 애그리거트는 모델을 이해하는데 도움을 줄 뿐아니라, 데이터 일관성을 관리하는 기준이 되기도 합니다.

애그리거트는 각자 루트 엔티티가 존재합니다. 애그리거트안의 데이터 변경은 모두 루트 애그리거트를 통해서 이뤄져야합니다.

이렇게 규칙을 정함으로써 루트 애그리거트만 도메인 규칙을 올바르게 작성하면 애그리거트의 데이터 일관성을 지킬 수 있게됩나다.

하나의 트랜잭션에서 여러 애그리거트의 데이터 변경이 이뤄져야한다면 응용계층에서 이를 컨트롤하며, 도메인 이벤트를 사용해도 좋다.

에그리거트 단위로 레포지터리를 만듭니다. 즉, 하나의 애그리거트안에서 루트 애그리거트에 대한 레포지터리만 만듭니다.

나머지 엔티티는 전부 jpa의 cascade persist all 옵션을 활용하여 루트 애그리거트를 통해서 저장합니다.

애그리거트간에는 객체 참조를 금지하며, id기반으로 조회하도록 합니다. 이유는 아래와 같습니다.

  1. 객체참조를 열어주면 다른 애그리거트의 데이터를 수정하는것도 가능해지기 때문에 애그리거트간의 의존성이 생깁니다.
  2. 애그리거트간의 로딩 시점에 대한 전략이 필요해집니다.
  3. 트래픽이 늘어났을때 도메인별로 시스템을 분리하기 어렵습니다.

4장 리포지터리와 모델 구현

SpringDataJPA가 제공해주는 여러 기능들에 대해서 설명해줍니다. JPA를 활용해서 엔티티와 벨류를 구성하는 방법을 가이드해줍니다.

JPA가 제공해주는 @embedded와 @embeddable을 통해서 벨류를 매핑할 수 있습니다.

또한 AttributeConverter를 이용해서 두개의 프로퍼티를 하나의 디비 칼럼에 매핑하는것도 가능합니다.

별도 테이블에 저장한다고해서 엔티티로 꼭 만들어야하는것은 아닙니다. 애플리케이션으로 들고올 때 JPA를 통해서 키값이 없는 벨류로 매핑하면 됩니다.

만약에 엔티티로써 불러와야한다면 다른 애그리거트를 같은 애그리거트로 묶어놓은 실수를 범하지않았는지 확인해야합니다.

애그리거트 루트가 아닌데 엔티티로 들고오게되면, 애그리거트 루트와 다르게 라이프사이클을 가질 수 있기 때문에 주의해야합니다.

루트 애그리거트가 로딩될때 반드시 모든 애그리거트가 같이 로딩되어야하는것은 아닙니다. 적절하게 지연로딩과 즉시로딩 전략을 설정해야합니다.

5장 스프링 데이터 JPA를 통한 조회 기능

SpringDataJpa가 제공해주는 조회관련 여러 기능들에 대해서 설명해줍니다.

Specification을 통해서 복잡한 쿼리를 쉽게 만들수 있습니다. 또한 SpringDataJpa가 지원해주는 페이징 기능을 통해서 페이징 처리를 쉽게 구현 가능합니다.

프로잭션을 통해서 데이터를 로딩할때 엔티티가 아닌 DTO로 매핑할 수 있으며, 이는 의도치않은 데이터 변경을 방지할 수 있습니다.

6장 응용 서비스와 표현 영역

응용 서비스와 표현 영역의 책임에 대해서 설명합니다.

표현영역에서는 사용자의 요청을 응용 서비스에 맞게 컨버팅하는정도의 코드만 들어갑니다. 비지니스 로직이 들어가면 안됩니다.

응용 서비스는 디자인패턴의 퍼사드 패턴과 같습니다. 중복을 줄이고 응집성을 높이기위해서 비지니스 로직은 도메인 모델에 담는것이 좋습니다.

권한감사같은 기능은 AOP를 활용하면 좋다.

7장 도메인 서비스

여러 애그리거트가 하나의 비지니스에서 같이 동작해야하는경우에 사용되는, 도메인 서비스라는 개념을 설명합니다.

여러 애그리거트의 비지니스가 함께 동작해야하는 경우 도메인 서비스를 활용하면 좋습니다.

여기에서 도메인 서비스와 응용 영역의 서비스간의 차이점이 조금 모호할수도 있는데요.

도메인 서비스는 여러 도메인들이 함께 비지니스 로직을 수행하는 경우 사용하게되며, 응용 서비스는 여러 도메인들의 비지니스 로직 흐름을 제어할때 사용하게 됩니다.

이 때, 클래스명에 명확하게 비지니스용도를 담아주는게 좋습니다. 또한 도메인 서비스 객체를 애그리거트에 주입하지않도록 주의해야합니다.

8장 애그리거트 트랜잭션 관리

비관적락과 낙관적락에 대해서 설명합니다.

비관적락은 데이터베이스 레벨에서 락을 잡고 데이터를 변경하는것입니다. 이 때, 교착상태에 대해서 잘 컨트롤 해야합니다.

또한 비관적락은 조회이후에 트랜잭션을 잡고 데이터를 수정하는 상황에서 그 사이 다른 스레드가 데이터를 변경하면 데이터 일관성이 지켜지지않습니다.

이러한 문제는 낙관적락을 통해서 해결해야합니다. 낙관적락은 update 할때 조회 시점의 데이터와 현재 디비상의 데이터가 같은지 비교합니다.

9장 도메인 모델과 바운디드 컨택스트

DDD의 바운디드 컨택스트라는 개념에 대해서 설명합니다.

도메인 모델을 만들 때, 도메인을 완벽하게 표현하는 단일 모델을 구성하려고 하는 실수를 범하면 안됩니다.

배송 도메인의 상품과 주문 도메인의 상품은 다를 수 있습니다.

따라 같은 도메인이어도, 바운디드 컨택스트에 따라 별도 모델로 분리해야할 수 있음을 명시해야합니다.

마이크로서비스에서는 바운디드 컨택스트가 시스템의 단위가 됩니다.

10장 이벤트

이벤트의 장점에 대해서 설명하며 다양한 케이스에서 어떻게 이용될 수 있는지 설명합니다.

하나의 프로세스에서 이벤트를 사용할때는 스프링이 제공하는 ApplicationEventPublisher를 사용하는것도 좋은 방법입니다.

이를 통해서 트랜잭션을 하나로 묶을 수도 있고, 비동기로 분리할 수도 있습니다.

이벤트 로직은 멱등성을 지켜주는것이 중요한데요, 멱등성이란 연산을 여러번 적용해도 결과가 달라지지않는 성질을 의미합니다.

이벤트를 중복처리해도 결과값이 달라지지않도록 설계하는것이 중요합니다.

또한 이벤트 방식에서는 예외처리에 대한 어려움이 있을 수 있는데, @TransactionalEventListener 를 통해서 트랜잭션의 상태에 따라만 이벤트를 컨숨하도록 만들 수 있습니다.

11장 CQRS

CQRS에 대해서 간략하게 설명합니다.

CQRS 패턴은 도메인이 복잡한경우에 사용하면 적절합니다.

도메인이 복잡할수록 명령 기능과 조회 기능이 다루는 데이터 범위에서 차이가 발생합니다.

CQRS 패턴의 레벨에 따라 시스템과 데이터소스를 물리적으로 분리하냐, 논리적으로 분리하냐를 결정할 수 있습니다.

데이터소스를 분리했을때는 이벤트 소싱을 통해서 데이터 싱크를 맞춰줄 수 있습니다.

CQRS를 적용하면 명령 모델을 구현할 때, 도메인 자체에 집중함으로써 조회 성능을 위한 코드를 명령 모델에서 분리할 수 있습니다.

또한 조회 모델의 성능 튜닝이 가능해집니다.

반면에 시스템 복잡도를 높일 수 있으므로, 도메인 로직이 복잡한 경우에 사용하는것이 좋습니다.

읽고 나서..

먼저 저는 DDD라는 키워드만 몇번 들어본 상태에서 해당 교재를 읽게 되었는데요.

애그리거트, 바운디드 컨택스트, CQRS 등등 자세한 내용을 안다고는 할 수 없지만 어떠한 개념을 말하는거구나 정도는 알 수 있게되었습니다.

어떤 내용을 의미하는지 알게되었다는것은, 앞으로 점점 발전시킬수 있는 여지를 마련했다는 의미로 중요합니다.

또한 개인적으로 해당 교재의 내용을 의식적으로 실무 프로젝트에서 반영하기 위해서 노력했는데요.

물론 “애그리거트당 엔티티는 하나만 존재하며 나머지는 전부 벨류 오브젝트로 만든다.” 와 같이 실무에 적용하기 어려운 개념도 존재했습니다.

그렇지만 팀 내에서 애그리거트라는 개념을 공유함으로써 과도한 객체참조를 끊어낼 수 있었고, 애그리거트 루트를 통한 데이터 변경으로 데이터 일관성을 지켜줄 수 있도록 의식하면서 코드를 개발할 수 있었습니다.

또한 신규 프로젝트에서 시스템을 분리하는데 적절한 기준을 찾을 수 있게되었고,https://github.com/madvirus/ddd-start2 에 예시 코드가 만들어져있는데 기본적인 셋팅들을 실무 프로젝트에 많이 접목시킬 수 있었습니다.

이후로 최범균님의 유투브 영상도 자주 보게되었는데, 개념을 처음 듣는 사람도 이해할 수 있게 너무 설명을 잘해주셔서 감사한 마음을 갖게 되었습니다.


© 2021. All rights reserved.

Powered by Hydejack v9.2.1