잡동사니

도메인 주도 설계 2부 독서 후기 (feat. DDD) 본문

IT Paradigm/DDD

도메인 주도 설계 2부 독서 후기 (feat. DDD)

yeTi 2023. 8. 11. 14:30

안녕하세요. yeTi입니다.
오늘은 도메인 주도 설계 의 2부의 읽은 후기를 기록하려고 합니다.

도메인을 격리하자

도메인의 격리를 보편적으로 할 수 있는 방식은 LAYERED ARCHITECTURE 입니다.

객체지향 프로그램을 개발하면서 가장 쉬운 방법으로 도메인 코드와 도메인과 관련없는 코드를 혼재하는 것입니다.

그러나 이렇게 되면 도메인과 관련된 코드를 확인하고 추론하기가 힘들어지고 모델 주도적인 객체를 구현하는 것이 비현실적인 이야기가 돼버립니다.

객체지향의 5대원칙 중 하나인 관심사의 분리가 중요한 요소로 언급되는 것처럼 소프트웨어 시스템을 분리하는 방법 중 보편적으로 사용하는 것이 LAYERED ARCHITECTURE 입니다.

그 외의 다수의 패턴들이 풀고자 했던 문제 중 하나는 느슨한 결합을 위해 계층을 분리하고 각 계층을 연결하는 것이었습니다.


그리고 모델 주도 설계의 적합성을 제시하기 위해 안티 패턴을 하나 설명하는데요.

지금까지 현업에서 보던 설계 패턴을 호칭할 수 있는 단어가 생겨 반가웠습니다.

Smart UI

Smart UI 가 정의하는 설계를 다음과 같습니다.

모든 업무 로직을 사용자 인터페이스에 넣자. 애플리케이션을 작은 기능으로 잘게 나누고, 나눈 기능을 각기 분리된 사용자 인터페이스로 구현해서 업무 규칙을 분리된 사용자 인터페이스 에 들어가게 하자. 관계형 데이터베이스를 데이터의 공유 저장소로 사용하고 이용 가능한 최대한 자동화된 UI 구축 도구와 시각적인 프로그래밍 도구를 사용하자.

이 설명을 보고 현업에서 UI 에 직접적으로 쿼리로 대응하는 설계 패턴이 떠올랐습니다.

그리고 Smart UI 의 장점은 아래와 같이 설명합니다.

초기 개발의 경우 생산성이 높고 효과가 즉각적으로 나타나며 다소 능력이 부족한 개발자도 약간의 교육으로 이러한 방식으로 업무를 진행할 수 있고 요구사항 분석 단계에서 결함이 발생하더라도 사용자에게 프로토타입을 배포한 후 요구에 맞게 제품을 변경해서 문제를 해결할 수 있는 장점이 있다.
그리고 애플리케이션이 서로 분리되므로 규모가 작은 모듈의 납기 일정을 비교적 정확하게 계획할 수 있고 부가적이고 간단한 작업만으로도 시스템을 확장하기가 수월할 수 있다. 관계형 데이터베이스와 잘 어울리고 데이터 수준의 통합이 가능하다. 애플리케이션을 인도했을 때 유지보수 프로그래머가 이해하지 못하는 부분을 신속하게 재작업할 수 있다. 이는 변경의 효과가 특정 UI에 국한되기 때문이다.

반면에 아래와 같은 단점을 가진다고 말합니다.

데이터베이스를 이용하는 방식 말고는 여러 애플리케이션을 통합하기가 수월하지 않다. 행위를 재사용하지 않으며 업무 문제에 대한 추상화가 이뤄지지 않는다. 업무 규칙이 적용되는 연산마다 업무 규칙이 중복된다. 신속한 프로토타입 작성과 반복주기가 SMART UI가 지닌 태생적인 한계에 도달하게 된다. 이는 추상화의 부재로 리팩터링의 여지가 제한되기 때문이다. 복잡성에 금방 압도되어 애플리케이션의 성장 경로가 순전히 부가적인 단순 응용으로만 향한다. 우아한 방법으로 더욱 풍부한 행위를 갖출 수 있는 방법은 없다.

