본문 바로가기
Learning

도메인 주도 설계 소프트웨어의 복잡성을 다루는 지혜

by zsgg 2018. 8. 1.

alt book

동작하는 도메인모델 만들기

  • 지도는 모델
  • 도메인 모델은 어떤 특정한 다이어그램이 아니라 다이어그램이 전달하고자 하는 아이디어다.
  • 소프트웨어의 본질은 해당 소프트웨어의 사용자를 위해 도메인의 관련된 문제를 해결하는 능력에 있다.

의사소통과 언어사용

  • 용어의 의미가 바뀌면 팀에서는 클래스 다이어그램을 수정하고 코드상의 클래스와 메서드의 이름을 변경하며, 심지어 동작방식도 바꿀 것이다.
  • 개발자들은 구현이라는 맥락에서 이러한 언어를 사용하고 의미가 부정확 하거나 모순되는 사항을 지적해서 도메인 전문가가 실행 가능한 대안을 생각해 내게끔 만들 것이다.
  • 개발자는 설계를 어렵게 만드는 모호함과 불일치를 찾아내는 데 촉각을 곤두 세워야 한다.
  • 모델을 정제하는 가장 좋은 방법은 가능한 모델 변형을 구성하는 다양한 요소를 큰소리로 말하면서 말하기를 통해 살펴보는 것이다.
  • 시스템에 관해 이야기르 주고받을때 모델을 사용하라. 모델의 요소와 상호작용을 이용하고 모델이 허용하는 범위에 개념을 조합하면서 시나리오를 큰소리로 말해보라. 표현해야 할 것같을 더 쉽게 말하는 방법을 찾아낸 다음 그러한 새로운 아이디어를 다이어그램과 코드에 적용하라.
  • 도메인 전문가와 개발자 사이에 언어적 분열이 일어나서는 안된다.
  • 설계의 생생한 세부사항은 코드에 담긴다. 잘 작성된 구현은 투명해서 설계를 지탱하는 모댈을 드러내야 한다.

모델과 구현의 연계

  • 코드를 작성하는 사람이 모델에 책임을 느끼지 못하거나 애플리케이션을 대상으로 동작하게 만드는 법을 모른다면 그 모델을 소프트웨어와 무관해진다.

모델 주도 설계의 기본요소

  • 훌륭한 도메인 모델을 개발하는 것도 일종의 예술이다

도메인의 격리

  • 우리는 시스템안에서 도메인과 관련이 적은 기능으로부터 도메인 객체를 분리할 필요가 있으며, 그렇게 해서 도메인 개념을 다른 소프트웨어 기술에만 관련된 개념과 혼동하거나 시스템이라는 하나의 큰 덩어리 안에서 도메인을 전혀 바라보지 못하는 문제를 방지할 수 있다.
  • 사용자 인터페이스 : 사용자에게 정보를 보여주고 사용자의 명령을 해석하는일
  • 응용 계층 : 소프트웨어가 수행할 작업을정의하고 표현력 있는 도메인 객체가 문제를 해결하게 한다. 이 계층에서 책임지는 작업은 업무상 종요하거나 다른시스템의 응용 계층과 상호작용하는 데 필요한 것들이다. 여기에는 업무 규칙이나 지식이 포함되지 않으며 오직 작업을 조정하고 아래에 위치한 계층에 포함된 도메인 객체의 협력자에게 작업을 위임한다.
  • 도메인 계층 : 업무 개념과 업무 상황에 관한 정보, 업무 규칙을 표현하는 일을 책임진다. 저장상태 같은 기술적인 세부사항은 인프라스트럭처에 위임한다.
  • 인트라스트럭처 : 상위 계층을 지원하는 일반화된 기술적 기능을제공한다.
  • 결국 도메인을 격리할 때의 가장 좋은점은 부수적인 것을 배제하고 도메인 설계에만 집중할 수 있다는 것이다.

소프트웨어에서 표현이 되는 모델

  • 어떤 객체가 연속성과 식별성을 지닌것을 의미하는가? 아니면 다른 뭔가 상태를 기술하는 속성에 불과한가? 이것은 ENTITY, VALUE OBJECT를 구분하는 가장 기본적인 방법이다.
