도메인 주도 설계란 무엇인가?

쉽고 간략하게 이해하는 DDD

이 책은 아카데미 컨퍼런스 2025년 2회차 책이다. 총 2회 스프린트로 진행했으며 약 1달간 진행했다.

객사오, 오브젝트를 읽고 나서 이후로 공부하면 좋은 개념인 DDD에 관심이 있었다. 이번에 읽게 된 책인 Domain Driven Design Quickly은 DDD라는 개념의 프롤로그 같은 책이다. 실제 코드가 등장하거나 많은 사례가 있기보다 용어와 개념에 대한 설명이 나와있는 책이라고 생각한다.

이해가 잘 되는 개념도 분명 있었고 몇번을 다시 읽은 개념도 있었다. 이후에 에릭 에반스의 Domain-Driven Design이른 거대한 책을 읽기 전에 미리 맛 볼 수 있어서 좋았다.

0장: 머리말

옮긴이의 글

옮긴이는 항상 이번에는 해결 가능할까? 라는 의문을 가지고 새로온 개발 방법론을 탐구한다. 그러던 도중 만나겐 DDD를 우리에게 전달해주기 위해 번역해주었다. 처음에는 더 좋은 설계로 시작한 고민이 지금은 DDD라는 좋은 무기를 전달해주기 위해 이 책을 번역한 것 같다.

옮긴이처럼 학문적인 탐구와 실제 적용을 위해 HappyDDDing와 같은 모임을 만들어서 지속적인 개발을 추구해보고 싶다. 최수경님 감사합니다.

서문: 쉽고 간략하게 이해하는 DDD를 왜 쓰게 되었는가

이 책을 쓰게 된 이유는 DDD라는 새로운 설계 방법이 지금의 개발에 가장 적합하다고 생각했기 때문이라고 한다. 실제로 주위의 개발자 친구들을 봐도 웹앱쪽의 경우 OOP에 대한 개념을 많이 필요로 하지 않는다. 그 대신 새로운 프레임웤이나 개념들에 대해서 더 높은 이해를 요구하는 듯 하다.

전통적인 객체지향 패러다임이 DDD와 만나 더 효과적이고 실제 개발과정에서 일어날 수 있는 소통의 오류나 설계 방법에 대해서 알아보는 책이라고 할 수 있다.

들어가기 전에

소프트웨어는 현실 생활의 복잡함을 다루기 위해 생성된 도구다. 소프트웨어는 목적을 위한 수단이며, 그 목적은 매우 실용적이고 실제적인 것이다.

소프트웨어의 본질이라고 생각한다. 다만 게임이라는 소프트웨어는 생활의 복잡함을 해소하기 위함보다 재미라는 목적이 다르다고 생각한다. 책에서는 도메인 즉, 필요에 의해 실제 세계의 문제를 소프트웨어로 설계하는 그 과정이 개발과정이라고 말한다.

바라보는 목적의 차이가 조금 존재할 뿐 소프트웨어를 통해 피드백을 받을 대상은 똑같고 소비자, 판매자의 입장은 동일하다. 따라서 주어진 문제에 따라 그것을 효과적으로 해결해 나가는 방식을 나는 게임 개발에 빗대어 생각해볼 생각이다.

소프트웨어 설계는 예술이다. 다른 예술과 마찬가지로 정밀한 과학이나 명제와 수식으로 무장하여 가르치거나 배울 수 있는 것이 아니다. 소프트웨어 개발에 적용할 수 있는 유용한 원칙이나 기법을 발견할 수 있지만, 정확한 경로는 제공할 수 없다.

결국 소프트웨어에는 개인적인 성향, 특별한 기질과 천부적인 재능이 포함되기 마련이다. 이 시작점에서 몇년간 확고해지고 단단해진 설계 방법론인 DDD를 가볍게 설명하며 왜 유용한지 다루는 책이다. 이 책은 DDD를 가볍게 다룬 책이므로 더 깊고 실용적인 예를 알고 싶다면 DDD관련 책을 추천

1장: 도메인 주도 설계란 무엇인가?

자동화된 비즈니스 프로세스나 현실 세계의 문제가 소프트웨어의 도메인이다.

기본적으로 개발자는 많은 시간을 코드로 작업하기에 소프트웨어를 단순하게 메서드와 오브젝트의 관점으로 보기도 한다. 다만 반드시 소프트웨어는 도메인과 뗄 수 없는 관계임을 알아야 한다.

단순하게 자동차도 부품들로 분리될 수 없다. (물론 대부분의 개발자 서적에서는 단순한 부품의 재귀형태로 만들어짐을 설명한다.) 자동차에는 비전이 있고 명세서가 있으며 그 과정이 담겨 있다.

소프트웨어도 마찬가지다. 좋은 소프트웨어를 만들기 위해서는 그 소프트웨어가 무엇인지를 알아야 한다. 예로 실제 금융어플 개발자 분들은 실제 금융 업무에 참여시킨다고 알고있다.

도메인에 대한 깊은 지식 없이 복잡한 소프트웨어를 만드는 것은 불가능하다. 만약 만들었다면 분명 사용자를 만족시키지 못할 것이다. 결국 소프트웨어 개발자가 시작해야하는 출발점은 도메인이다.

도메인과 조화를 이루는 소프트웨어를 만들기 위해서는 결국 도메인을 모델링해야 한다. 도메인을 모델링한다는 것은 결국 추상화를 말하며 추상화는 도메인을 표현한 모델이다.

단순하게 도메인 모델이라고 해서 특정한 다이어그램이 아니라 그 다이어그램이 전달하고자 하는 아이디어이다. 이 과정은 충분한 훈련이 필요하다고 생각한다. 단순하게 다이어그램을 그리는 것이 아닌 글로도 충분히 모델링이 가능하다.

모델은 소프트웨어에서 필수적인 부분이다. 복잡성을 다루려면 모델이 필요하다. 도메인에 대한 우리의 모든 사고 활동은 모델로 통합된다. 이러한 모델을 통해 개발자와 설계자들이 소통해야 한다.

코드 설계와 소프트웨어 설계는 다르다. 소프트웨어 설계는 집의 구조를 만드는 것이라면 코드 설계는 집에 걸 그림을 정하는 작업이다. 수정도 마찬가지로 소프트웨어 설계는 매우 비용이 크고 코드는 간단하다.

소프트웨어를 설계하는 방법에는 다양한 방법이 있다. 폭포수 모델, 애자일 그리고 DDD 등.. 기존 폭포수의 경우 개발자와 설계자의 관게에서 피드백이 전무하다는 단점이 있었다. 이후 등장한 익스트림 프로그래밍XP의 경우 빠른 피드백을 중심으로 지속적인 개발을 추구하는 방법이다.

애자일의 문제점은 단순성을 추구하지만 사람들마다 단순성을 바라보는 견해와 시각이 제각각이라는 점이다. 견고한 설계원칙이 빠진 상태로 지속적인 리팩터링을 수행하다 보면 변경하기 어려운 코드들이 양산된다.