현업에서 업무 도메인 규칙이 분산되어 특정 규칙이 추가되거나 변경되었을 경우 다수의 쿼리를 수정하는 행동이 생각이 났고 개발자의 인지 능력이 벗어난 범위에서는 버그의 형태로 업무 규칙이 상이하게 동작하는 경우를 봐왔습니다.

이는 패턴 선택의 적합성의 문제라는 시야를 얻을 수 있었고,

그 동안 Smart UI 는 안 좋은 패턴이라는 생각을 가지고 있었지만 개발 목표나 팀의 상황에 따라 적합한 선택이 될 수 있다는 시야를 가지게 되었습니다.

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

모델 을 구성하는 기본 요소에는 연관관계 , 엔티티 , 값객체 , 서비스 , 모듈 이 있습니다.


모델링과 실제 구현 간의 상호작용은 여러 객체 간의 연관관계에서 특히 까다롭습니다.

추상화란 불필요한 정보를 제거하여 정보의 복잡성을 제거하는 것인데, 연관관계도 현실세계를 그대로 반영하면 복잡도가 높습니다.

그렇기 때문에 방향성을 부여하고 다중성을 줄이고 불필요한 것을 제거하는 추상화가 필요합니다.


엔티티의 설명을 읽고 JPA에서 표현하는 엔티티와 영속성 컨텍스트의 의미를 짐작할 수 있게 됐습니다.

엔티티는 속성의 동일성보다는 생명주기 내내 이어지는 연속성을 가지는 객체를 의미하고 영속성 컨텍스트는 이러한 엔티티의 연속성을 보장하기 위해 만들어진 공간을 의미합니다.

그렇다면 엔티티에서 동치성은 속성의 동일성이 아닌 식별자의 동일성으로 판단하는게 맞아 보입니다.

이어서 JPA에서 영속하다(persist)는 생명주기를 관리하겠다는 것이고 분리하다(detach)는 생명주기를 관리하지 않겠다는 것입니다.

이렇게 정리하니 그 동안 JPA의 영속상태를 Insert, Delete, Update 로 보던 것보다 이해가 잘 되는거 같습니다.

그런데 분리하다(detach)로 이미 엔티티의 생명주기를 끝내겠다는 표현을 했는데 remove() 는 왜 별도로 존재할까요?

차차 알아갈 숙제입니다.


개념적 식별성을 갖지 않으면서 도메인의 서술적인 측면을 나타내는 객체가 VO입니다.

VO 객체가 불변적이어야 하는 이유는 변경의 불확실성을 제거하여 안전하게 객체를 공유하기 위해서입니다.

VO 객체 특성에 의한 Flyweight 패턴과 같이 성능 최적화도 가능합니다.


서비스라는 이름은 다른 객체와의 관계를 강조합니다.

조영호님의 객체지향의 사실과 오해를 보면서 나름대로 서비스는 객체들이 활동하는 목적을 표현하는 것이라고 생각했는데 방향성이 틀리지 않았다는 느낌을 받았습니다.

서비스는 객체들이 활동하는 목적이라고 표현했는데 이는 도메인 서비스에 해당합니다.

외부 통신을 위한 인프라스트럭처 서비스도 구분하는게 좋아 보이고.

도메인과 상관없이 서비스를 유지하기 위한 역할을 응용 서비스에 할당하도록 해야겠습니다.


패키지는 모듈로써 개념의 구분에 따라 나눠져야 하고 한 사람이 한 번에 생각할 수 있는 양으로 구성되어야 합니다.

따라서 Module도 하나의 의사소통 매커니즘으로 인식해야 합니다.

앞으로 IDE 가 해주는 자동 완성의 import 를 모듈간의 의존성을 확인하는 장치로 활용해보려고 합니다.


프레임워크가 제공하는 관례를 무조건적으로 따르는 것은 설계를 더욱 복잡하게 만들 수 있습니다.

