도메인의 격리
Last updated
Last updated
생각보다 도메인에서 발생하는 문제는 시스템에서 작은 요소인 경우가 많다. 시스템에서 도메인과 관련이 적은 기능으로부터 도메인을 분리할 필요가 있으며, 다른 개념과 혼동하는 문제를 방지할 수 있습니다
소프트웨어 프로그램에는 갖가지 작업을 수행하는 설계와 코드가 포함되어 있다.
객체지향 프로그램에서는 종종 UI와 데이터베이스나 다른 보조적인 유틸기능을 비즈니스 객체안에 작성하기도 한다. 부가적인 업무로직은 UI나 데이터베이스에 들어가기도 하는데, 가장 편하고 빠르게 동작하게하는 방식이기 때문이다.
하지만 이런 도메인과 관련되지 않은 코드가 상당량 존재하게되면 도메인에 관련된 코드를 추론하고 이해하기 어려워지게된다. 또한 UI의 변경이 도메인&비즈니스 로직의 변경으로 이어지는 경우가 생기기도 한다.
복잡한 작업을 하는 프로그램일 수록 관심사
를 분리할 필요가 생긴다.
특정 기능을 하는, 구성요소를 서로 다른 영역(= 관심사)로 분리한다는 것을 의미한다
이는 기능적, 도메인적 분리가 될수있으며 각 관심사는 메시지를 통해 주고받으며, 각 관심사는 독립적으로 작동해야 한다
종류 | 설명 |
---|---|
UI | 정보를 보여주고 명령을 해석하는 일을 한다 다른 시스템의 접근도 포함된다 |
응용 계층 | 작업을 조정하고 아래계층의 기능을 pecade로 묶어주는 역할을한다 |
도메인 | 업무 개념과 규칙을 표현해준다. 업무 상황을 반영하는 상태를 제어하고, 사용한다 |
인프라스트럭 | 기술적 기능을 제공한다 데이터베이스, 영속화, 메시지 전송 API콜등이 있다 |
복잡한 코드를 여러 계층으로 나눠 계층별 응집도를 높이고 결합을 느슨하게 유지하게한다.
계층별 설계를 해도 각 계층은 서로 연결되는데, 이때 분리도를 잃지 않기위해 각종 패턴이 존재한다. 각 계층은 한 방향으로만 의존성을 두어 느슨하게 결합되게 하며, 상위와 소통하는 경우 콜백이나 관찰자 패턴을 활용하는 것이 용이하다.
응용 계층과 도메인 계층에 UI가 연결되는 MVC패턴은 스프링, NEST및 많은 프레임워크에서 채용할 정도로 활용성이 좋다
계층화의 동작 방식과 각계층이 느슨하게 결합되는 방식은 상당히 직관적이다. 하지만 일부 기술적&구조적인 문제는 조금더 침습적 형태의 인프라 스트럭쳐가 필요하다.
특정 프레임워크는 특수한 방식으로 구현되야 하는 경우가 있는데, 스프링 시큐리티와 같이 다른 프레임워크 클래스의 하위클래스가 되어야 하는 경우가 있다. 이처럼 프레임워크가 방해가 되는 경우도 존재한다.
도메인 계층은 소프트웨어에서 필요한 개념과 설계요소에 관계되어 있는 모든 것들을 명시하며, 업무 로직에 설계와 구현으로 구성된다.
특정 도메인 측면 중에는 객체보다는 행동이나 연산으로 명확하게 표현되는 것도 있다.
모델 내에 탐색 가능한 연관 관계와 그와 같은 매커니즘은 소프트웨어에도 존재한다. 모델링과 실제 구현은 여러 객체의 연관 관계에서 까다롭다.
도메인을 이해하다 보면 굉장히 한정적인 관계가 있다는것을 깨닫곤 하는데, 더 근원적인 개념과 덜 근원적인 개념의 관계가 있다는것을 알게된다.
해당 객체들은 개념적으로 식별성을 가져야한다
객체를 모델링할 때, 종종 객체의 속성에 집중하곤 하는데 ENTITY의 근본적인 개념은 객체의 생명주기 내내 이어지는 연속성이며, 여러 형태를 거쳐 전달된다.
종종 어떤 객체는 속성이 같지 않아도 동일한 것으로 표현해야 되는 경우도 있고 같은 속성이지만 다른 것으로 표현해야 되는 경우도 있다.
객체가 속성보단 식별성으로 구분되는 경우, 모델 내에서 이 객체를 주된 정의로 삼아야 한다. 클래스의 정의는 단순하게 하고 생명주기와 식별성에 집중해야한다.
객체를 모델링할 떄 속성과 행위에 대해 생각해 보는것은 아주 중요하다 그러나 ENTITY의 가장 기본적인 책임은 객체의 행위가 명확하고 예측가능하고, 연속성을 확립하게 하는것이다.
각 ENTITY에는 다른 객체와 구분해줄 식별성을 만들어낼 수단이 있어야한다. 식별에 사용되는 속성은 시스템내에서 유일해야한다.
객체의 속성으로 구성될 실질적인 고유키가 없다면 인스턴스에 유일한 기호로 ID를 생성한다 이는 시스템 내에서 유일 해야하며, 다른 시스템간의 상호 작용에도 사용해야 되는 경우도 고려해야한다
대개 모델에서 눈에 잘띄는 객체는 ENTITY지만 모든 객체에 식별성을 할당하려 고려하는 것은 자연스러운 현상이다. 인위적으로 만들어 진 식별성을 도입할 경우 오히려 불편하고, 관리가 까다로워진다.
개념적 식별성을 갖디 않으면서 서술적 측면을 나타내는 객체를 VO라 한다. 이는 해당 요소(값)이 무엇인지에 초점을 지닌다는 의미를 내포한다.
VO는 메시지의 매개변수로 사용되기도 하며, 일시적으로 만들어지고 연산후 폐기되기도 한다
객체를 안전하게 공유하려면 객체가 불변성을 가져야한다. 객체의 공유는 공간은 절약하거나, 객체의 수를 줄이는데에 중점을 둬야한다. 복사의 경우는 객체의 수가 굉장히 많아져 시스템이 무거워 질 가능성이 존재한다.
두 VO간의 양방향 연관관계는 논리적으로 타당하지 않다. 식별성이 없이 서로를 가리키는 것은 아무 의미가 없다
시스템 설계자는 VO의 양방향 연관관계를 완전히 제거하도록 노력해야 한다.
도메인 개념 가운데 객체엔 어울리지 않는것이 있다 필요한 도메인 기능을 ENTITY, VALUE에 맡기게 하면 객체의 정의가 왜곡되거나, 무의미하고 인위적인 객체가 추가될 것이다
SERVICE는 모델에서 독립적인 인터페이스로 제공되는 연산으로써, 상태를 캡슐화하지 않는다. 다른 객체와의 관계를 강조한다.
도메인의 중대한 프로세스나 변환과정이 ENTITY, VALUE의 책임이 아니라면, 독립 인터페이스인 SERVICE로 선언되는 독립 인터페이스로 추가하고, 상태를 갖지 않도록 해야한다.
SERVICE는 도메인 계층에서만 이용되는 것이 아니다.
각 계층의 SERVICE들을 구분하고 책임을 나누는데 주의를 기울여야 한다.
ENTITY와 VALUE OBJECT로부터 클라이언트를 분리하고, 도메인 계층의 인터페이스 구성 단위를 제어하는 수단
클라이언트 제어와 융통성보다는 인터페이스의 단순함이 더 중요 대형 시스템에서 분산 시스템에서 컴포넌트를 패키지화는데 유용한 중간 구성 단위의 기능을 제공
모듈로 쪼개지는 것은 코드가 아닌 개념이다.
사람이 한 번에 생각해 낼 수 있는 양에는 한계가 있으며(따라서 결합도는 낮춰야 한다), 일관성 없는 단편적인 생각은 이해하기 어렵다(따라서 응집도는 높혀야 한다)
분할되는 객체의 의미에 따라 모듈을 선택해야 하며, 한 모듈 안에 든 개념은 한 번에 모아서 생각해야 하는 단위와 같다.
프레임워크의 분할 관례 탓에 도메인 객체 본연의 응집도가 불분명해진다. 프레임워크 분할에 매몰되어 모델을 도메인 요구에 맞게 조정하기 어려워진다.
개념은 단순하지만, 도메인 지식을 포착할 만큼 풍부하다.
모델을 소프트웨어에서 표현하게 해주는 도구가 지원된다.
개발자 커뮤니티와 설계 문화가 성숙되어있다.
도메인 모델이 반드시 객체 모델이어야하는 것은 아니다.
패러다임을 혼합해서 각 개념들이 가장 잘 어울리는 형식으로 모델링해야 한다.
패러다임이 혼재할 때 MODEL-DRIVEN DESIGN 고수하기
룰 엔진같은 로직 패러다임은 객체의 약점을 보충해준다.
하지만 데이터와 규칙 간의 연관관계가 단절되기 쉽다.
두 가지 구현 패러다임에서 모두 작용할 수 있는 하나의 모델을 찾는 것이 중요하다.
구현 패러다임을 도메인에 억지로 맞추지 않는다. 도메인에 관한 사고방식은 반드시 하나만 있는 것이 아니다.
유비쿼터스 언어에 의지한다. 일관된 언어를 사용하면 설계의 각 부분이 분화되는 것을 방지할 수 있다.
UML에 심취하지 않는다. 그리기 쉬운 방향으로 모델이 왜곡되지 않도록 해야한다.
회의적이어야 한다. 도구가 실제로 제 몫을 하고 있는가? 규칙이 있다고 반드시 룰 엔진이 필요한 것은 아니다.