DDD를 적용하면 도메인의 복잡한 문제를 모델링하고 구현할 수 있는 능력이 유지 가능한 방식으로 획기적으로 높아진다. 또한 설계와 개발 방식을 연관 지어 설계와 개발이 더 나은 해결책을 도출하는 데 어떻게 함께 작동할 수 있는지 보여준다.

도메인 지식 쌓기

만약 비행 항로 제어 시스템 구축 프로젝트를 생각해봤을 때 실제 전문가와 개발자의 시점은 다를 것이다. 앞서 말한 것 처럼 도메인을 모델링하는 과정은 소프트웨어 개발의 시작점이고 필수적이다.

따라서, 실제 도메인 전문가와 대화를 통해 이를 구체화하는 과정을 가져야 한다. 이 과정에서 서로 다른 언어로 대화하는 느낌을 받을 수 있지만 지속하며 그 연결점을 찾아야 한다.

이를 통해 밑 그림을 그리기 시작할텐데 정답도 아니며 완전하지도 않을 것이다. 다만 그 그림이 출발점이 될 것이다. 전문가와 대화를 통해 모델을 구축하는 과정은 상호 피드백으로 더 명확해지고 체계화될 것이다.

그 과정에서 도메인의 핵심 개념을 발굴해 내는 데 도움이 된다.

정리

과거 객체지향 책을 읽으면서 항상 들었던 생각은 도메인 모델에 대한 생각과 실제 코드에 대한 관계였다. 이 내용은 과거 객체지향의 사실과 오해 그리고 오브젝트라는 책을 읽으며 스스로의 언어로 정리한 내용이다.

도메인 모델은 실상 정적 모델이라고 할 수 있다. 정적 모델은 언어적으로 말하자면 클래스이고 정적인 형태라고 할 수 있다. 하지만 실제 소프트웨어는 동적 모델로 바라봐야 한다. 동적 모델은 동적이며 객체들의 집합으로 볼 수 있다.

마치 실제 소프트웨어는 정적 모델을 투사하여 동적 모델을 바라보듯 다형성을 중심으로 소프트웨어가 동작할 수 있도록 설계해야 한다고 생각했다. 1장을 읽고 나서는 그렇게 나만의 정적이자 도메인 모델로 만드는 과정이 실제 전문가와 많은 소통을 통해 핵심 개념을 찾아내는게 중요한 것 같다.

논의사항

책에서 말하는 전문가와 개발자의 대화를 통해 도메인을 구체화하는 과정이 필요한 이유가 두 가지로 느껴졌습니다.

첫 번째는 좋은 설계를 위해 실제 도메인에 대한 이해가 필요하다. 즉, 실제 동작 과정이나 해결법, 일반화해야 하는 부분 등 다양한 아이디어를 받아야 한다. 조금 더 코드적인 부분

두 번째는 소프트웨어는 결국 소비자가 있기 때문에 그들을 만족시키기 위해 진짜 목적인 좋은 소프트웨어를 만들어야 한다. 해당 소프트웨어의 목적이 뭔지 정확하게 알고 그것을 단순하고 쉽게 전달하고 빠르게 피드백을 통해 해결한다. 사용자적인 부분

이 외에도 DDD에서 말하는 도메인 모델링이 필요한 이유들에 대해서 같이 논의해보면 재밌을 것 같습니다.

2장: 유비쿼터스 언어

개발자는 언제나 개발자이지만 도메인 전문가는 개발자의 개념을 알지 못한다. 의사소통 방식의 차이를 극복하기 위해선 모델을 만들 때, 모델에 대한 아이디어, 포함되어야 할 요소, 그것들을 어떻게 연결할 것인지, 어떤 것들이 관계가 있고 어떤 것들이 관계가 없는지와 같은 정보를 교환해야만 한다.

이 의사소통은 자신만의 전문 분야의 언어를 사용하기에 개발자는 개발언어 도메인 전문가는 그들의 전문 언어로 대화하게 된다. 따라서 모델을 이야기할 때는 공통된 언어로 이야기할 필요가 있다. 여기선 개개인의 언어를 방언과 같이 이야기했다.

이것을 지칭하는 말이 유비쿼터스 언어이다. 버그를 해결하고 새로운 문제에 직면하고 더 좋은 설계를 하기 위해서는 필요하다. 이런 언어는 하루밤 사이에 만들어지지 않는다. 언어가 지녀야 할 핵심 요소들을 골라내는 것은 어렵고 집중을 필요로 하는 일이다.

명확한 의미를 도출할 수 있는 단어가 필요하고 개발하며 사용하는 언어는 대안이 될만한 단어들을 탐색하여 합의해야 한다.

유비쿼터스 언어 만들기

단적이지만 개발자와 전문가의 대화를 통해 하나의 언어를 만들어가는 과정을 보여준다. 여기서 중요한 점은 과정이라는 것이다. 틀린 길을 가서 오히려 더 좋은 정보를 발견하기도 하고, 공통되는 부분을 찾아 합의하는 그런 과정이 결국에는 더 좋은 소프트웨어를 만들 수 있도록 한다는 것이다.

재귀적으로 계속해서 팀에 좋은 영향과 같은 모델을 공유하고 대화가 가능해지는 것이다. 모델의 핵심은 UML이 아니며 결국은 모델도 도메인에 맞게, 그 특성에 맞게 정리되어가는 것이다. 내 생각이지만 UML은 이해관계자에게 설명하기에 효과적이라고 생각한다.

정리

유비쿼터스 언어를 처음 들은 것은 작년 멘토님과 저녁 식사도중에 듣게 되었다. 대부분의 게임을 유니티로 작업하던 나는 언리얼을 접하게 되면서 엔진의 구조 차이로 기존에 생각하던 게임 구조가 대부분 유니티에 고정되어 있다는 사실을 알게 되었고, 한 차원을 넘어 게임 자체를 머리속으로 정리하고 있다는 말에 유비쿼터스 언어에 대한 설명을 들을 수 있었다.

책에서 말하는 개발자와 전문가의 형태는 아닐 수 있다. 나는 개발자이기 때문에 내가 가진 도메인은 애초에 게임의 구조에 대한 고민이었고 그 도메인을 스스로 나와 대화하며 기존의 개발자로 가진 전문성 영역에서 한 단계 추상화한 구조로 옮기려 한 시도라고 생각한다.

게임은 전문가 필요하다고 보기엔 어렵고, 단순하게 기획자와 개발자간의 대화가 이루어진다. 그 대화에는 대부분 표준이라는게 존재하기에 유비쿼터스 언어보다 실무적인 경험이 더 중요할 것 같다는 생각도 든다.

논의사항

책에서 말하는 것과 같이 사람마다 주어진 도메인을 모델링하는 과정이 다를 것 같습니다. (방언) 팀 표준이 있더라도 자신이 재해석하는 과정이 필요하다고 생각되는데 그 과정에 대해서 같이 논의해보면 도움이 될 것 같습니다.

3장: 모델 주도 설계

앞서 다룬 내용은 도메인 전문가와 대화를 통해 도메인에 대한 깊은 이해를 바탕으로 모댈링을 하는 과정을 말했다면 다음으로 중요한 모델을 코드로 구현하는 과정을 설명한다.