중요한것은 도메인 모델러와 개발자가 인지할 수 있는 개념으로 메타포를 사용해야하는데 메타포 자체가 복잡해지면 그것을 이해하기 위해 드는 비용 때문에 비용을 사용해야하는 모델을 다루기가 어려워진다는 것입니다.

객체지향 패러다임이 지배적인 이유는 패러다임의 성숙도와 함께 널리 보급되어 사용되고 있기 때문입니다.

널리 보급되어 사용되다보니 성숙한 인프라스트럭쳐와 도구도 지원되어 많은 기술적인 위험을 제거해줍니다.

그리고 개발자 커뮤니티와 설계 문화 자체도 성숙해졌기 때문에 숙련된 개발자들을 찾는것도 다른 패러다임에 비해서 수월합니다.


그러나 도메인 모델이 반드시 객체 모델이어야 하는것은 아닙니다. 만일 상호의존성이 적다면 다른 패러다임의 하위시스템을 캡슐화할 수도 있습니다.


그럼에도 개발자가 소프트웨어에 포함된 응집력 있는 모델을 분명하게 볼 수 없다면, MODEL-DRIVEN DESIGN이 사라질 수 있습니다.

다양한 패러다임이 혼재할 때 객체 패러다임으로 캡슐화를 한다면 부자연스러운 부분이 생길 수 있기 때문에 각각의 패러다임의 강점을 살릴때 객체의 강점과 약점을 보충할 수 있습니다.

결국 모델 주도 설계가 객체지향적일 필요는 없지만 표현력이 풍부한 모델 구성물의 구현에는 의존합니다.

객체가 아닌 요소를 객체지향 시스템에 혼합하기 위해서는 다음의 4가지 법칙을 고려해보면 좋습니다.

• 구현 패러다임을 도메인에 억지로 맞추지 말자. 도메인에 관한 사고방식은 반드시 하나만 있는 것이 아니므로. 패러다임에 어울리는 모델 개념을 찾자.
• 유비쿼터스 언어에 의지하자. 각종 도구가 서로 엄밀한 관계에 있지 않더라도 언어를 매우 일관되게 사용하면 설계의 각 부분이 분화되는 것을 방지할 수 있다.
• UML에 심취하지 말자. 간혹 사람들이 UML 다이어그램과 같은 도구에 집착해서 그리기 쉬운 방향으로 모델을 왜곡하곤 한다. 때론 간단한 문장으로 설명을 써놓는 편이 객체를 바라보는 특정 관점을 나타내고자 도식 방법을 완곡하게 바꾸는 것보다 낫다.
• 회의적이어야 한다. 도구가 실제로 제 몫을 하고 있는가? 단순히 어떤 규칙이 있다고 해서 반드시 값비싼 룰 엔진이 필요한 것은 아니다. 아마도 약간은 덜 깔끔하겠지만 규칙은 객체로 표현할 수 있으며, 복합적인 패러다임은 문제를 터무니없이 복잡하게 만든다.

도메인 객체의 생명주기

#Aggregate, #Factory, #Repository 를 활용하면 체계적이고 의미 있는 단위로 조작할 수 있습니다.


Aggregate는 변경의 일관성을 보장하기 위해 사용하는 것입니다.

Aggregate 의 가치는 생명주기의 전 단계에서 불변식이 유지돼야 할 범위를 표시해주는것에 있습니다.


Factory는 단순하게 생성자를 대체하는 표현이 아니라 aggregate의 지식을 캡슐화 하는 것입니다.

우리가 객체의 변경 단위를 인지하기 위해서 aggregate로 표현하는 것처럼 클라이언트가 관심없는 내부 구조를 숨기고 factory를 통하여 aggregate를 알 수 있도록 하는것이 좋습니다.

이러한 FACTORY는 기본적으로 해당 FACTORY에서 만들어내는 객체와 매우 강하게 결합돼 있으므로 FACTORY는 자신의 생성물과 가장 밀접한 관계에 있는 객체에 있어야 합니다.

