ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • DDD. ORM과 투명한 영속성 3부
    JAVA/DDD 2021. 1. 23. 13:49

    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의 적용

Designed by Tistory.