Eternity's Chit-Chat

aeternum.egloos.com



Domain-Driven Design의 적용-4.ORM과 투명한 영속성 3부 Domain-Driven Design

ENTITY의 생명 주기


엔터프라이즈 어플리케이션을 구성하는 도메인 객체의 생명 주기를 바라보는 시각은 크게 두 가지로 나눌 수 있다
.

 

첫 번째는 도메인 객체의 생명주기를 구현 기술에 종속적인 시각으로 바라보는 것이다. 예를 들어 퍼시스턴스 메커니즘으로 JDBC를 직접 사용하는 TRANSACTION SCRIPT 패턴 기반의 어플리케이션을 생각해 보자. 이 경우 도메인 로직은 TRANSACTION SCRIPT 내에 절차적 방식으로 구현되고 도메인 개념들은 getter/setter만을 가지는 Anemic Domain Model로 구성된다. Anemic Domain Model은 도메인 개념을 반영한 클래스명과 속성명을 가지지만 단순히 레이어 간의 데이터 전달을 위해서만 사용되는 가짜 객체를 의미한다. Anemic Domain Model을 적용한 도메인 객체는 상태는 가지지만 행위는 포함하지 않는다. 이 경우 정보를 데이터베이스에 저장하기 위해 속성이 설정된 도메인 객체를 생성하고 JDBC API를 사용하여 데이터베이스에 정보를 저장한 후 도메인 객체를 가비지 컬렉터에 넘겨 소멸시킨다. 만약 데이터베이스에 저장된 데이터가 필요한 경우 JDBC API를 사용하여 쿼리를 실행하고 새로운 도메인 객체를 생성한 후 반환된 결과 셋을 도메인 객체로 변환시킨다. 사용이 종료된 도메인 객체는 가비지 컬렉터에 넘겨져 소멸된다.

 

이 시각은 관계형 데이터베이스와 객제 지향이라는 구현 기술 자체에 지나치게 종속되어 있다. 도메인 객체들의 생명주기는 데이터베이스와의 상호작용 관점에서 파악된다. 도메인 개념의 유일성이나 추적성은 구현 기술에 파묻혀 그 의미를 상실하고 도메인 객체는 단지 데이터베이스 테이블의 구조를 반영한 메모리 저장소로 파악된다. 이 시각을 가진 대부분의 사람들은 도메인 객체의 생명주기를 데이터베이스와의 상호작용을 중심으로 파악하는 경향이 강하다.

 

두 번째 시각은 도메인 객체의 생명주기를 ENTITY 관점에서 바라보는 것이다. 고객이 신규 가입한 경우 시스템에는 새로운 고객 정보를 가지는 새로운 ENTITY가 생성된다. 고객 ENTITY는 잠시 메모리 객체의 형태로 시스템 내에 존재한다. 비즈니스 로직을 처리한 후에 고객 ENTITY는 형태를 바꾸어 데이터베이스 레코드의 형태로 존재한다. 또 다른 비즈니스 로직이 고객 정보를 필요로할 경우 고객 ENTITY는 다시 메모리 객체로 형태를 바꾸어 해당 비즈니스 로직을 처리한다. 최종적으로 고객이 탈퇴를 하는 경우 해당 고객 ENTITY 정보가 소멸된다. , 고객이 탈퇴를 한 이후로는 해당 고객의 정보가 데이터베이스의 형태로든 메모리 객체의 형태로든 시스템 내에 존재하지 않게 된다.

 

이 시각에서 도메인 객체는 단순히 데이터베이스와의 상호작용을 위해 필요한 데이터 저장소가 아니다. 도메인 객체는 ENTITY의 한 형태일 뿐이다. 도메인 객체가 가비지 컬렉터에 의해 소멸되는 것은 도메인 개념의 소멸이 아니라 형태의 변경에 따른 불필요한 메모리의 해제일 뿐이다. 도메인 개념 자체는 사라지지 않는다. 이 시각은 도메인 분석 시에 발견된 연속성과 추적성을 그대로 구현 레벨에 반영하기 위한 발상의 전환을 가져온다.

 

도메인을 시스템 개발의 주도적 위치로 격상시키기 위해서는 도메인 객체를 ENTITY의 개념에서 바라볼 필요가 있다. 그리고 우리의 분석/설계 도구에는 이미 이를 가능하게 해주는 유용한 도구가 존재하고 있다. REPOSITORY가 바로 그것이다.

 

고객이 신규 주문을 입력한 경우 OrderRepository는 새로운 Order ENTITY를 내부적으로 등록하고 관리한다. 저장된 Order가 필요한 경우 클라이언트는 OrderRepository에게 저장된 Order ENTITY를 찾아 달라고 요청하고 OrderRepository는 관리하고 있던 Order ENTITY를 찾아 반환한다. 고객이 마음을 바꿔 주문을 취소한 경우 등록된 Order ENTITY가 더 이상 필요하지 않으므로 OrderRepository에게 해당 Order ENTITY를 제거하라고 요청한다. OrderRepository는 해당 Order ENTITY를 제거한다.

 