그러나 구상 구현체나 생성 과정의 복잡성과 같은 것을 감춰야 한다면 비록 자연스러운 곳으로 보이지는 않더라도 전용 FACTORY 객체나 SERVICE를 만드는것이 좋습니다.

우리가 생성자와 factory 의 선택의 문제가 있을 때 대부분의 경우는 factory 가 좋습니다. 왜냐하면 Aggregate의 캡슐화와 표현력을 높여주기 때문이다.

그러나 다형성을 활용하지 않는 간단한 객체는 생성자로 표현하는것이 복잡도를 줄여주기도 합니다.

표현력의 측면에서 Entity factory는 필수 속성만 받아들이는 경향이 있지만 VO factory는 생성물에 대한 풍부한 설명을 곁들여야 합니다.

인스턴스 생성을 위한 접근 지점으로 Factory를 도입하여 해당 모델을 나타내는 객체를 뚜렷하게 드러내는 데 일조하는 도메인 설계의 일부로 볼 수 있도록 표현력을 줄 수 있습니다.


DDD를 읽으며 JPA의 장점 중 하나인

도메인 모델에 집중할 수 있게 해준다.

가 어떠한 근거를 기반으로 나왔는지 짐작할 수 있게 해줍니다.

Repository를 통하여 개발자는 Aggregate과 Entity를 기반으로 도메인 규칙을 이해하는데 도움을 받을 수 있습니다.

JPA를 사용함에 있어서 그 특징을 잘 모르고 사용한다면 문제를 만들 수 있기 때문에 캡슐화된 행위를 활용하는 것에 내포된 의미를 알아야 합니다.

객체를 생성한다는 관점에서는 레포지토리와 팩토리가 같은 개념으로 다가올 수 있습니다.

그러나 캡슐화의 목적이 다릅니다. 팩토리는 aggregate 의 내부 관계를 캡슐화하는 것이 목적인 반면 레포지토리는 데이터베이스를 켑슐화하는 것이 목적입니다.

분석 모델-객체 모델-관계형 데이터 모델 이 갈라지기 시작한다면 투명성이 빠르게 사라질 수도 있습니다.

그럴때 중심을 잡아 개념적인 통일성을 가져갈 수 있는것이 UBIQUITOUS LANGUAGE 입니다.

관계형 데이터 모델을 객체 모델의 저장소로 사용한다면 객체 시스템 외부의 프로세스는 이러한 객체 저장소에 접근해서는 안 됩니다.

그러나 현실에서 관계형 데이터 모델은 객체 모델을 사용하지 않는 시스템도 사용할 수 있는 영역입니다.

따라서 관계형 데이터 모델이 꼭 객체 모델을 따라야할 필요는 없습니다. 오히려 객체 모델과 관계형 데이터 모델을 분리하면 보다 자연스러운 관계형 데이터 모델을 가질 수도 있습니다.

마무리

이렇게 2부인 모델 주도 설계의 기본 요소들을 알아봤습니다.

설계라는 행위를 하면서 관습적으로 사용하던 단어들이 가지는 의미를 알수 있어 뜻깊은 시간이었고, 앞으로 설계를 해나감에 있어 좀 더 명확하게 사용할 수 있어 개발활동에 도움이 될것이라고 기대합니다.

특히 그 동안 개념적으로 잘 와닿지 않던 aggregate 을 명확하게 알 수 있게 되어 엔티티의 묶음을 설정하여 개념적인 집합체를 만드는데 도움이 될거 같습니다.

마지막으로 JPA에서 강조하는

도메인 모델에 집중할 수 있게 해준다.

의 의미가 실무적으로 잘 와닿지 않았었는데, 도메인의 표현과 응집을 위한 개념을 이미 내포하고 있다는 느낌이 들었고,

영속성 컨텍스트에만 관심을 가진다면 도메인 모델에 집중하는 것이 곧 서비스의 개발로 이어진다는 느낌을 받았습니다.

개념적 경계를 어떠한 형태로 담아낼 수 있는지를 알 수 있는 유익한 시간이었습니다.

지난 기록

Comments