모델링된 모델을 두고 개발자는 자신의 생각을 덧붙여서 고유한 설계를 만들어낸다. 개발이 계속되면서 더 많은 클래스가 추가되며 코드와 모델사이의 간격이 넓어진다. 설계자와 개발자를 분리시키거나 모델과 코드를 분리하는 등의 개발방식은 켤코 좋은 소프트웨어를 생산하지 못한다.

아마 프로그래밍이 매력적인 이유도 같은 도메인이라도 전부 다른 설계를 만들어내기 때문이라 생각된다.

코드를 작성하는 사람은 모델을 아주 잘 알고 있어야 하고, 모델의 무결성에 대해 책임감을 느껴야 한다. 그리고 코드의 변경은 곧 모델의 변경을 의미한다는 것을 깨달아야 한다.

결국 이 말은 모델에 기여하는 기술자들은 모두 역할에 상관없이 코드에 기여하는 작업을 해야하고 코드를 변경하는 책임이 있는 모든 사람들은 코드를 통해 모델을 표현하는 방법을 배워야 한다. 모든 개발자도 모델을 주제로 한 일정 수준의 논의에 참석하고 도메인 전문가와 만나야 한다. 여기서 중요한 것이 유비쿼터스 언어의 필요성이다.

이런 과정이 불필요하다고 느껴지고 모델과 설계의 핵심 부분이 잘 맞아 떨어지지 않는다면 모델은 가치가 없으며 오히려 비용적인 측면이 강화될 것이다.

소프트웨어 시스템의 각 부분은 도메인 모델이 글자 그대로 반영 되도록 설계해야 한다. 코드는 결국 모델을 표현한 것이고, 코드의 변경이 곧 모델의 변경으로 이어질 것이다.

객체지향프로그래밍(OOP)는 모델을 구현하기 적합한 패러다임으로 모델을 투사하여 표현하기에 유용하다. 복잡한 도메인은 절차적 언어로 대응하는 모델을 만들어내는 데 한계가 있다.

모델 주도 설계를 위한 블록

모델 주도 설계에서 사용되는 가장 중요한 패턴들을 소개한다. 여기 등장하는 패턴들의 목적은 도메인 주도 설계 관점에서 객체 모델링 및 소프트웨어 설계의 핵심 요소들을 보여주는 데 있다.

계층형 아키텍처

실제로 애플리케이션을 만들 때, 상당 부분은 도메인과 직접적인 연관이 없는 부분이 많다. 그 목적을 동작하게 하기 위한 제반 시스템이 주를 이루거나 도메인은 다른 부분에 비해 적은 비중을 차지한다. 네트워크 코드, 데이터베이스, 사용자 인터페이스 등등..

객체지향 프로그램에서 UI, 데이터베이스, 도메인과 직접 관련되어 있지 않은 여타 지원 성격의 코드들은 흔히 직접 비즈니스 객체 내부에 작성된다. 이런 경우는 대부분 일을 가장 빠르게 처리할 수 있기 때문이다.

그러나 이처럼 도메인과 관련된 코드가 다른 레이어와 섞여 있다면, 다른 사람이 코드를 읽고 이해하기가 매우 어렵다. 결국 프로그램이 복잡해지고 테스트도 어려워 진다.

따라서 복잡한 프로그램을 ‘레이어’로 분할해야 한다. 각 레이어 내부에서 설계를 수행하여 응집도 높고 자기 하위 레이어에만 의존하도록 만들어야 한다. 또한 상위의 레이어에 대한 결합도를 낮추려면 표준적인 아키텍처 패턴을 따라야 한다.

하나의 레이어에 도메인과 관련된 모든 코드를 집중시켜서, 사용자 인터페이스, 애플리케이션, 인프라스트럭처 코드로부터 독립적으로 만들어야 한다.

  • 사용자 인터페이스
    • 사용자에게 정보를 보여주고 사용자의 명령을 해석하는 책임을 진다.
  • 애플리케이션 레이어
    • 애플리케이션 활동을 조율하는 얇은 레이어다. 업무 로직을 포함하지 않는다. 비즈니스 객체의 상태를 보관하지 않지만, 애플리케이션 작업의 처리 상태는 보관한다.
  • 도메인 레이어
    • 도메인의 정보를 포함한다. 업무 소프트웨어의 심장에 해당한다. 비즈니스 객체의 상태를 포함한다. 비즈니스 객체와 이 객체의 상태 정보 중 가능한 부분의 영속성에 대한 책임은 인프라스트럭처 레이어로 위임된다.
  • 인프라스트럭처 레이어
    • 다른 레이어 모두를 지원하는 라이브러리로 동작한다. 레이어 간의 통신을 제공하고 비즈니스 객체의 영속성을 구현하고 사용자 인터페이스 레이어의 라이브러리를 포함한다.

애플리케이션을 분할된 레이어를 나누고 레이어 간의 상호작용 규칙을 수립하는 일은 매우 중요하다. 게임도 마찬가지로 레이어가 존재한다. 다만 명칭은 다르지만 목적성은 동일하다고 생각된다.

레이어를 분할했다는 것은 도메인 레이어는 핵심 도메인 이슈에만 집중해야 한다. 조금 더 확장해서 이렇게 레이어를 분할하여 상위 레이어가 하위 레이어에 일반적인 의존을 가지는 것은 꼭 레이어에 국한된 것이 아니라 더 작은 부분에서도 이 법칙이 적용되어야 한다. 예를 들어 UI레이어 코드 내에서도 여러 계층을 만들고 그 계층끼리 앞서 말한 구조를 유지해야 응집도가 높고 결합도가 낮은 구조가 재귀적으로 만들어진다.

엔티티

소프트웨어가 여러 상태를 거치는 동안에도 동일한 값을 유지하는 식별자를 지니는 유형의 객체가 있다. 이러한 객체들에게 중요한 것은 속성이 아니라, 시스템 전 생명주기 동안, 또는 그 이상으로 확장될 수 있는 연속성과 식별성의 흐름이다. 이러한 객체를 엔티티라고 부른다.

사람의 주민등록번호, 게임 유저의 고유 코드 ID, 사람의 경우 여러 속성들의 조합, 계좌번호 등 이러한 고유한 식별자로 객체를 구분 및 관리한다. 객체가 일반 속성이 아닌 식별자에 의해 구별된다면, 모델의 해당 객체 정의에는 주로 이 식별자가 반영되어야 한다.

엔티티는 도메인 모델에서 매우 중요한 객체들이고, 모델링 작업을 시작할 때부터 깊이 고민해야 할 부분이기도 하다. 더불어 어떤 객체를 엔티티로 봐야 할지 말아야 할지 여부를 결정하는 작업 또한 매우 중요하다.

값 객체

엔티티는 도메인 모델링에 필수적이다. 엔티티는 추적이 될 수 있어야 한다. 추적 가능하다는 것은 그만큼의 비용을 지불해야 한다는 것을 말한다. 즉, 모든 객체를 엔티티로 만들면 성능상의 문제가 발생할 수 있다.