ENTITY (엔티티, 참조객체라고도 함)
  • 기본적인 책임은 객체의 행위가 명확하고 예측가능해질 수 있게 연속성을 확립하는 것이다.
  • ENTITY의 속성이나 행위에 집중하기 보다는 본질적인 특징(식별, 탐색하며 일치시키는데 널리 사용되는)만으로 정의한다
VALUE OBJECT
  • ENTITY의 식별성을 관리하는일은 매우 중요하지만 그 밖의 객체에 식별성을 추가한다면 시스템의 성능이 저하되고, 분석작업이 별도로 필요하며,
    모든 객체를 동일한 것으로 보이게 해서 모델이 혼란스러워질 수 있다.
  • 우리는 특별히 다뤄야할 부분과 그렇지 않은 부분을 구분해야 한다.
  • 우리는 이러한 설계요소가 어느 것이지에 대해서는 관심이 없고 오직 해당 요소가 무었인지에 대해서만 관심이 있다.
  • VALUE OBJECT가 ENTITY를 참조할 수도 있다.
  • 모델에 포함된 어떤 요소의 속성에만 관심이 있다면 그것을 VALUE OBJECT로 분류
SERVICE
  • 설계가 매우 명확하고 실용적이라도 객체에 속하지 않는 연산이 포함될 때가 있다. 이러한 문제를 억지로 해결하려 하기보다는 문제 자체의 면면에 따라 SERVICE를 모델에 명시적으로 포함할 수 있다.
  • 서비스라는 이름은 다른 객체와의 관계를 강조한다.
  • 서비스의 매개변수와 결과는 도메인 객체여야 한다.
  • 도메인 서비스? 나는 응용 서비스만 있는지 알았는데?!

todo: 도메인 서비스

MODULE(Package)
  • 모듈로 쪼개지는건 코드가 아닌 개념이다.
  • 어떤 사람이 한 번에 생각해낼 수 있는 양에는 한계가 있으며(따라서 결합도는 낮춰야 한다)
  • 일관성 없는 단편적인 생각은 획일적인 생각을 섞어놓은 것처럼 이해하기 어렵다.(따라서 응집도는 높여야 한다)
  • 도메인으 이해하는 바가 바뀔 때마다 이를 모듈에도 반영하면 모듈안의 객체도 더 자유롭게 발전할 수 있다.
  • 어떤 클래스들을 한 모듈안에 둔다면 그것은 바로 여러분 옆에서 설계를 살펴보는 동료 개발자에게 그 클래스들을 하나로 묶어서 생각하자고 말하는 것과 같다.
  • 모델이 어떤 이야기를 들려주는 것이라면 모듈은 이야기의 각 장에 해당한다.
패러다임이 혼재할 때 MODEL-DRIVEN DESIGN 고수하기

객체가 아닌 요소를 객체지향 시스템에 혼합하는 데는 경험상 다음의 4가지 법칙이 있다.

  • 구현 패러다임을 도메인에 억지로 맞추지 않는다
  • 유비쿼터스 언어에 의지한다
  • UML에 심취하지 않는다
  • 회의적이야 한다

여러 페러다임이 혼재하는 데서 오는 부담을 떠안기 전에 지배적인 패러다임 내에서 선택가능한 방안을 샅샅이 살펴봐야 한다.
어떤 도메인 개념이 분명한 객체로서 스스로를 드러내지 않더라도 보통 해당 패러다임 내에서는 모델링될 수 있다.

관계형 페러다임은패러다임의 혼합의 특수한 경우이다.
또한 가장 흔히 접하는 비객체 기술인 관계형 테이터베이스는 다른 구성요소에 비해 객체 모델과 더욱 직간접적인 관련이 있는데,
이것은 관계형 데이터베이스가 객체를 구성하는 데이터의 영구 저장소 역활을 하기 때문이다.