이 시나리오에서 Order ENTITY는 새로운 주문 정보가 추가되는 시점에 단 한번 생성된다. 그 이후로는 동일한 Order ENTITY가 동일한 주문 정보를 표현한다. 그리고 최종적으로 해당 주문 정보가 도메인에서 사라지는 시점에 Order ENTITY도 소멸된다. 따라서 Order ENTITY의 생명주기는 실제 도메인 내에서 일어나는 주문의 생명주기와 일치한다. 이 관점에서는 Order ENTITY의 생명주기 중간에 새로운 객체가 생성된다고 생각하지 않는다. 단지 REPOSITORY가 이미 등록되어 있던 Order ENTITY 반환할 뿐이다.

 

REPOSITORY ENTITY의 생성과 소멸 시점 사이를 책임지는 생명주기 관리 객체이다. 우리는 REPOSITORY를 통해 동일한 ENTITY를 얻을 수 있다. , ENTITY의 유일성을 보장할 수 있다. 시스템의 모든 부분이 REPOSITORY를 통해 ENTITY에 접근하기 때문에 변경된 ENTITY 정보는 시스템 전체에 전파된다. REPOSITORY는 도메인 객체들이 메모리 내에서 관리된다는 신기루를 만들어 냄으로써 도메인 모델 전체의 복잡성을 낮춘다. REPOSITORY가 실제로 ENTITY를 메모리에서 관리하는지 데이터베이스에서 관리하는지 또는 XML 파일 내에 관리하는 지는 도메인 레이어와는 상관이 없다. REPOSITORY는 내부 구현을 캡슐화하면서 도메인 레이어에 객체 지향적인 컬렉션 관리 인터페이스를 제공한다.

 

지금까지 개발해 온 주문 시스템을 떠올려 보라. OrderRepository는 메모리 내에 컬렉션을 관리하는 Registrar를 사용해서 Order ENTITY를 관리한다. 외부의 도메인 객체는 OrderRepository가 이전에 등록한 동일한 Order ENTITY를 메모리에 계속 저장하고 있다고 생각한다. 따라서 도메인 객체는 OrderRepository에게 해당 Order ENTITY를 찾아 달라고 요구하고 반환된 Order ENTITY는 이전에 등록된 바로 그 Order ENTITY라고 생각한다.

 

이제 OrderRepository가 메모리가 아닌 데이터베이스에 Order ENTITY를 저장하도록 내부 구현을 변경하는 경우를 생각해 보자. 다행히도 이전에 리팩토링을 통해 인터페이스와 구현 클래스를 분리하고 의존성 주입을 제공하는 Spring 프레임워크를 도입한 덕에 OrderRepository의 내부 구현이 변경되었더라도 도메인 레이어에는 영향을 미치지 않게 되었다. OrderRepository는 이제 내부적으로 Registrar 대신 데이터베이스에 접근하기 위한 다른 인프라 스트럭처를 사용하는 클래스를 의존 주입하도록 수정되겠지만 이 사실은 인터페이스에 의해 외부로 새나가지 않는다. 따라서 도메인 객체들은 OrderRepository가 여전히 메모리 컬렉션 내에 동일한 Order ENTITY를 관리하고 있다고 가정할 수 있다. OrderRepository가 내부적으로 저장 메커니즘을 데이터베이스에서 XML 파일로 변경한다고 해도 메모리 컬렉션 관점을 수정할 필요는 없다. 이것이 인터페이스를 통한 캡슐화의 힘이며 도메인 모델에 REPOSITORY를 도입한 결과로 얻게 되는 추상화의 장점이다.

 

그러나 OrderRepository가 데이터베이스를 사용할 경우 한 가지 문제가 발생한다. 지금까지 REFERENCE OBJECT의 동일성을 테스트하기 위해 사용하는 “==” 연산자가 메모리 주소를 비교한다는 것이다. REPOSITORY가 내부적으로 저장소로써 데이터베이스를 사용한다면 ENTITY를 룩업할 때마다 새로운 객체가 생성되어 반환될 것이다. 결국 이들은 동일한 ENTITY지만 동일한 메모리 주소를 가지지 않기 때문에 식별자를 비교하는 “==” 테스트가 실패하게 될 것이다. 결국 “==” 테스트의 실패로 인해 지금까지 유지해왔던 도메인 객체의 추적성이 한 순간에 붕괴되는 결과를 낳고 만다. 따라서 우리는 객체 지향 시스템이 제공하는 REFERENCE OBJECT의 식별자를 그대로 사용할 수 없다. 다양한 형태로 옷을 갈아 입는 ENTITY의 특성을 고려하는 순간 새로운 식별자의 개념이 필요해 진다.


핑백

  • Domain-Driven Design | Jongmin Kim's Blog 2014-09-02 01:18:17 #

    ... /03/25 Domain-Driven Design의 적용-4.ORM과 투명한 영속성 4부 [4] 2009/02/27 Domain-Driven Design의 적용-4.ORM과 투명한 영속성 3부  2009/02/23 Domain-Driven Design의 적용-4.ORM과 투명한 영속성 2부 2009/0 ... more