값 객체는 영속성이 필요 없는, 하나의 객체가 도메인의 어떠한 측면을 표현하는 데 사용되지만 식별자가 없는 경우이며, 이것을 값 객체라고 부른다.

쉽게 값 객체를 떠올리듯이 엔티티의 정의에 부합하는 객체만을 엔티티로 두고 나머지를 값 객체로 만들어라. 이렇게 하면 설계가 좀 더 단순화되고, 다른 긍정적인 결과도 생긴다.

값 객체는 알아서 소멸될 것이다 (GC에 의해), 다만 값 객체는 수정할 수 없게 만들어야 한다. 생성자를 통해 값 객체를 생성하고 나면 생명주기 동안 상태가 변경되지 않는다. (필요하다면 복사하여 수정) 값 객체가 불변을 보장해야 한다는 것은 객체지향의 기본적인 원리이자 황금률이다.

서비스

도메인을 분석하여 모델을 구성하는 주요 객체를 정의할 때 도메인의 특정 부분들은 객체로 쉽게 매핑될 수 없다는 사실을 발견하게 된다. 예를 들어 한 계좌에서 다른 계좌로 돈을 보내는 기능의 경우 어떤 계좌에 이 기능이 존재해야 하는지에 대한 문제이다.

어느 한쪽도 아니고 이는 서비스에 해당한다. 서비스 객체는 내부적인 상태를 가지지 않으면서, 단순히 도메인에 기능을 제공하는 목적을 지닌다. 서비스가 제공하는 기능은 매우 중요하고, 서비스는 엔티티와 값 객체에 기여하는 관련된 기능을 묶을 수 있다.

서비스는 하나의 개념들 캡슐화하여 도메인에서 명확하게 구분되어 만들어지기 때문에, 명시적으로 선언하는 편이 좋다. 엔티티나 값 객체에 이런 기능을 넣는다면 해당 객체의 의미를 불분명하게 만들어 혼란스러워질 것이다.

서비스는 오퍼레이션을 제공하는 인터페이스와 비슷하게 동작한다.

  • 서비스 특징
    • 서비스에 의해 수행되는 오퍼레이션은 일반적으로 엔티티 또는 값 객체에 속할 수 없는 도메인의 개념을 나타낸다.
    • 수행되는 오퍼레이션은 도메인의 다른 객체를 참조한다.
    • 오퍼레이션은 상태를 저장하지 않는다.

애플리케이션이나 도메인 서비스들은 양쪽 모두 그 객체들과 직접 관계된 기능을 제공하는 도메인 엔티티와 값 위에 만들어진다. 따라서 서비스가 속하는 레이어를 결정하기는 어렵다.

서비스라는 명칭을 익숙하지 않게 느낀 것 같다. 실제로 객체의 책임이 예매해지는 경우가 종종 있는데 그럴 때 마다 다른 객체를 만들어 위임하는 등 여러 방법들을 사용했지만 설계 단계에서 그걸 서비스라는 명칭으로 정의할 수 있다는 점이 마음에 든다.

하지만 오퍼레이션이 개념적으로 애플리케이션 레이어에 속하는 일을 한다면 해당 서비스는 그 레이어에 존재하는 게 맞다. 같은 맥락으로 도메인 레이어도 마찬가지다. 즉, 서비스도 레이어에 맞게 존재해야 한다.

모듈

규모가 크고 복잡한 애플리케이션의 경우 그 모델은 점점 더 커지는 경향이 있다. 어떤 지점에 다다르면 모델 전체를 가지고 이야기하는 것이 힘들어지고 작은 부분들 간의 관계나 상호작용을 이해하기도 어려워진다.

이러한 이유로 인해, 모델은 모듈로 나누어 구조화할 필요가 있다. 모듈화란, 관련된 개념과 작업을 조직화하여 복잡도를 감소시키는 기법이다.

대규모 모델일지라도 모델에 속해 있는 모듈과 이들 간의 관계를 중심으로 본다면 그 개요를 파악하기 쉬워진다. 모듈들 간의 상호작용이 이해되었다면 그때부터 모듈 하나하나의 내부를 파악하기 시작하면 된다.

이 방법으로 소스코드를 읽어야 한다.

모듈을 사용하는 또 다른 이유는 코드의 품질 때문이다. 코드가 높은 응짐도와 낮은 결합도를 추구해야 한다는 사실은 이미 널리 알려진 사실이다. 응집도는 클래스와 메서드 레벨에서 시작하지만 모듈 레벨에서도 적용될 수 있다.

응집도를 가능한 최대화하기 위해 밀접한 관계를 지닌 클래스들을 하나의 모듈로 정의하는 방법이 권장된다. 응집도에는 여러 종류가 있지만 가장 널리 알려진 종류는 통신 응집도기능 응집도이다.

통신 응집도는 모듈의 일부가 같은 데이터를 다룰 때 얻을 수 있다. 이 코드들은 관계가 견고하기 때문에 그룹핑하는 것이 당연하다. 데이터 응집도가 더 맞는 말 아닌가? 기능 음집도는 모듈의 모든 부분이 잘 정의된 임무를 함께 수행하고 있을 때 얻어지며 최고의 응집도라고 할 수 있다.

모듈은 다른 모듈들이 접근할 수 있는 잘 정의된 인터페이스를 가져야 한다. 내부 객체에 접근을 허용하기 보다 인터페이스를 통해 접근하는 것이 결합도가 감소하기에 더 좋다. 또한 모듈 하나가 다수의 모듈과 연결되어 있는 것 보다 잘 정의된 입무를 수행하는 모듈끼리 연결 고리가 별로 없을 때 시스템의 기능을 이해하기 수월하다.

모델은 상위 수준의 도메인 개념에 잘 부합하게 분할되도록 정련하고, 관련 코드도 서로 결합되지 않도록 해야 한다. ‘정련하다’ 순수하게 만들다. 부합물을 없애다.

모듈은 설계 단계에서 구체화되어야 한다. 유비쿼터스 언어에 맞게 이름도 필요하며 지속적인 통합과 학장이 필요하다.

집합

집합은 내가 앞 모듈파트에서 정리한 부분의 좀 더 작은 부분이라고 생각한다. 일종의 생명주기를 다루는 것으로 응집도를 높이기 위해 그룹핑을 하지만 관계이자 주기에서 결합도를 낮추기 위해 집합을 사용한다.

집합은 객체의 소유권과 경계를 정의하는 데 사용되는 패턴이다. 모델 하나에는 굉장히 많은 도메인 객체를 담을 수 있다. 이 때 수많은 객체들이 서로 연결된다. 예를 들어 고객과 계좌에서 일대일 관계는 두 객체간의 참조가 있음을 말할 수 있다. 고객과 고객, 계좌 정보 등

모델에서 도전과제는 완성도를 갖춘 모델보다 모델을 더 단순화하고 이해하기 쉽게 만들 수 있느냐에 달려있다. 그래서 대부분 모델에서 관계를 제거하거나 단순화하는데 시간을 보낸다.