도메인 객체의 생명주기

  • 객체들을 관리하는데 실패한다면 MODEL-DRIVEN-DESIGN을 시도하는 것이 쉽게 좌절될 수 있다

도메인 객체 관리와 관련된 문제는 두가지 범주로 나뉜다

  • 생명주기 동안 무결성 유지하기
  • 생명주기 관리의 복잡성으로 모델이 난해해지는 것을 방지하기

AGGREGATE(집합체)

  • AGGREGATE는 우리가 데이터 변경의 단위로 다루는 연관 객체의 묶음을 말한다
  • AGGREGATE에는 ROOT와 boundary가 있다 경계는 AGGREGATE에 무엇이 포함되고 포함되지 않는지를 정의한다. 루트는 단하나만 존재하며 특정 ENTITY를 가리킨다.
  • 경계 바깥의 객체는 해당 AGGREGAGE의 구성요소 가운데 ROOT만 참조할 수 있다.

개념적 AGGREGATE를 구현하려면 모든 트랜잭션에 적용되는 다음과 같은 규칙이 필요하다

  • ROOT ENTITY는 전역 식별성을 가지며 궁국적으로 불변식을 검사할 책임있음
  • 경계안의 ENTITY는 지역 식별성을 가지며 이러한 지역식별성은 해당 AGGREGATE안에서만 유일하다
  • AGG의 경계박에서는 ROOT ENTITY를 제외한 AGG 내부의 구성요소를 참조할 수 없다
  • 데이터베이스의 질의를 이용하면 AGG의 루트만 직접적으로 획득할 수 있다. 다른 객체는 모두 AGG를 탐색해서 발견해야 한다.
  • AGG안의 객체는 다른 AGG 루트만 참조할 수 있다.
  • AGG안의 객체를 변경하더라도 전체 AGG의 불변식은 모두 지켜져야 한다.
  • AGG는 생명주기 전 단계에서 불변식이 유지돼야 할 범위를 표시해준다.

FACTORY(팩토리)

어떤 객체나 전체 AGG를 생성하는 일이 복잡해지거나 내부 구조를 너무 많이 드러내는 경우 FACTORY가 캡슐화를 제공해준다.

  • 객체의 장점 중 대부분은 객체의 내부구조와 연관고나계를 정교하게 구성하는 데서 나온다.
  • 객체의 생명주기 중간 단계에서 수행해야 하는 것들이 많다. 문제는 이러한 책임만으로도 복잡한 객체에 객체 자체를 생성하는 책임까지 맡기는데 있다.
  • 복잡한 복합 객체를 조립하는 일은 조립이 완료됐을 때 해당 객체가 하는 일과 가장 관련성이 적은 일이다 (자동자 엔진과 자동자 조립공정 비교하며 설명)
  • 복잡한 객체를 생성하는 일은 도메인 계층의 책임이지만 그것이 모델을 표현하는 객체에 속하는 것은 아니다.
  • 즉, 모델 내의 어떤것에도 해당하지 않는 요소를 설계에 추가하는 것이긴 하지만, 그럼에도 요소는 도메인 계층에서 맡고 있는 책임의 일부를 구성한다는 것이다.

FACTORY를 잘설계 하기위한 두가지 기본요건은 다음과 같다

  • 각 생성 방법은 원자적이어야 함. AGG의 불변식을 모두 지켜야 한다.
  • FACTORY는 생성된 클래스보다는 생성하고자 하는 타입으로 추상화 되야한다

REPOSITYOTY

우리는 연관관계를 토대로 다른 객체와의 관계에 근거해 특정 객체를 찾을 수 있다. 그러나 객체의 생명주기 중간에도 ENTITY나 VALUE를 탐색하기 위한 진입점이 있어야 한다.

도메인 주도설계의 목표는 기술보다는 도메인에 대한 모델에 집중해 더 나은 소프트웨어를 만들어내는 것이다.

구현

