도메인 모델은 무엇일까요? 단순히 도메인 모델을 설계하면 끝일까요? 피해야할 도메인 모델의 안티 패턴을 알아봅니다.
이건 꽤 오랫동안 존재해왔던 안티 패턴 중 하나이지만, 최근 더 특출난 모습을 보이는 것 같습니다. 에릭 에반스와 이에 대해서 잠시 얘기를 나눴는데, 둘다 이게 보다 유명해졌다는 걸 알아차렸습니다. 적절한 도메인 모델의 부스터 역할을 하기에 좋은 것은 아닙니다.
빈약(Anemic)한 도메인 모델의 기초 증세는 처음엔 진짜처럼 그럴듯해 보인다는 겁니다. 도메인 영역에서 사용하는 명사들로 네이밍된 여러 객체들이 있고, 실제 도메인처럼 풍부(Rich)하게 관계와 구조를 맺고 있죠. (빈약함의 여부는) 객체들의 동작(Behavior)들을 보면 알 수 있으며, 이들은 동작들이 거의 없고 단지 getter&setter를 담는 그릇에 지나지 않습니다. 이따금 실제로 이런 객체들은 도메인 객체와 도메인 로직을 넣지 말라는 디자인 규칙과 함께 제공됩니다. 대신 도매인 로직을 모두 담고 있는 서비스 객체가 있으며, 결과를 도출하는 계산과 도메인 모델을 수정하는 로직들을 수행합니다. 이러한 서비스 객체들은 도메인 객체보다 높은 위치에 있으며, 도메인 모델을 데이터를 실어나르는 용도로 사용하죠.
이러한 안티패턴의 무서운 점은 프로세스와 데이터를 결합하는 OOP의 기본 의도와는 전혀 반대로 사용된다는 점입니다. 빈약한 도메인 모델은 진짜 그저 절차적인 디자인이며, 에릭과 나와 같은 사람들이 초창기부터 싸워왔던 딱 그런 것들입니다. 무엇보다 나쁜 건 사람들이 빈약한 도메인 모델이 진짜 객체라고 생각한다는 것이며, (빈약한 도메인 모델은) OOP가 말하고자 하는 것을 완벽하게 놓치고 있습니다.
순수한 객체 지향은 이제 꽤 잘 되고 있지만, 이러한 anemia(빈혈, 빈약함)와는 반대로 좀더 기초적인 인자들이 필요하다는 것을 자각하고 있습니다. 본질적으로 이런 빈약한 도메인 모델의 문제는 도메인 모델의 이점을 하나도 가져오지 못한 채 (도메인 모델에 필요한) 모든 비용을 부담한다는 것입니다. 주요 비용은 데이터베이스 매핑 시의 어색함이며, 이는 일반적으로 O/R 매핑의 전체 레이어에서 그 결과를 초래합니다. 복잡한 로직을 구성하기 위해 강력한 객체 지향 기술들을 사용하면 가치가 있지만, 이를 서비스 레이어로 끌어올리게 되면 본질적으로는 트랜잭션 스트립트 패턴으로 끝나게 됩니다. 그리고 이건 도메인 모델이 가져올 수 있는 이점을 모두 잃는다는 것을 의미합니다. P of EEA(Patterns of Enterprise Application Architecture)에서 얘기했던 것처럼, 도메인 모델은 항상 가장 적절한 기술이 되진 않습니다.
또한 ‘도메인 모델에 동작들을 집어넣는 것’과 ‘퍼시스턴스, 프레젠테이션 레이어의 책임같은 것들을 도메인 로직으로부터 분리하기 위해 사용되는 레이어링같은 solid한 접근법’이 서로 모순되는 것이 아니라는 것은 강조해야할 가치가 있습니다. 반드시 도메인 모델 안에 있어야 하는 것(유효성 검사, 계산, 비즈니스 규칙)들이 도메인 로직입니다. 물론 원하는 대로 불러도 상관없습니다. (도메인 객체 안에 데이터 소스 혹은 프레젠테이션 로직을 넣기 위한 인자를 만들어야 한다는 경우가 있지만, 빈약한 객체에 대한 제 관점과는 독립적입니다.)
이러한 모든 혼란의 원인 중 하나는 많은 객체 지향 전문가들이 Service Layer를 생성하기 위해 도메인 모델의 위에 프로시저 서비스의 레이어를 넣는 것을 추천한 것이었습니다. 그러나 이것은 도메인 모델을 동작의 무효로 만들려는 주장이 아니었으며, 실제로 서비스 계층 옹호자들은 행동적으로 풍부한 도메인 모델과 함께 서비스 계층을 사용합니다.
에릭 에반스의 Domain Driven Desing은 이러한 레이어들에 대해 아래와 같이 이야기합니다.
*Application Layer (에릭 에반스가 부르는 Service Layer): 소프트웨어가 하기로 예정되어있는 작업들을 정의하고 표현이 풍부한 도메인 오브젝트들이 우리의 문제들을 해결하도록 지시합니다. 이 레이어들가 책임지는 작업들은 비즈니스에 의미 있는 것들이거나, 다른 시스템의 애플리케이션 레이어와 상호작용하기 위한 필수적인 것들입니다. 이 레이어는 얇게 유지되어야 합니다. 비즈니스 규칙이나 지식을 포함하지 않아야 하며, 작업들을 조정하고 다음 아래 계층(하단 Domain Layer)에 있는 도메인 객체들의 협업에 작업을 위임할 뿐입니다. 프로그램이나 사용자를 위한 작업의 진행도(진전)을 반영하는 상태는 가질 수 있지만, 비즈니스 상황을 반영하는 상태를 가져서는 안됩니다.
Domain Layer (or Model Layer): 비즈니스 개념을 표현하고 비즈니스 상황과 규칙에 대한 정보를 표현하는 책임을 가집니다. 비록 저장하는 기술적인 책임이 인프라스트럭처에 있더라도,비즈니스 상황을 반영하는 상태를 제어하고 사용하는 곳은 도메인 레이어입니다. 이 레이어는 비즈니스 소프트웨어의 심장입니다.*
여기서의 핵심은 모든 핵심 로직은 도메인 레이어에 위치해 있어야 하며, 서비스 레이어는 얇아야 한다는 것입니다. 그는 이런 포인트를 그의 서비스 패턴에서 다시 이야기합니다.
이제, 우리가 하는 일반적인 실수는 적절한 객체에 적절한 동작을 fitting(메서드를 만들어주는) 것을 너무 쉽게 포기하고 점차 절차적 프로그래밍 쪽으로 미끄러지게 되는 것입니다.
전 왜 이런 안티 패턴들이 흔한지 모르겠습니다. 예상하건데 아마 적절한 도메인 모델을 사용하지 않는 많은 사람들 때문일거에요, 데이터 쪽에서 온 사람들은 더욱 그렇고요. POJO 도메인 모델들을 선호하게 만든 원인 중 하나인 J2EE의 엔티티 빈들같은 기술들은 우리가 도메인 모델을 사용할 수 밖에 없도록 합니다(너무 별로여서).
일반적으로 서비스 레이어에서 많은 객체의 동작들을 찾고 있다면, 도메인 모델의 이점을 빼앗기게 될 가능성이 점점 높아지게 될겁니다. 만약 서비스 레이어에 비즈니스 로직들이 있다면 스스로를 스스로가 장님으로 만드는 겁니다.