일대다 관계는 관계될 수많은 객체를 수반하기 때문에 복잡하지만 한 객체가 다른 객체의 집합관계를 가지는 것으로 단순화할 수 있다. 다대다 관계는 대부분 양방향이다. 이 관계는 복잡도를 가파르게 상승시켜 객체들의 생명주기 관리를 어렵게 만든다. 따라서 관계에 참여하는 숫자의 수를 최소화하는 것이 좋다.

이해, 관계, 복잡성, 응집도, 결합도 모든 측면에서 단순한 것이 좋다.

  • 관계의 수를 줄이는 방법
    • 모델의 핵심 사항이 아닌 관계가 있다면 제거한다.
    • 다수성의 숫자는 제약사항을 추가하여 감소시킨다.
    • 많은 경우 양방향 관계는 단방향 관계로 대체될 수 있다.
      • 일종의 상속관계에서 포함관계로의 전환, 상위가 하위에 의존한다.

또한 불변식을 따르도록 강제해야 한다. 불변식은 데이터가 변경될 때 마다 검증하는 규칙이다. 그러나 수많은 객체가 수정된 데이터 객체를 참조하고 있을 때, 이 규칙을 실현하기는 매우 어렵다. 따라서 집합을 사용해야 한다.

집합은 결국 데이터를 변경할 때 하나의 단위로 간주되는 관련된 객체들의 집합이다. 집합은 하나의 객체의 외부와 내부를 가르는 경계를 정헤 구분한다. 클래스간의 모듈 느낌으로 해석했다.

각 집합은 하나의 root를 지닌다. root는 엔티티이고 외부에서 접근할 수 있는 창구이다. root는 집합된 다른 객체들에 대한 참조를 담고 있으며, 다른 객체들은 서로 관계를 맺고 있다. 그러나 객체의 외부에서는 root 객체를 통해서만 참조할 수 있다.

집합이 무결성과 불변식을 보장하는 이유는 다른 객체들은 root에 대한 참조만 지니기 때문이다. 즉, 다른 객체들은 집합에 속한 다른 객체들을 변경할 수 없다는 말이다. root 자체를 변경하거나 root에게 요청한다.

이것이 중요한 이유는 앞서 말한 생명주기에 관련이 있다. 현재 참조가 집합에 의해서 관리된다는 것은 결국 집합 외부의 객체가 집합 내부의 객체의 행동이나 값이 필요할 때 root를 거쳐야 한다는 것이고 이것은 제어 가능한 오퍼레이션 영역의 일이다. 따라서 root의 제거가 곧 집합 전체의 삭제이기 때문에 결합도가 매우 낮다.

실제로도 참조 복사가 아닌 새로 생성하여 복사하여 객체의 불변성도 보장한다. 해당 데이터를 활용하는 것은 그 객체의 일이다. root 엔티티는 전역 식별자를 지니며 불변식을 검증할 책임을 진다. 내부 엔티티는 지역 식별자를 지닌다.

팩토리

복잡한 집합을 자신의 생성자를 이용해 만들어 내려는 시도는 실제 도메인의 처리 방식과는 정반대 방식이다. 실제 도메인에서는 사물이 외부의 다른 사물에 의해 생성된다.

만약 Customer 객체가 다른 객체를 생성하고자 한다면 몇 개의 매개변수를 가지고 생성을 할 것이다. 그러나 객체를 생성하기란 힘든 일이다. 객체를 생성하는 동안에는 객체의 내부 구조, 그 객체가 포함하고 있는 다른 객체가 서로 맺을 관계, 이와 관련되어 적용해야 할 규칙에 대한 충분한 지식이 필요하다.

그러나 이러한 지식을 포함하면 도메인 객체의 캡슐화와 집합의 캡슐화에 위배된다. 따라서 복잡한 객체의 생성의 절차를 캡슐화할 수 있는 새로운 개념이 도입될 필요가 있다. 이것이 팩토리다. 팩토리는 객체 생성에 필요한 지식을 캡슐화하는 데 사용되며 집합을 생성하는 데 특히 유용하다.

중요한 것은 생성 절차를 쪼갤 수 없는 원자적인 상태로 만들어야 한다. 만약 그렇지 못한다면 절반만 생성되거나 일부 객체가 정의되지 않은 상태일 수 있다. 이후에 정의가 되어야 한다면 NULL객체를 사용해야 할 수 있다. 게임에서 캐릭터를 생성할 때 아직 스킬을 배우지 않은 상태라면 아무것도 없는 스킬을 넣는 방식

팩토리를 구현하는 몇 가지 설계 패턴이 있다. 팩토리 메서드추상적인 팩토리라는 두 패턴이다. 이 패턴들을 설계 관점이 아닌 도메인 모델링 관점에서 사용해 볼 것이다.

팩토리 메서드는 다른 객체를 생성하는 데 필요한 자식을 포함하지만, 외부에 드러내지 않는 객체 메서드다. 이 메서드는 집합에 속한 객체를 생성하고자 할 때 매우 유용하다. 객체 생성과 불변식을 보장하는 메서드를 집합 root에 추가해서 객체의 참조나 복사본을 되돌려 주는 것이다.

만약 객체의 생성이 좀 더 복잡하거나 여러 객체를 생성하는 경우에는 객체의 생성을 별도로 전담하는 팩토리 객체가 수행하고 생성 자체는 외부에 드러나지 않아야 한다.

팩토리를 생성할 때는, 객체의 캡슐화를 깨뜨리기 때문에 매우 주의해서 작업해야 한다. 생성 작업과 관련된 규칙이나 불변식에 영향을 미치는 어떠한 변경이 객체에 일어날 때 마다, 새로운 조건을 지원할 수 있도록 팩토리를 수정해야 한다.

팩토리들은 생성한 객체들과 밀접한 관련이 있다. 이것은 설계 관점에서 약점이지만 동시에 강점이기도 하다. 집합은 밀접하게 관련된 연속된 객체를 포함한다. root의 생성은 집합에 속해 있는 다른 객체들의 생성과 연관되어 있고, 집합 내부 객체들을 연관시키는 일정 수준의 로직을 추가해야 한다.

그 로직은 원래 어떤 객체에도 속하지 않는다. 왜냐하면 다른 객체의 생성에 관여하는 내용이기 때문이다. 이러한 집합 전반의 생성 작업은 다루는 업무는 특별한 factory 클래스가 맡는 것이 적절하다.

또한, 엔티티 팩토리와 값 객체 팩토리는 다르다. 값은 변하지 않는 객체이고 모든 속성 값이 생성 시점에 정의되어야 한다. 반대로 엔티티는 변경될 수 있기에 이를 고려해야 한다.

  • 팩토리 대신 생성자를 사용하는 경우
    • 생성 작업이 복잡하지 않다.
    • 객체의 생성이 다른 객체의 생성과 연관되어 있지 않으며 모든 속성이 생성자를 통해 전달되어야 한다.
    • 클라이언트가 구현에 관심이 있어서, 사용할 전략 패턴을 선택하려고 한다.
    • 클래스가 바로 해당 타입이다. 관련된 계층 구조가 없어서 구현 목록에서 선택할 필요가 없다.

팩토리는 아무것도 없는 상태에서 완전히 새로운 객체를 생성할 필요가 있다.

리파지토리

