-
도메인 주도 설계의 적용 - 3. 의존성 주입과 관점 지향 프로그래밍 2부[옛날 글들] 도메인 주도 설계 2024. 4. 16. 09:23728x90
이전글 : 도메인 주도 설계의 적용 - 3. 의존성 주입과 관점 지향 프로그래밍 1부
이 글은 제가 2008년 6월부터 10월까지 5개월간 마이크로소프트웨어에 연재했던 "도메인 주도 설계의 적용"이라는 원고의 원글입니다. 잡지에 맞추어 편집을 하는 과정에서 지면 상의 제약으로 인해 수정되거나 삭제된 부분이 있어 제 블로그에 원글을 올립니다. 도메인 주도 설계(Domain-Driven Design)에 관심 있는 분들에게 도움이 되었으면 합니다.
객체 그리고 영속성(Persistence)
모든 객체는 생성자가 호출되는 시점에 생성된다. 값 객체의 수명은 짧다. 생성된 후 잠시 다른 참조 객체 또는 값 객체에 의해 사용되다가 다른 값 객체로 대체된다. 갈 길을 잃은 값 객체는 가비지 컬렉터에게 넘겨진 후 조용히 생을 마감한다. 값 객체에 비해 참조 객체의 수명은 상대적으로 길다. 참조 객체는 다양한 이벤트에 반응함에 따라 상태가 변한다. 일단 고객 객체가 생성되면 시스템은 고객이 탈퇴할 때까지 고객 객체를 참조하고 추적할 수 있어야 한다. 이를 위해 도메인 모델에 추가되는 PURE FABRICATION이 리포지토리이다.
이제 도메인 객체의 생명 주기라는 콘텍스트에 영속성이라는 개념을 추가해 보자.
메모리는 비싼 자원이다. CPU가 애플리케이션을 실행하기 위해서는 우선 일차 저장소(primary storage)인 메모리에 필요한 모든 것들을 주섬주섬 올려 놓아야만 한다. 메모리는 비싼 자원이기 때문에 그 크기 역시 작을 수 밖에 없다. 부족한 메모리 공간에 이것 저것 쑤셔 넣다 보면 결국 빈번한 스와핑(swapping)으로 인해 전체적인 성능이 저하될 수 밖에 없다. 따라서 시스템은 자기가 현재 처리해야 할 고객, 주문, 상품을 제외한 모든 다른 정보들은 최대한 빨리 잊어 버려야 한다. 단기 기억 상실증은 시스템으로 하여금 전체적인 성능을 유지하게 하는 훌륭한 질병인 셈이다.
메모리는 휘발성이다. 메모리는 전원이 꺼지는 순간 청문회에 출두한 정치인들의 뇌처럼 과거의 모든 기억을 깨끗하게 잊어버린다. 따라서 시스템은 모든 도메인 객체를 메모리에 보관할 수 없으며 그렇게 해서도 안된다. 모든 사람들은 주문 시스템이 복구되었을 때 시스템 내에 보관되어 있던 모든 사용자, 주문, 상품 정보가 그대로 복구되기를 원한다. 단기 기억 상실증에 걸린 메모리에 모든 정보를 보관하고 있다가 뒤통수를 맞는 일을 바라지 않는다. 따라서 시스템 내에 지속적으로 보관되어야 하지만 지금 당장 필요하지 않은 정보를 이차 저장소(secondary storage)로 옮겨 놓을 필요가 있다. 에드가 앨런 포우의 말처럼 당장 어떤 것을 잊고 싶다면, 기억할 수 있게 적어 두어야 한다.
이처럼 일차 저장소인 메모리에서 활발하게 뛰어 놀던 도메인 객체를 붙잡아 이차 저장소에 동면시키는 기법을 영속성(Persistence) 메커니즘이라고 한다. 도메인 객체 중 고객, 주문, 상품처럼 이차 저장소에 영구히 보관하는 객체를 영속 객체(Persistence Object)라고 한다.
다양한 형식의 직렬화(Serialization) 기법들 역시 영속성 메커니즘의 일종이라고 볼 수 있다. java에서 제공하는 기본 직렬화는 메모리 내의 객체 상태를 이차 저장소인 파일 시스템에 저장한 후 필요할 때 원래의 상태로 복원한다. 그러나 대용량 데이터를 처리하는 엔터프라이즈 어플리케이션에서는 직렬화보다 더 견고하고 성능이 뛰어나며, 데이터 보안과 같은 다양한 지원 기능을 제공할 수 있는 인프라 스트럭처를 요구한다.
일반적으로 엔터프라이즈 애플리케이션은 대용량 데이터를 관리하기 위한 이차 저장소로 관계형 데이터베이스(Relation Database Management System)를 사용한다. 대부분의 엔터프라이즈 애플리케이션이 관계형 데이터베이스를 사용한다는 사실은 애플리케이션 개발자들에게 두 가지 의미를 가진다.
첫째, 관계형 데이터베이스 기술은 수십 년 동안 점진적으로 성장하고 발전한 안정적인 기술이다. 많은 벤더들이 RDMS를 개발하고 있으며 수많은 레거시(legacy) 시스템들이 관계형 데이터베이스를 영속성 저장소로 사용하고 있다. 수십 년 동안의 적용 경험과 축적된 노하우는 안정적인 어플리케이션 개발을 가능하게 한다.
둘째, 객체와 관계형 테이블 간의 거리는 너무 멀다. 관계형 데이터베이스는 수학적인 집합 개념을 그 기반으로 하며 정규화를 통해 데이터의 중복을 제거하는 것이 목적이다. 이에 비해 객체 지향은 객체 또는 객체들 간의 응집도와 결합도를 고려한 책임 할당과 행위의 다형성을 기반으로 한다. 관계형 데이터베이스는 상속성, 캡슐화, 다형성과 같은 객체 지향의 핵심적인 개념을 지원하지 않는다. 객체 세계의 초창기 시절부터 사람들은 객체와 관계형 데이터베이스 간에 임피던스 불일치(impedance mismatch)가 있음을 알고 있었다.
임피던스 불일치의 어려움은 엔터프라이즈 애플리케이션의 아키텍처에 큰 영향을 끼쳤다. 임피던스 불일치를 극복하지 못했던 초창기 개발 커뮤니티의 선택은 데이터베이스 테이블에 매핑하기 쉬운 구조로 도메인 레이어를 설계하는 것이었다. 불에 기름을 붓는 격으로 엔티티 빈의 제약 사항은 이런 설계를 장려하는 결과를 낳았다. 결과적으로 객체 지향 언어로 개발된 절차적인 방식의 애플리케이션 아키텍처라는 사생아가 탄생했으며 J2EE 엔터프라이즈 애플리케이션의 주도적인 아키텍처로 자리 잡고 말았다. 이 영향으로 현재까지도 행위가 없이 상태만 가지는 빈약한 도메인 모델(Anemic Domain Model)을 가지는 트랜잭션 스크립트(TRANSACTION SCRIPT) 패턴이 엔터프라이즈 애플리케이션 아키텍처의 주를 이루고 있다.
행위와 상태를 함께 가지는 객체를 사용하여 도메인 레이어를 설계하는 방식을 도메인 모델(DOMAIN MODEL) 패턴이라고 한다. 도메인 모델 패턴은 상속성, 캡슐화, 다형성과 같은 모든 객체 지향 기법들을 활용하기 때문에 임피던스 불일치 문제를 해결하기 위한 하부 인프라 스트럭처의 지원 없이는 적용하기가 쉽지 않다.
임피던스 불일치를 해결하는 가장 쉬운 방법은 객체 지향 데이터베이스를 사용하는 것이다. 그러나 객체 지향 데이터베이스 기술은 상업적으로 성공하지 못했고 결과적으로 대부분의 프로젝트에서 고려사항이 아니다. 비록 객체 지향 데이터베이스를 사용할 수 없다고 하더라도 방법은 있다. 객체 계층과 관계형 데이터베이스 계층 사이에 가상의 객체 지향 데이터베이스를 구축하는 것이다. 이처럼 객체와 관계형 데이터베이스 테이블 간의 불일치를 소프트웨어적으로 해결하는 것을 객체 관계 매핑(Object-Relation Mapping)이라고 하며 객체 관계 매핑을 수행하는 소프트웨어를 객체 관계 매퍼(Object-Relation Mapper, ORM)라고 한다.
ORM은 내부적으로 데이터 매퍼(DATA MAPPER) 패턴을 사용한다. 데이터 매퍼는 객체 지향 도메인 객체와 관계형 데이터베이스 테이블, 그리고 매퍼 자체의 독립성을 유지하면서 도메인 객체와 테이블 간의 데이터를 이동시키는 객체이다. 일반적으로 데이터 매퍼는 메타데이터 매핑(METADATA MAPPING) 패턴을 사용하여 객체와 테이블 간의 매핑 정보를 애플리케이션 외부의 설정 파일로 관리한다. 데이터 매퍼의 메커니즘에 관해서는 마틴 파울러(Martin Fowler)의 “엔터프라이즈 애플리케이션 아키텍처 패턴”을 참고하라.
다시 주문 도메인을 살펴보자. Customer 클래스는 고객의 상태와 상태를 변경시키기 위한 행위를 함께 가지고 있다. Order 클래스는 주문의 상태와 상태를 변경시키기 위한 행위를 함께 가지고 있다. 즉, 주문 도메인 애플리케이션의 도메인 로직은 도메인 모델 패턴으로 구성되어 있다. 따라서 임피던스 불일치 문제를 해결하기 위한 가장 쉬운 방법은 데이터 매퍼 패턴을 사용한 ORM을 적용하는 것이다. 이제 주문 애플리케이션에 영속성을 부여하기 위해 ORM을 적용해야 할 때가 왔다.
지금까지 애플리케이션의 생명주기 동안 지속적으로 추적해야 하는 객체들을 참조 객체로 모델링하고 연관된 참조 객체들을 애그리게이트라고 하는 하나의 객체 클러스터로 식별했다. 각 애그리게이트에 대해 엔트리 포인트를 선정하고 엔트리 포인트 별로 리포지토리를 할당한 후 리포지토리를 통해 애그리게이트의 생명 주기를 관리하도록 했다. 이제까지는 단순히 리포지토리를 참조 객체의 메모리 컬렉션을 관리하는 객체로만 바라보았다. 이제 참조 객체의 영속성을 관리하는 객체로 리포지토리의 개념을 확장해 보도록 하자.
728x90'[옛날 글들] 도메인 주도 설계' 카테고리의 다른 글
도메인 주도 설계의 적용 - 3. 의존성 주입과 관점 지향 프로그래밍 4부 (0) 2024.04.18 도메인 주도 설계의 적용 - 3. 의존성 주입과 관점 지향 프로그래밍 3부 (0) 2024.04.17 도메인 주도 설계의 적용 - 3. 의존성 주입과 관점 지향 프로그래밍 1부 (0) 2024.04.13 도메인 주도 설계의 적용 - 2. 애그리게이트와 리포지토리 5부 (0) 2024.04.12 도메인 주도 설계의 적용 - 2. 애그리게이트와 리포지토리 4부 (0) 2024.04.12