데이터가 객체 데이터베이스나 관계형 데이터베이스에 저장되든, 아니면 단순히 메모리상에 상주하느냐에 관계없이 클라이언트 코드를 동일하게 유지하는 것이다.

  • 타입을 추상화 한다 REPO가 특정 타입의 모든 인스턴스를 담기는 하지만 이것이 각 클래스마다 하나의 REPO가 필요하다는 의미는 아니다. 타입은 계층 구조상의 추상 상위 클래스가 될 수 있다. 또는 인터페이스, 구상클래스가 될 수 있다.
  • 클라이언트와의 분리를 활용한다 이렇게 하면 클라이언트에서 직접 메커니즘을 호출했을때보다 더 자유롭게 REPO의 구현을 변경할 수 있다. 이로써 영속화 전략을 자유롭게 교체하면서 질의 기법을 다양하게 하거나 메모리상에 객체를 캐싱해 성능을 최적화 할 수도 있다.
  • 트랜잭션 제어를 클라이언트에 둔다. 데이터베이스에 대한 삽입과 삭제를 REPO에서 하겠지만 REPO는 아무것도 COMMIT하지 않을 것이다. 클라이언트에서 올바르게 단위 작업을 개시하고 커밋하는 컨텍스트가 있을것이다.
FACTORY와의 관계

FACTORY가 생애의 초기 단계를 다루는 데 반해 REPO는 중간, 마지막 단계를 관리하는데 도움을 준다.

관계형 데이터베이스르 위한 객체 설계
  • 데이터베이스가 하나의 객체저장소로 보여진다면 매칭 도구의 기능과는 상관없이 데이터모델과 객체모델이 서로 갈라지게 해서는 안된다.
  • 객체 시스템 외부의 프로세스는 이러한객체 저장소에 접근해서는 안된다.
    그러한 프로세스는 객체에 적용된 불변식을 위반할 수 있기 때문이다.
    그뿐만 아니라 그러한 프로세스가 접근하게 되면 데이터 모델을 고착화해서 객체를 리팩터링할때 변경하기가 힘들어진다.

언어의 사용(확장 예제) ★★★★★

DDDSample

DDD start와 이 책의 1부 2부 그리고 사내 code를 참조해서 DDD를 만들고 있었는데 비슷한 예가 나와서 반가웠다. 하지만 사내 code와는 조금 달라서일까 주제만 비슷하지 셈플코드나 설명은 조금 달라 의야한점이 많았다. 조금 더 해보고 다시 읽어 봐야겠다.

ETC

도메인 주도 설계 (Domain-Driven Design) 개요 WEBPONENT By Cyber-X

DDD 입문 @ZUM

DDDQ 2장 유비쿼터스 언어

Domain-driven Design Example

도메인 이벤트

도메인 이벤트: 디자인 및 구현
A better domain events pattern

룰엔진

Drools 룰 엔진 퀵 스타트

로직이 수시로 변경되는 경우 개발자들은 매번 프로그램을 수정해줘야만 한다.
아주 작은 조건의 변화라도 프로그램을 수정하고, 수정한 프로그램을 배포하는 작업을 해야만 한다.

이렇게 로직이 수시로 변경되는 경우에 사용할 수 있는 것이 바로 룰 엔진(Rule Engine)이다. 룰 엔진을 롤(Rule; 규칙, 즉 로직)을 별도로 저장해두고 프로그램에서 룰을 가져다 쓸 수 있도록 해 주는 기능을 제공한다.
프로그램에 있던 로직이 룰 엔진으로 옮겨가고, 프로그램에서는 롤 엔진을 통해서 룰(로직)을 실행하기만 하면 된다.

Review

1부 2부 까지 읽었다. 3부 부터는 통찰력과 경험이 필요한 파트같고 조금 더 DDD를 해본다음 답답할때 읽어봐야겠다.

'Learning' 카테고리의 다른 글

해커를 위한 디자인 레슨  (0) 2018.11.08
자바 객체지향 디자인 패턴  (0) 2018.08.08
REST API 디자인 규칙  (1) 2018.07.20
프로젝트가 서쪽으로 간 까닭  (0) 2018.07.10
네이버를 만든 기술 - 자바 편  (0) 2018.07.10

댓글