모델 중심의 설계에서 객체에는 생성과 함께 삶을 시작하여 삭제되거나 저장소에 보관되는 순간 생을 마친다. 생성자, 팩토리가 객체의 사용을 위해 생성을 한다면 리파지토리는 객체의 참조를 얻는 로직을 캡슐화하기 위해 사용한다.

만약 코드에서 리파지토리를 사용하지 않는다면 다른 객체의 참조를 얻기 위해 (이미 생성된 객체) 데이터베이스를 쿼리하거나 인프라스트럭처를 참조하는 등 원래의 도메인 모델링, 설계의 측면에서 벗어난다. 이는 곧 복잡성을 의미한다.

리파지토리는 몇몇 객체의 참조를 저장할 수도 있다. 객체가 생성되었을 때 리파지토리에 저장되고, 나중에 사용할 목적으로 조회된다. 일종의 클라이언트와 데이터베이스의 중간 다리 역할이다.

리파지토리는 전략을 포함하기도 한다. 특정 영역에 접근하거나 다른 곳에 접근할 때 특별한 전략을 만든다. 전역 접근이 필요한 여러 유형의 객체의 경우 해당 타입 객체가 메모리에 집합적으로 존재하는 것처럼 보이게 하는 객체를 생성하라. 그리고 널리 알려진 인터페이스를 통해 여기에 접근이 가능하게 한다.

실제로 리파지토리에서 사용하는 쿼리나 기술적인 내용을 숨겨서 직접 접근할 필요가 있는 root에 대해서만 리파지토리를 제공한다. 클라이언트는 모델에만 집중하고 객체의 저장이나 접근과 관련된 내용은 리파지토리에 위임한다.

리파지토리는 인프라스트럭처의 구현과 매우 유사해지지만, 리파지토리 인터페이스는 순수하게 도메인 모델이다. 게임에서 로그인할 때 클라이언트에 해당 유저 정보를 반환해주는

팩토리는 객체의 생성에 관여하는 반면에, 리파지토리는 이미 존재하는 객체들을 관리한다. 리파지토리는 자체적으로 객체를 캐싱하기도 하지만 영송적인 스토리지에서 읽어와야 할 경우가 더 많다. 팩토리는 위에서 본 것 처럼 순수 도메인에 가깝지만 리파지토리는 데이터베이스나 인프라스트럭처와 이어지는 연결이 포함될 수 있다.

정리

이런 방법론이나 패러다임에 대한 책을 읽다보면 좋은 것은 알겠으나 실제로 적용 가능한가? 에 대해서는 결국 성숙한 팀으로 귀결되는 것 같다. 10명과 일을 한다면 9명 중 1명만 이에 대해서 부정적이거나 관심이 없다면 생산성이 크게 떨어질 것 같다는 생각이다.

모두를 설득시키는 비용 또한 매우 쎄고 팀 마다 다르고 회사마다 다를 것이다. 이상적인 내용이지만 그 만큼 팀의 수준이 높을 경우 퍼포먼스가 뛰어날 것 같다.

3장은 기본적인 틀은 객체지향 설계이지만 그 중심이 모델이 되어야 하는 이유와 더 좋은 점에 대해서 설명해준 것 같다. 특히 용어에 대해서 설명도 직관적이고 그 동안 생겼던 의문들이 조금 해결이 되기도 했다.

또한 모델 주도 설계를 위한 블록이 내용 하나만으로 하나의 시스템을 덩어리로 나눠 그려볼 수 있겠다는 생각도 들면서 객체간의 느슨한 참조를 위한 방법론 같다는 생각입니다. 물론 책에서 말하듯 도메인에 관한 내용이기에 실제 개발에서는 더 많은 부분의 코드들이 존재할 것 같습니다.

논의사항

4장: 깊은 통찰을 향한 리팩터링

지속적인 리팩터링

만약 모델 없이 설계한다면, 우리가 만든 소프트웨어는 지원해야 할 도메인에 전혀 부합하지 않을 것이다.

리팩터링의 궁극적인 목표는 코드를 나쁘지 않게, 다시 말해 더 낫게 만드는 것이다. 이때, 자동화 테스트를 하면 수정 작업이 기존 기능의 어떤 것도 망치지 않았음을 확신할 수 있다. 현대에는 이런 리팩터링을 위한 도구 IDE가 많이 발전되기도 했으며 관련된 패턴도 있다.

코드적인 리팩터링도 있지만 도메인과 그 모델에 관련된 또 다른 유형의 리팩터링이 있다. 때로는 도메인에 대한 새로운 통찰이 생기고 어떤 것들은 점점 명확해지며, 둘 간의 관계가 발전되기도 한다. 이러한 것들은 모두 리팩터링을 통해 설계에 반영되어야 한다.

코드가 진정한 모델의 실체를 표현하는 순간은 코드를 읽었을 때 그 코드가 무슨 일을 하고 있는지 직관적으로 이해할 수 있을 때이다.

모델링에 대해서 가장 먼저 배운 것은 비즈니스 명세를 읽고 명사와 동사를 찾는 것이다. 명사는 클래스로, 동사는 메서드로 변환된다. 이것은 심한 단순화이며 결국은 편합한 모델을 만들어낼 뿐이다. 모든 모델은 초기에는 깊이가 얕을 수밖에 없으나 모델이 점점 깊은 통찰을 가지도록 개선해야 한다.

설계는 유연해야 한다. 유연하지 못한 설계는 리팩터링을 막는다.

유연성을 생각하지 않고 만들어진 코드로는 일해 나가기가 어렵다. 변경이 필요할 때마다 코드와 싸워야 하고, 이 코드를 개선하는데 많은 시간을 소모할 것이기 때문이다.

피상적인 것은 버리고 본질적인 것만 표현한 모델이 깊이 있는 모델이다. 이런 모델은 소프트웨어 개발자와 도메인 전문가의 생각과 일치되고 전문가의 미묘한 고민을 표헌하며 실용적인 설계를 이끌어낸다. 이러한 모델이 정교한 도메인 모델이다. 이를 위해선 지속적인 리팩터링이 필요하다.

핵심 개념 드러내기

리팩터링은 작은 단계로 나누어 진행되며, 그 결과 또한 작은 개선의 연속으로 나타난다. 수많은 작은 변경이 결국 설계에는 아주 미미한 가치를 제공하는 경우도 많고, 소규모의 변경이 큰 차이를 초래하는 경우도 있다. 후자가 바로 도약이다.

우리는 다듬어지지 않고 피상적인 모델에서부터 시작한다. 조금씩 다듬어 나가고 도메인과 관련된 깊은 지식과 이해관계에 대한 더 나은 인식을 바탕으로 삼아 설계해 나간다. 우리는 새로운 개념과 추상화를 추가한다. 이제 설계는 개선되었고, 개선은 각각 설계를 조금 더 명확하게 한다. 이로써 도약을 위한 전제가 만들어진다.

도약의 경우는 코드를 작성하다 보면 나오는 결과로 DDD가 아니더라도 자주 발생한다고 생각한다. 도약의 가능성이 오히려 적어야 더 좋은 설계가 아닐까?

