본문 바로가기
Architecture

[DDD] 2019 NHN FORWARD: DDD Lite @Spring

by 2D3 2022. 11. 15.
728x90

 

강연 출처: [2019] NHN FORWARD: DDD Lite@Spring

 

Contents

     

    복잡성과 위기

     

    유지보수 / 복잡성이 왜 자꾸 늘어나는 걸까?

    빠르고 간단하게 구축 -> 새로운 요구사항 -> 복잡성 증가 -> 가파른 비용 증가 / 급격한 생산성 감소 -> 위기 -> 차세대 개편 + 레거시 의존 ->빠르고 간단하게 구축

     

    DDD로 위기 극복

    DDD 전략적 패턴: 해결 공간을 뽑아내는 패턴

    • 전략: 큰 그림
    • 전술: 작은 그림 (DDD-Lite)

    일반: 일반 서브 도메인을 구현한 Bounded Context. 대부분에 도메인에서 필요한 일반적인 도메인(계정, 메일 등)

    지원: 핵심 도메인을 지원하는 서브 도메인(제품 조회, 배송 서비스 등)

    핵심: 해결해야 되는 도메인의 영역 중 가장 핵심이 되고 복잡한 도메인(결제 등)

     

    DDD의 전술적 패턴

    -> 핵심 도메인에 적용: Bounded Context 내에서 Model-Driven하게 개발 / 모델링 하는 방법

     

    • Bounded Context: 모델은 특정한 컨텍스트(문맥)에서 완전한 의미를 갖는데, 이렇게 구분되는 경계를 갖는 컨텍스트
    • 유비쿼터스 언어: 보편 언어. 전문적인 용어가 아닌 일반적으로 쓰는 언어
    • Model: 데이터와 비즈니스 로직을 관리. 앱이 포함해야할 데이터가 무엇인지를 정의
    • Model-Driven: 모델을 작성하면 템플릿 수준의 미완성 자바 소스 코드가 표준화된 양식에 맞게 자동으로 생성

     

    • 패턴 언어: 패턴들의 모음 / 컬렉션 (에릭 에반스 - DDD)
    • DDD-Lite: DDD 전술적 패턴의 일부를 적용한 것

     

    지식 탐구: 위키

     

    • Dooray: NHN의 협업툴
    • 테넌트(tenant): 클라우드 서비스 이용자가 가지게 되는 자신만의 환경
    • DDD-Entity: Id가 있어서 식별할 수 있음. 생명주기가 있음. 행위가 우선
    • 응집력이 중요한 패턴: 행위와 연관되는 속성이 존재
    • Data-Driven: 속성에 집중

     

    DDD에서 전술적 패턴으로 모델링 할 때는 행위가 중요.

    속성은 행위에서 필요로 할 때 생성.

    객체간의 연관관계도 행위에서 특정한 관계가 필요하면 그 때 관계를 맺어줌.

    최소한의 꼭 필요한 관계만 단방향으로 순환참조가 되지 않게 도메인 모델을 기반으로 관계를 연결.

     


     

    명세를 만족시키기 위해 개념 모델을 변경.

    create 메서드를 생성하고, 거기에 필요한 속성들을 나중에 추가한다.

    -> 속성보다는 행위가 먼저!

    • create(): static 메서드

     

    요구사항에서 버전 이력관리(연관 엔티티)가 추가 됐을 때, 명세에 추가된 것을 기반으로 새로운 연관관계 설정

     


     

    값 객체: 식별성은 존재하지 않지만, 도메인의 특정 영역을 서술적으로 추상화 시키는 객체(연관된 속성들, contentType, contentBody)

    • 서술적: 과정을 차례대로 기술하는 것
    • 추상화: 어떤 환경에서든지 실행될 수 있는 것

     

    contentType, contentBody를 하나의 개념으로 표현할 수 있기 때문에 모델링을 고도화(값 객체를 통해서 단순화 함)

    • 일반적으로 값 객체는 불변으로 구현해서 부여가 없음
    • 값 객체 자체에 행위를 추가할 수 있음 -> 도메인 언어가 더 풍부하게 됨 => 개념을 캡슐화, 추상화 시킬 수 있음
    • 최소한의 단위를 모델링 할 때 도움이 되는 패턴
    • 예시: 수량, 금액, 주소 등

     

    값 객체로 묶을 수 있는 것: 접두어가 같음

     


     

    멘션 기능을 추가하면서 Entity 연관관계 추가하면서 영역이 새로 추가 됨

    애그리거트(집합체): 엔티티를 그룹화 시키는 특정 불변식의 개념.

    도메인 모델의 일관성을 유지하는 불변식. 데이터 변경의 단위. 트랜잭션 단위. 분산 환경에서는 Lock의 범위가 되기도 함

     

    엔티티 예시

    Page-Aggregate

    Page는 PageUser랑 PageFile이랑 함께 있기 때문에 그룹화 시킴

    • 분산환경: 다른 기종 컴퓨터 간에 애플리케이션을 분산 처리하기 위한 환경

     

    추후에는 PageFile을 다른 애그리거트가 됨. PageUser는 Page와 경계가 같음.

    하지만, PageFile만 따로 업로드 하거나, 다운로는 하는 UseCase가 추가되면서, PageFile도 애그리거트로 분리.

    • UseCase: 사용자(User)가 해당 서비스를 통해 하고자 하는 것(검색, 댓글 등)

     

    Aggregate 규칙

    1. AggregateRoot: 애그리거트를 통과하는 통로

        PageHistory가 PageUser에 다이렉트로 접근할 수 없음. Page 애그리거트 루트를 통해서만 접근 가능
    2. 애그리거트 루트 하나에 리포지터리 하나

     

     

    • version: 낙관적 오프라인 락 패턴을 구현하기 위한 것 (마틴 파울러)
    • Cascade: 영속성 전이를 위해 사용.
    • PageUser에 대해서는 따로 save 할 필요 없이 여기서만 받아져 있으면, 애그리거트 루트 안에 있는 Page Entity안에 있는 PageUsers 속성에 값이 변하면 영속성 전이가 일어나서 save나 update가 됨.

     


     

    도메인 모델로 표현하게 되면 set을 호출하는 게 아니라, chageContent(content)처럼 도메인 모델로 표현해야 됨.

     

    페이지가 변경되는 사건이 발생 => 도메인 이벤트 패턴

    도메인 이벤트

    발행-구독 모델을 기반으로 해서 구현을 지원 함.

    이벤트의 후속 구독 작업은 트랜잭션에 포함되지 않음

    이벤트를 발행하는 입장과 이벤트를 구독하는 입장에서 분할 정복

     

    ApplicationService

    트랜잭션과 보안 등을 관리하는 얇은 서비스

    파사드로 구현

    트랜잭션이나 애플리케이션 관심사는 처리하되, 도메인 로직은 절대 존재하면 안 됨

    도메인 모델을 로딩에서 도메인 로직에 위임

     

    Application Servce의 진입점

    changeContent(): UseCase의 표현. UseCase 하나에 Application Method 하나

    • 파사드 패턴(Facade): 커다란 코드 부분에 대하여 간략화된 인터페이스를 제공해주는 디자인 패턴

     

    내부에서 도메인 로직을 처리한 다음에, 도메인 이벤트를 등록 함. 도메인 이벤트를 생성해서 어딘가로 날려줌(이벤트를 발행하고 구독하는 프레임워크나 애플리케이션 관심사)

    EventProcessor가 받아서 구독자에게 각각 발행하면서 트랜잭션 종료.

     

     

    구현: Model-Driven vs Data-Driven

     

     


     

     


     

    Application Service라서 Domain 로직이 있으면 안 됨.

    Domain Service에 위임

    • Domain Service: 무상태의 도메인을 처리하는 서비스로서 엔티티와 애그리거트와 같은 속성과 행위를 다 가지고 있는 것이 아니라, 행위만 가지고 있는

     


     

    자바독(주석): 발행-구독 모델의 단점은 추적이 안 됨. 누가 발행했는지, 구독했는 지 알 수 없음.

    그래서 Domain Event 클래스에 추적할 수 있게 발행자가 누구인지, 구독자가 누구인지 주석으로 표시

     

    개념 모델

    occurredOn

    Event는 실행 순서를 보장하지 않음.

    발생 일시를 넣어줘야 멱등성 있게 이벤트를 처리할 수 있음

    • 멱등성: 동일한 요청을 한 번 보내는 것과 여러 번 연속으로 보내는 것이 같은 효과를 지니고, 서버의 상태도 동일하게 남을 때

     


     

    스프링의 @Repository는 DDD를 지원하기 위해 만들어진 애너테이션.

    JpaRepository를 상속해서 구현하는 것이 아닌, POJO로 설계한 다음에 JpaRepository와 모양이 같으면 트레이드 오프를 해도 되지만,

    findAll, delete, save 3개 정도만 사용한다면, 스프링이라는 기술에 의존하는 것이 아닌가 고민이 필요.

     


     

    아키텍처와 모듈

     

     

    ​ Adapter: 일종의 바인딩 개념. 어떤 추상적인 기술 구현이 필요할 때, 이 어댑터 를 쓰면 이 기술로 되고 저 어댑터를 쓰면 저 기술로 되는 개념

    ​ => 기술에 대한 Binding Port & Adapter Pattern

    • Primary Adapter: 인입되는 지점. Rest Api, WebPage, CLI 배치 등이 포함
    • Secondary Adapter: 기술적으로 구현을 바인딩 하는 곳. 영속화를 담당하는 DB, 레디스와 같은 캐싱과 같이 인프라 또는 다른 서비스를 호출하는 것까지 다 포함
    • Domain Model: 도메인의 핵심을 간략하게 단순화해서 표현할 수 있는 모든 것
    • Applicaion: Application Service 등이 있음

    Dooray wiki의 본문을 수정하는 컴포넌트를 나열할 것.

    헥사고날 아키텍처의 핵심: 모든 화살표(의존성의 화살표, 데이터 흐름X)가 바깥에서 안으로 모여든다.

    계층형 아키텍쳐의 단점: 도메인이 인프라에 의존하게 됨. 도메인적 관심사와 기술적 관심

     


     

    Notificaion Service를 구현하는 Dooray Adapter가 DIP 이걸 통해서 의존성 방향을 역전시킴.

    도메인 모델의 순수성을 지킬 수 있음.

     

    Rabbitmq: 나가는 통로와 들어오는 통로 둘 다 됨 (Primary adapter와 Secondary adapter 역할 둘 다 함)

     

     

    728x90

    'Architecture' 카테고리의 다른 글

    [디자인 패턴] MVC 패턴  (0) 2023.02.21

    댓글