2012/03/17 21:52
이전 글에서 언급했듯이 도메인 모델을 Domain Driven Design (DDD) 을 적용해서 구축을 하면 중요한 부분중 하나가 바로 리파지토리 (Repository) 입니다. 새롭게 생성된 도메인 객체 (transient instance) 를 데이타베이스에 집어 넣고, 저장되었던 객체 (persisted instance) 를 꺼내서 도메인 모델에 전달해 주는 객체들이죠. 도메인 모델 관점에서 봤을 때는 마치 데이타 저장소처럼 보이는 객체들이고, 데이타베이스 관점에서 봤을 때는 저장된 관계형 데이타를 도메인 객체로 바꿔서 모델에 전달해 주는 역할을 합니다. Persistence Ignorance (PI) 에 직/간접적인 영향을 미치는 객체들입니다.
리파지토리는 개념적으로는 도메인 모델의 일부지만 그 역할이나 구현으로 봤을 때는 DAO (Data Access Object)에 가깝다고 볼 수도 있습니다. 박쥐같은 녀석이죠. 그래서 잘못 설계가 되면 도메인 모델과 데이타베이스와의 결합 (coupling) 의 주범이 될 수 있습니다. 또한 도메인 모델과 데이타 저장소의 중간에서 일차적 PI의 역할을 합니다. 왜냐하면 리파지토리는 어떻게든 객체를 저장을 하고 저장된 데이타를 읽어 객체화 해야 하기 때문입니다. SQL 을 직접 사용하던 ORM 을 사용하던 간에 도메인 모델과 데이타베이스 사이에서 중계 역할을 해야 합니다.
리파지토리에 대한 첫번째 고민은 도메인 객체들이 리파지토리에 접근이 가능해야 하는가 입니다. 개념적으로는 도메인 모델의 일부이니 도메인 객체들이 리파지토리에 접근하는 것은 자연스러워 보입니다. 하지만 도메인 객체에서 직접적으로 사용하도록 설계한다면 도메인 모델을 분리해 낸 의미가 퇴색하게 됩니다. 앞서 언급했듯이 리파지토리는 직/간접적으로 데이타베이스에 접근을 해야하기 때문에 도메인 모델과 데이타베이스의 결합 (coupling)이 발생하게 되죠. 다른 곳에서 도메인 모델만을 온전히 사용할 수 없게 됩니다. 리파지토리에 대한 인터페이스를 이용해서 간접 접근을 한다 해도 별반 다르지 않습니다. 도메인 객체가 그 인터페이스를 사용하는 한, 그 인터페이스를 구현하는 실객체가 존재하기 전에는 사용을 할 수 없기 때문입니다.
그렇다면 도메인 모델이 리파지토리를 사용할 수 없는 경우를 생각해 보겠습니다. 도메인 모델에는 순수하게 도메인 객체들만이 존재하고 리파지토리에서는 도메인 모델을 레퍼런스 하고 있겠죠. 따라서 누군가 도메인 객체를 레파지토리에 넣고 빼는 역할을 해야 합니다. 일종의 서비스가 필요해진다는 말이 됩니다. 이런 경우 도메인 로직이 서비스로 흘러나가는 경우가 생깁니다.
예를들어, 도메인 모델은 수 많은 객체들의 상호작용을 기반으로 하고 있습니다. 한 객체가 특정 조건에 따라 다른 객체를 사용해야 하죠. 이 경우 그 객체는 리파지토리에 접근할 수 없기 때문에 리파지토리를 사용할 수 있는 서비스에서 필요한 객체를 찾아내서 그 객체에 전달을 해야 합니다. 따라서 어떤 다른 객체가 필요한지에 대한 판단을 해당 객체가 하지 못하고 서비스가 하게 됩니다.
이런 경우 도메인 로직이 자연스럽게 서비스로 이동이 되고 따라서 도메인 로직이 분산이 됩니다. 만약 도메인 모델이 리파지토리를 접근할 수 있었다면 서비스가 필요 없었겠죠. 자신에게 무엇이 필요한지 가장 잘 알고 있는 객체 자신이 필요한 다른 객체를 얻어 낼 수 있기 때문입니다. 물론 도메인 객체들간의 특정한 상호작용을 도메인 로직으로 볼 것이냐 서비스 로직으로 볼 것이냐에 대한 논의가 필요할 수도 있습니다.
리파지토리에 대한 또 다른 고민은 메서드에 있습니다. 특정 객체나 객체의 집합을 조회하는 데에는 많은 조건이 붙습니다. 예를들어, 어떤 기간에 해당되는 주문들, 특정 고객군에 대한 주문들, 특정 상품이 속한 주문들... 수시로 변화가 필요한 조건들이 많습니다. 이런 다양하고 변화가 심한 조건을 어떻게 충족시킬 것인가 하는 점입니다.
각 조건에 따른 메서드를 만든다면 수많은 메서드들을 감당할 수 없겠죠. 하지만 그 반대로 아주 일반적인 메서드만을 갖고 있다면 성능의 문제가 발생합니다. 몇개의 객체를 찾기 위해서 포괄적인 조건으로 수십수백개의 객체를 로드한 후에 조건에 맞는 객체를 다시 필터링해야 하는 상황이 됩니다.
보통 리파지토리는 하나의 객체를 키로 찾을 수 있는 메서드와 외부키로 여러개의 객체를 찾을 수 있는 메서드는 기본적으로 갖게 됩니다. 그 이외에 발생하는 다양한 조건마다 새로운 메서드를 추가하지 않으려면 외부에서 조건을 주입할 수 있어야 합니다. 이 경우 실질적으로 데이타베이스에 접근하는 방식에 따라서 차이가 납니다. 자체 제작된 OR 매핑을 사용하는 경우 데이타베이스의 SQL 을 직접 사용하거나 쿼리객체 패턴을 사용할 수 있고, ORM 을 사용하는 경우는 해당 기술에서 지원되는 쿼리를 주입할 수 있습니다. 물론 이 경우 ORM 과 데이타베이스에 종속성이 생기지만 이는 일단 별개의 문제입니다. 참고로, .NET 의 경우 Entity Framework 을 사용한다면 DbQuery를 사용해서 다이나믹한 just-in-time 쿼리가 가능합니다.
리파지토리를 사용하면서 대두되는 두개의 큰 고민거리들을 정리해봤습니다. 첫번째 문제는 도메인 모델의 분리와 PI 에 의한 도메인 로직의 분산 가능성에 대한 것이었고, 두번째는 리파지토리 디자인과 성능에 관한 문제였습니다. 그래도 이전 글에서 이야기했던 Data Transfer Object (DTO)와 사용자화면에 대한 문제에 비하면 어느정도는 해답이 있는 문제같습니다.
다음에는 서비스에 대해서 정리를 해 보겠습니다.
리파지토리는 개념적으로는 도메인 모델의 일부지만 그 역할이나 구현으로 봤을 때는 DAO (Data Access Object)에 가깝다고 볼 수도 있습니다. 박쥐같은 녀석이죠. 그래서 잘못 설계가 되면 도메인 모델과 데이타베이스와의 결합 (coupling) 의 주범이 될 수 있습니다. 또한 도메인 모델과 데이타 저장소의 중간에서 일차적 PI의 역할을 합니다. 왜냐하면 리파지토리는 어떻게든 객체를 저장을 하고 저장된 데이타를 읽어 객체화 해야 하기 때문입니다. SQL 을 직접 사용하던 ORM 을 사용하던 간에 도메인 모델과 데이타베이스 사이에서 중계 역할을 해야 합니다.
리파지토리에 대한 첫번째 고민은 도메인 객체들이 리파지토리에 접근이 가능해야 하는가 입니다. 개념적으로는 도메인 모델의 일부이니 도메인 객체들이 리파지토리에 접근하는 것은 자연스러워 보입니다. 하지만 도메인 객체에서 직접적으로 사용하도록 설계한다면 도메인 모델을 분리해 낸 의미가 퇴색하게 됩니다. 앞서 언급했듯이 리파지토리는 직/간접적으로 데이타베이스에 접근을 해야하기 때문에 도메인 모델과 데이타베이스의 결합 (coupling)이 발생하게 되죠. 다른 곳에서 도메인 모델만을 온전히 사용할 수 없게 됩니다. 리파지토리에 대한 인터페이스를 이용해서 간접 접근을 한다 해도 별반 다르지 않습니다. 도메인 객체가 그 인터페이스를 사용하는 한, 그 인터페이스를 구현하는 실객체가 존재하기 전에는 사용을 할 수 없기 때문입니다.
그렇다면 도메인 모델이 리파지토리를 사용할 수 없는 경우를 생각해 보겠습니다. 도메인 모델에는 순수하게 도메인 객체들만이 존재하고 리파지토리에서는 도메인 모델을 레퍼런스 하고 있겠죠. 따라서 누군가 도메인 객체를 레파지토리에 넣고 빼는 역할을 해야 합니다. 일종의 서비스가 필요해진다는 말이 됩니다. 이런 경우 도메인 로직이 서비스로 흘러나가는 경우가 생깁니다.
예를들어, 도메인 모델은 수 많은 객체들의 상호작용을 기반으로 하고 있습니다. 한 객체가 특정 조건에 따라 다른 객체를 사용해야 하죠. 이 경우 그 객체는 리파지토리에 접근할 수 없기 때문에 리파지토리를 사용할 수 있는 서비스에서 필요한 객체를 찾아내서 그 객체에 전달을 해야 합니다. 따라서 어떤 다른 객체가 필요한지에 대한 판단을 해당 객체가 하지 못하고 서비스가 하게 됩니다.
이런 경우 도메인 로직이 자연스럽게 서비스로 이동이 되고 따라서 도메인 로직이 분산이 됩니다. 만약 도메인 모델이 리파지토리를 접근할 수 있었다면 서비스가 필요 없었겠죠. 자신에게 무엇이 필요한지 가장 잘 알고 있는 객체 자신이 필요한 다른 객체를 얻어 낼 수 있기 때문입니다. 물론 도메인 객체들간의 특정한 상호작용을 도메인 로직으로 볼 것이냐 서비스 로직으로 볼 것이냐에 대한 논의가 필요할 수도 있습니다.
리파지토리에 대한 또 다른 고민은 메서드에 있습니다. 특정 객체나 객체의 집합을 조회하는 데에는 많은 조건이 붙습니다. 예를들어, 어떤 기간에 해당되는 주문들, 특정 고객군에 대한 주문들, 특정 상품이 속한 주문들... 수시로 변화가 필요한 조건들이 많습니다. 이런 다양하고 변화가 심한 조건을 어떻게 충족시킬 것인가 하는 점입니다.
각 조건에 따른 메서드를 만든다면 수많은 메서드들을 감당할 수 없겠죠. 하지만 그 반대로 아주 일반적인 메서드만을 갖고 있다면 성능의 문제가 발생합니다. 몇개의 객체를 찾기 위해서 포괄적인 조건으로 수십수백개의 객체를 로드한 후에 조건에 맞는 객체를 다시 필터링해야 하는 상황이 됩니다.
보통 리파지토리는 하나의 객체를 키로 찾을 수 있는 메서드와 외부키로 여러개의 객체를 찾을 수 있는 메서드는 기본적으로 갖게 됩니다. 그 이외에 발생하는 다양한 조건마다 새로운 메서드를 추가하지 않으려면 외부에서 조건을 주입할 수 있어야 합니다. 이 경우 실질적으로 데이타베이스에 접근하는 방식에 따라서 차이가 납니다. 자체 제작된 OR 매핑을 사용하는 경우 데이타베이스의 SQL 을 직접 사용하거나 쿼리객체 패턴을 사용할 수 있고, ORM 을 사용하는 경우는 해당 기술에서 지원되는 쿼리를 주입할 수 있습니다. 물론 이 경우 ORM 과 데이타베이스에 종속성이 생기지만 이는 일단 별개의 문제입니다. 참고로, .NET 의 경우 Entity Framework 을 사용한다면 DbQuery를 사용해서 다이나믹한 just-in-time 쿼리가 가능합니다.
리파지토리를 사용하면서 대두되는 두개의 큰 고민거리들을 정리해봤습니다. 첫번째 문제는 도메인 모델의 분리와 PI 에 의한 도메인 로직의 분산 가능성에 대한 것이었고, 두번째는 리파지토리 디자인과 성능에 관한 문제였습니다. 그래도 이전 글에서 이야기했던 Data Transfer Object (DTO)와 사용자화면에 대한 문제에 비하면 어느정도는 해답이 있는 문제같습니다.
다음에는 서비스에 대해서 정리를 해 보겠습니다.
'소프트웨어 개발' 카테고리의 다른 글
| 리파지토리에 대한 고민 (4) | 2012/03/17 |
|---|---|
| 도메인 모델에 대한 고민들 (2) | 2012/03/16 |
| Repository/DAO, DTO, 그리고 확장성과 퍼포먼스 (2) | 2012/03/14 |
| 개발자의 틀 (0) | 2012/03/12 |
| Enum 과 객체지향 (0) | 2012/01/21 |
| 어제 생긴 일... (4) | 2011/11/15 |