도약으로 이어지는 암시적 개념은 그대로 두어선 안되고 그것이 도메인 개념이라면 모델과 설계에 반영해야 한다. 앞서 비행 시뮬레이션의 예로 이를 구체화하다 보니 비행기의 도착을 관리하는 직원의 존재가 드러났다. 같은..?

명시적인 것을 만들어 낼 때 유용한 추가 개념은 제약 조건, 처리, 명세다. 제약 조건은 불변식을 표한하는 간단한 방법이다. 객체의 데이터에 무슨 일이 일어나든 불변식은 지켜져야 한다.

서비스가 필요한 경우에는 전략패턴으로 이어진다는 흐름이 좋다.

  • 암시적 개념을 인지하는 방법
    • 언어를 주의 깊게 듣는 것
      • 유비쿼터스 언어가 필요한 이유과 같이 일부 정보가 언어의 차이에 의해 누락되거나 잘못 전달될 가능성이 있기에 암시적 개념을 인지하기 위해선 언어를 주의 깊게 들어야 한다.
    • 누락된 개념이 없는지 찾기 위해 노력하자
      • 설계의 어떤 부분은 분명하지 않은 경우도 있다. 전문가의 영역에서 벗어나는 경우 무엇인가 누락되어 있는 경우가 종종 있기에 이를 위해 다른 어떤 객체가 그 자리를 차지하게 되면서 필요없는 역할과 책임을 지니게 된다 이는 결국 객체가 비대하게 되며 설계의 명료성이 떨어지는 결과를 가져온다. 일종의 갓 객체가 만들어지는 상황
    • 상충되는 것들을 조화시키는 시도를 하자
      • 지식 체계를 만들 때는 모순에 부딪히기도 한다. 도메인 전문가가 말하는 것이 다른 결정을 유지하는 데 배치되는 것처럼보일 수 있다. 요구사항이 다른 요구사항에 상충되는 것 같기도 하다. 그러나 상충되는 것의 일부는 실제로 상충되는 것이 아니며, 같은 사물을 바라보는 관점의 차이거나 단순히 설명의 부족일 수 있다.
    • 해당 도메인의 문헌을 활용하자
      • 해당 도메인에 대한 정보를 담고 있는 책을 활용하여 도메인에 대한 깊이 있는 관점을 얻을 수 있다.
    • 명세를 활용하라.
      • 명세는 객체가 어떠한 요구를 만족하였느지 또는 어떠한 목적에 비추어 준비가 되었느지 살펴보기 위해 객체를 테스트하는 용도로 사용된다.

5장 모델 무결성 보존

이번 장은 대규모 프로젝트를 다루는데, DDD가 기초가 되어 설계된다.

대규모 설계에서는 여러 팀이 참여하여 하나의 모델을 각각 모듈로 나누어 담당하게 되는데, 이 때 모든 사람이 공통의 모델을 바라보지 않기 때문에 작은 변화가 큰 폭풍을 일으킨다.

좋은 모델에서 출발하여 불일치가 많은 쪽으로 변질되기는 쉽다. 모델이 갖추어야 할 첫 번째 요건은 변하지 않는 용어와 모순 없는 일관성이다. 하지만 기업 규모 프로젝트를 통일된 모델로 만드는 것은 이상적일 수 있다. 이때에는 많은 팀의 노력이 필요하다.

대규모 프로젝트에서 DDD가 이상적인 이유와 실제로 생기는 문제점에 대해서 공감될 수 있도록 잘 설명해준다. 또한, 모델의 무결성을 실제로 실현하기 위한 모델의 분할과 같은 방법에 대해서 다룬다.

분할된 컨텍스트

모델은 각각 컨텍스트가 하나씩 있다. 하나의 모델을 다룰 때 컨텍스트는 암묵적이며, 우리는 그 컨텍스트를 정의할 필요가 없다. 하지만 대규모 프로젝트의 경우 각 팀이 담당할 모듈에 대해서는 컨텍스트를 정의할 필요가 있다.

대규모 모델 하나를 작은 것으로 분할하는 데에는 어떠한 규칙도 존재하지 않는다. 상호 관련되어 있고 자연스러운 개념을 형성하는 요소를 하나의 모델에 넣도록 하라.

하나의 모델은 하나의 팀에 할당하기에 적합할 만큼 작아야 한다.

모델의 범위를 정의하는 작업의 기본 개념은, 모델의 범위를 정하고 컨텍스트 간의 경계를 설정한 다음 모델이 통합된 상태를 최대한 유지하도록 하는 것이다.

분할된 컨텍스트는 모듈이 아니다. 분할된 컨텍스트는 발전하는 모델에 담길 논리적 프레임을 제공한다. 모듈들은 모델의 구성 요소들을 조직화하기 위해 사용된다. 따라서 분할된 컨텍스트란 모듈을 포함하는 개념이다.

처음에는 모듈이 분할되며 통일된 모델을 바라보기 어렵다고 생각할 수 있지만 모델을 작은 모듈롤 분할하여 모든 사람은 자신들만의 조각을 가지고 자유롭게 작업할 수 있다. 또한 자신이 맡은 모델이자 객체의 한계 또는 책임과 역할을 알고 있기 때문에 그 경계를 넘지 않는다.

적어보니 OOP와 이어지는 개념이다 동일하다.

책에서 나온 예제와 같이 모델을 모듈로 나누게 되면 확장성이 매우 높아지고 이는 또다른 숨겨진 도메인이 나온다고 볼 수 있다.

지속적인 통합

컨텍스트가 정의되고 나면 이것을 건강하는 상태로 유지시켜야 한다. 일종의 균형잡기이다. 여러 사람들이 동일한 컨텍스트 안에서 일한다면 모델이 단편화될 가능성이 높다. 그렇다고 하나의 시스템을 너무 작은 규모의 여러 컨텍스트로 분할하는 것은 결과적으로 통합과 응집성의 가치가 보장되는 적절한 수준을 지나쳐 버리는 일일 수 있으므로 주의해야 한다.

모델은 초기부터 충분하게 정의될 수 없다. 모델은 생성되고 나면 개발 프로세스 동안 도메인에 대한 새로운 이해를 바탕으로 끊임없이 피드백을 받는다. 이것은 새로운 개념이 모델에 추가되거나 새로운 구성 요소가 코드에 추가될 수 있음을 의미한다. 이것이 지속적인 통합이 왜 필요한지에 대한 답이다.

따라서 코드가 올바르게 구현되도록 보장할 수 있는 프로세스가 존재한다. CI/CD 제목과 같이 모델에 존재하는 개념 통합에 기반하고 있으므로, 테스트를 통해 검증될 수 있도록 이 모델을 구현할 방법을 찾아야 한다.

컨텍스트 맵

기업 규모 프로젝트의 경우 다수의 모델을 가지기 때문에 각 모델은 고유의 분할된 컨텍스트를 가진다. 컨텍스트 맵은 서로 다른 분할된 컨텍스트들과 그들의 관계에 대한 개요를 표현한 문서다. 컨텍스트 맵의 목적은 모든 사람이 이를 공유하고 이해하는 것에 있다.

각 모델의 기능은 전체 시스템의 일부이기 때문에 반드시 통합되어야 한다. 결국 개별 조각은 함께 조립되어서 전체 시스템이 적절하게 동작하는 것이다. 각 컨텍스트가 명확하게 정의되지 않았다면 서로 중복되었을 확률이 높다.

각각의 분할된 컨텍스트는 유비쿼터스 언어의 일부인 고유한 이름이 있어야 한다. 이것은 시스템 전체에 대해 논의할 때 팀끼리 의사소통을 매우 원할하게 한다. 모든 사람은 각 컨텍스트의 범위와 컨텍스트와 코드의 매핑 상태를 알고 있어야 한다.

공유 커널

기능적 통합이 한계에 다다랐을 때, 지속적으로 통합하려는 오버헤드가 너무 크게 느껴질 수 있다. 지속적 통합을 수행할 기술이나 정치적 조직이 없거나 하나의 팀이 너무 커서 통제하기 힘들 때는 정말 그렇다. 따라서 별도로 분할된 컨텍스트를 기반으로 팀을 여럿으로 나눠 조직을 꾸리는 것이다.

초기에 컨텍스트를 나누어 조직을 꾸릴 때 실제로 프로젝트에 할당된 팀장급 팀원들이 모여서 상의를 할까?

모델에서 공유되는 지점이 없다면 팀이 독립적으로 창작하게 되면서 중복적인 일이 생기거나 유비쿼터스 언어의 장점잉 사라질 것이다. 따라서 모델에는 공유지점이 명확하게 나타나야 한다. 여기에는 모델 부분뿐만 아니라, 그 부분과 관련된 코드, 데이터베이스 설계를 포함한다.

기능 시스템을 자주 통합해야 하지만, 팀 내의 지속적인 통합의 빈도보다는 덜하도록 한다. 이렇게 통합하는 도중에 양쪽 팀의 테스트가 수행되어야 한다.

고객 공급자

두 서브시스템 한쪽이 다른 한쪽에 완전히 의존하는 식의 특별한 관게를 맺는 경우도 있다. 두 서브 시스텥의 컨텍스트는 별도로 존재하고, 한쪽의 처리 결과는 다른 쪽에 반영된다. 약간 합성의 관계와 같이 생각하면 될까?

데이터베이스를 직접적으로 사용이 아닌 그것을 관리하는 중간 관리자가 존재하여 의존성으르 줄일 수 있다는 개념으로 이해했다.

순응

고객은 공급자에 대해 매우 의존적인 반면 공급자는 그렇지 않다. 만약 전체를 진행하는 관리 주체가 존재한다면 공급자가 필요한 주의를 기울이고 고객의 요구사항을 듣게 만들 수 있다.

의존적인 관계를 순응이라는 언어 즉 도메인 모델링엥 맞게 설명한 것 같다.

변질 방지 레이어

대부분의 개발에서는 다양한 레거시 애플리케이션 모델을 사용한다. 하지만 해당 애플리케이션이나 라이브러리의 구조는 현재 작업중인 DDD와 접근 방법부터 다르기 때문에 변질 방지 레이어가 필요하다.

이 레이어는 우리 모델과 유사한 개념을 가지고 유사한 동작을 수행한다. 그러나 외부 모델에게 클라이언트 언어가 아닌 외부 언어로 말한다. 즉, 두 개의 서로 다른 도메인과 언어 간에 양방향 번역자의 역할을 한다.

이것의 가장 큰 장점은 클라이언트 모델이 외부로부터 영향을 받는 오염 없이 순수하고 일관된 상태로 남는다는 점이다.

변질 방지 레이어를 구현하는 가장 좋은 방법은 이 레이어를 클라이언트 모델 관점에서의 서비스의 하나로 보는 것이다. 실제 구현은 퍼샤드 패턴으로 구현될 것이며 어뎁터를 필요로 할 것이다.

분할 방식

분할 방법에는 정답은 없다 그 상황에 따라 절충만 있을 뿐 최선의 선택을 해야한다. 끊임없이 코드를 병합하며 그때마다 아무것도 망가뜨리지 않았음을 확인하는 테스트를 수행해야 한다.

분할 방식을 진행하기 앞서 통합된 시스템으로 되돌아가지 않을 것임을 확실히 할 필요가 있다. 독립적으로 개발된 모델들은 통합하기가 매우 어렵다.

오픈 호스트 서비스

두 서브시스템을 통합하려고 할 때 우리는 일반적으로 그들 사이에 번역을 위한 레이어를 만든다. 이 계층은 클라이언트 서브시스템과 우리가 통합하고자 하는 외부 서브시스템 사이에 완충지대의 역할을 한다.

자신의 시스템에 접근할 수 있는 프로토콜을 서비스의 집합으로 정의하라. 내 시스템과 통합될 필요가 있는 사람이라면 누구든지 사용할 수 있도록 프로토콜을 공개하라.

증류

증류란, 혼합체를 구성하고 있는 물질을 분리해 내는 절차다. 이 과정에서 발생하는 몇가지 부산물이 흥미로운 경우가 종종 있다. 도메인의 규모가 큰 경우 여러차례 리팩터링을 거치더라도 여전히 거대한 경우가 있다. 이 시점이 증류가 필요한 시점이다.

도메인의 정수를 표현하는 핵심 도메인을 정의해야 한다. 도메인을 증류 처리할 때 생기는 부산물은 도메인의 다른 파트를 구성하는 일반 서브도메인이다.

도메인을 보고 핵심적인 정수를 파악하는 일이 개발자로서 어려운 일이라는 생각이 든다.

핵심 도메인은 단번에 만들어지지 않는다. 핵심이 한층 명확하게 통합되기 위해서는 정제와 지속적인 리팩터링이 필요하다. 우리는 핵심을 설계의 중심부로 규정하고 그 경계를 명확히 할 필요가 있다.

6장: 오늘날 DDD는 중요하다

  • DDD는 사용자들이 밀접하게 관계되어 있는 도메인 이슈에 집중해야 한다는 원칙을 기반에 둔다.
  • 현대의 시대에서 복잡해진 도메인을 설계하기 위해선 빠질 수 없는 개념이다.
  • 도메인 모델링의 위험
    • 단순한 상태를 유지할 것, 모델로도 코드를 작성해야 한다.
    • 구체적인 시나리오에 초점을 맞출 것, 추상적인 생각은 실제 사례에 연결되어 있어야만 한다.
    • DDD를 모든 것에 적용하려 하지 말 것, 컨텍스트 맵을 그리고 어느 부분에 DDD를 적용하고 어느 부분에 하지 않을지 결정한다. 경계 밖은 신경쓰지 않는다.
    • 실험을 많이 하고 실수를 많이 할 것이라고 예상할 것, 모델링은 창조적인 작업이다.

생각

내 스스로 가장 경계해야 하는 것은 정답이 있을 것이라고 생각하는 것이다. 책 전반에서 시행착오를 많이 겪을 것이고 시작점에서 핵심을 볼 수 없음을 말한다. 과정에서 힌트를 찾아 계속 확장해 나가야 한다.

댓글남기기