Isolating the Domain: Introducing the Applications


도메인의 책임이 시스템의 다른 부분과 뒤섞이는 것을 방지하기 위해서, 도메인 레이어를 표시하는 Layered Architecture를 적용해 보자.
깊은 분석 없이도, 3가지 user-level application function을 명시할 수 있고, 3개의 어플리케이션 레이어 클래스에 할당할 수 있다.
1. 특정 화물(Cargo)에 대한 과거와 현재 처리에 접근할 수 있는 Track query.
2. 시스템에 New Cargo를 등록하고 준비할 수 있는 Booking Application.
3. 각 화물(Cargo)의 처리를 기록할 수 있는 Incident Logging Application. (Tracking Query에서 검색한 정보를 제공)

Distinguishing ENTITIES and VALUE Objects

(Entity와 Value Object의 구분)

Customer
Customer는 명백하게 사용자를 나타내는 Indentity를 가지고 있으므로 모델에서 Entity이다.

Cargo
2개의 동일한 상자가 구분되어야 하기 때문에 Cargo 객체는 Entity이다. 실제로 화물 회사에서는 각 화물에 tracking ID를 부여한다.
이러한 ID는 자동으로 생성되어 user에서 보여지고, 이 경우에는 booking time(화물을 부칠 때)시 customer에게 전달 될 것이다.

Handling Event and Carrier Movement
무슨 일이 일어나고 있는지 추적하기 위해서 개별 incident에 주의를 기울인다.
실제 세상의 이벤트를 반영하고, 일반적으로 교환가능하지 않다. 따라서 Entity에 해당한다. 개별 Carrier Movement는 수화물 선적 스케쥴에서 얻은 코드로 명시될 수 있다.
Handling Events는 Cargo ID, Completion Time, Type의 조합으로 유일하게 명시될 수 있다. 왜냐하면, Same Cargo는 동시에 선적되고, 하선될 수 없기 때문이다.

Location
동일한 이름을 가진 2개의 장소는 동일한 장소가 아니다. 위도와 경도가 유일한 키로 제공될 수 있지만, 실용적이지 않기 때문에 이 시스템에서 대부분의 목적에 적합한 측정값이 아니다. Location은 수화물 선적 항로와 다른 도메인 특화 개념에 따른 장소와 관련되어 있는 지정학적 모델의 일부이다. 따라서 임의의, 내부적인, 자동으로 생성된 identifier가 제공될 것이다.

Delivery History
까다로운 항목이다. Delivery History는 교환가능하지 않기 때문에 Entity이다. 그러나 Delivery History는 Cargo와 1:1 관계성을 갖기 때문에 실제로는 그 자체로 identity를 가지지 않는다. Delivery History의 identity는 Cargo의 것을 빌려온다. 모델을 Aggregate로 할 때 더 분명해진다.

Delivery Specification
비록 Delivery Specification이 Cargo의 목적지를 의미하지만, 이 추상화는 Cargo에 달려 있다. Delivery Specification은 사실은 어떤 Delivery History의 가상의 상태를 표현한다. Cargo에 첨부된 Dilivery History가 점차적으로 Cargo에 추가된 Delivery Specification을 만족시킬 것으로 기대한다. 만약 동일한 장소에 2개의 Cargo를 가지고 있다면, 동일한 Delivery Specification을 공유하지만, 동일한(empty)로 히스토리로 시작하는 경우일지라도, 동일한 Delivery History를 공유하는 것은 아니다. Delivery Specification은 Value Object이다.

Role and Other Attributes
Role은 자격을 얻는 연관성(association)에 대해서 이야기하고 있지만, 히스토리나 연속성은 없다. Role은 Value Object이고, 다른 Cargo/Customer 연관성 사이에 공유할 수 있다. 타임스탬프나 이름과 같은 다른 속성은 Value Object이다.

Designing Associations in the Shipping Domain


Original Diagram에 있는 연관성 중에 어느 것도 지정된 횡방향이 없지만, 설계에서 양방향 연관성은 문제가 있다. 또한 횡방향은 종종 도메인에 대한 통찰력(insight)을 포착하여 모델 그 자체를 심화시킨다.

Customer가 선적된 모든 Cargo에 대해 직접적인 참조를 가지고 있는 경우에는, 장기간 반복되는 Customer이 성가셔 할 것이다. 또한 Customer의 개념은 Cargo에 특화된 것이 아니다. 커다란 시스템에서는, Customer가 많은 객체로 작업하는 역할을 가지게 될 것이다. 그러한 특정 책임에 대한 자유를 주는 것이 가장 좋은 방법이다. Customer가 Cargo를 발견하는 능력을 필요로 하는 경우, 데이터베이스 쿼리를 통해서 작업할 수 있다.

어플리케이션이 선박의 재고(inventory)를 추적하는 경우에는 Carrier Movement에서 Handling Event로의 이동이 중요하다. 하지만, 비즈니스에서는 단지 Cargo가 필요할 뿐이다. Handling Event에서 Carrier Movement로 연관성을 이동가능하게 하는 것은 비즈니스에 대한 이해를 획득하는 것이다. 다중성을 가진 방향성을 허용하지 않기 때문에 단순한 객체 참조로 구현을 줄일 수 있다.

AGGREGATE Boundaries


Customer, Location, Carrier Movement는 고유한 Identity를 가지고, 많은 Cargoes에서 공유하기 때문에 자신의 속성과 여기서 논의하는 상세 레벨(detail level) 아래에 있을 수 있는 다른 객체들을 포함하는 자체 Aggregate의 root가 되어야만 한다.
Cargo는 또한 분명한 Aggregate root이지만, boundary를 그릴 때에는 약간 생각이 필요하다.

Cargo Aggregate는 존재하지 않는 모든 것을 쓸어 담을 수 있지만, 특정 Cargo에는 Delivery History와 Delivery Specification, Handling Event를 포함할 수 있다. Delivery History는 Cargo ID를 통해서 조회될 수 있으므로, Cargo Boundary내부에 존재하는 것이 적합하다. Delivery Specification(배송 명세서)은 Value Object이므로 Cargo Aggregate내에 포함되는 것에 문제가 안 된다.

Handling Event는 또다른 문제이다. 앞에서 이것을 검색할 수 있는 2가지 데이터베이스 쿼리에 대해서 고려했다. 하나는 Cargo Aggregate내에 오컬이 되는 Collection에 대한 가능한 대안으로써 Delivery History에 대해 Handling Event를 찾는 것이다. 다른 하나는 특정 Carrier Movement에 대해 로드(load)하고 준비할 모든 동작을 찾도록 사용하는 것이다. 두번째 경우에, Cargo를 처리하는 활동은 Cargo 그 자체와 별개로 고려될 때도 의미가 있어 보인다. 따라서 Handling Event는 그 자체로 Aggregate의 Root가 되어야 한다.

사용자 삽입 이미지

[그림 7-1] Aggregate Boundary

Selecting REPOSITORIES


설계에는 Aggreate root가 5개 Entity로 존재한다. 따라서 Repository를 가질 수 있도록 허용된 다른 객체가 없기 때문에 고려 대상을 제한할 수 있다.
실제로 이러한 Entity들 중에서 어느 것이 Repository를 가져야 하는지 결정하기 위해서는, 어플리케이션 요구사항으로 돌아가야만 한다. 예약 어플리케이션(Booking application)을 통해서 예약하기 위해서 사용자는 여러 역할을 수행할(수화물 배송자(shipper), 수화물 수취인(receiver), 등) Customer(s)를 선택해야 한다. 따라서 Customer Repository가 필요하다. Cargo에 대한 목적지를 명시하기 위해서 Location을 찾을 수 있는  Location Repository를 생성해야 한다.

Activity Logging Application은 사용자가 Cargo가 적재되고 있는 Carrier Movement를 찾을 수 있어야 하므로 Carrier Movement Repository가 필요하다. 사용자는 Cargo가 어느 시스템에 로드되었는지 알려야 하므로 Cargo Repository도 필요하다.

사용자 삽입 이미지

[그림 7-2] Repository

여기서 첫번째 iteration에서 Delivery History와의 연관성을 Collection으로 구현하기로 결정했고, Carrier Movenent로 무엇이 로드되었는지를 찾을 필요가 없기 때문에 Handling Event Repository는 없다. 이러한 이유중 하나가 만약에 바뀐다면, Repository를 추가해야 한다.


Walking Through Scenarios


이러한 모든 결정을 상호 체크하기 위해서 지속적으로 어플리케이션 문제를 효율적으로 해결할 수 있는 시나리오를 통해 단계를 밟아야 한다.

Sample Application Feature: Changing the Destination of a Cargo

경우에 따라서 Customer는 "화물을 Haceksack로 보내달라고 했지만, 사실은 Hoboken으로 보내야 합니다"라고 말할 수 있는데, 시스템에서는 이러한 변경을 반영할 수 있어야 한다.

Delivery Specification은 Value Object이기 때문에, 간단히 버리고 새로운 것을 얻을 수 있다. 따라서, Cargo는 setter method를 통해서 변경할 수 있다.

사용자는 동일한 Customer의 반복되는 예약은 유사한 경향이 있기 때문에 이전 Cargo를 새로운 것에 대한 프로토타입으로 사용하고 싶어 한다. 어플리케이션은 Repository에서 Cargo를 찾은 다음, 선택된 것을 기반으로 새로운 Cargo를 생성하는 명령을 선택할 수 있을 것이다. Prototype 패턴을 사용하여 설계할 것이다.

Cargo는 Eentity이고, Aggregate의 Root이다. 그러므로, 주의 깊게 복사해야 한다. Aggregate Boundary에 둘러싸여 있는 각 객체나 속성에 무슨 일이 일어날지 고려해야 한다.

각각에 대해서 살펴보자.
Delivery History: 이전 History가 적용되지 않기 때문에 새로운 비어있는 객체를 생성해야 한다. Aggregate Boundary내에 Entity가 있는 일반적인 경우에 해당한다.
Customer Roles: 새로운 수화물(shipment)에서 동일한 역할을 수행하게 될 수 있으므로, 키를 포함하고 있는 Customer를 참조하는 키를 가지고 있는 맵(Map, or Collection)을 복사해야 한다. 하지만, Customer 객체 자체를 복사하지 않도록 주의해야 한다. Aggregate Boundary 밖에 있는 Entity이기 때문에, 이전에 참조된 Cargo 객체와 동일한 Customer 객체에 대한 참조로 끝나야 한다.
Tracking ID: 처음부터 새로운 Cargo를 생성할 때처럼 동일한 소스에서 새로운 Tracking ID를 제공해야 한다.

모두 Cargo Aggregate Boundary 내부에서 모든 것을 복사했음에 유의하라. 복사본에 약간의 수정이 있을 수 있지만, Aggregate Boundary 바깥에는 전혀 영향을 미치지 않는다.

Object Creation


FACTORIES and Constructors for Cargo

Cargo에 대한 멋진 Factory를 가지고 있거나 "Repeat Business"시나리오에서처럼 Factory로 또다른 Cargo를 사용한다면, 여전히 Primitive 생성자를 가져야 한다.
생성자가 불변성(invariant)을 만족시키거나 최소한 Entity의 경우, Identity를 손대지 않는 객체를 생성하기를 원한다.

Factory에서 리턴하는 결과는 동일할 것이다. 비어 있는 Delivery History, 널 값을 가지는 Delivery Specfication을 가진 Cargo를 리턴한다.

Pause for Refactoring: An Alternative Design of the Cargo AGGREGATE

모델링과 설계는 지속적인 전진 과정이 아니다. 모델과 설계를 개선하는 새로운 통찰력(insight)을 얻기 위해서 잦은 리팩토링이 없으면 서서히 멈출 것이다. 비록 작업을 수행하고, 모델을 반영할지라도 지금까지, 이 설계에 몇 가지 까다로운 측면이 있다. 설계 시작할 때 중요하지 않게 보이는 문제들은 성가신 문제가 되기 시작할 것이다.

만약 동시에 몇몇 사용자가 Cargo를 수정하는 경우, Handling Event 트랜잭션은 실패하거나 지연될 것이다. Handling Event를 입력하는 것은 빠르고 쉽게 해야 하는 작업 활동(operational activity)이므로, 중요한 어플리케이션 요구사항은 경합없이 Handling Event를 입력할 수 있는 기능이다. 이것은 다른 설계를 고려하게 한다.

Handling Event의 Delivery History Collection을 쿼리로 대체하면 Aggregate 밖에서 무결성 이슈를 제기하지 않고 Handling Event를 추가할 수 있다.
이러한 변경은 간섭없이 트랜잭션을 완료할 수 있게 한다. 만약 입력되는 Handling Event가 많고, 상대적으로 쿼리수가 적으면, 이 설계는 더 효율적이 된다. 사실, 관계형 데이터베이스가 기본 기술일 경우, 쿼리를 사용하여 Collection을 에뮬레이션할 수 있다.

Collection보다 쿼리를 사용하는 것이 Cargo와 Handling Event 사이에 주기적 참조에 대해 일관성을 유지하는데 대한 어려움을 감소시킨다. 쿼리에 대한 책임을 지기 위해서 Handling Event에 대한 Repository를 추가한다.

Handling Event Repository는 특정 Cargo와 관련된 Event에 대한 쿼리를 지원한다. 게다가, Repository는 특정 질문에 대해 효율적으로 대답하기 위해 최적화된 쿼리를 제공할 수 있다. 예를 들면, 잦은 접근 경로가 마지막으로 보고된 로드 혹은 언로드를 찾는 Delivery History라면, Cargo의 현재 상태를 추론하기 위해서 관련된 Handling Event만을 리턴하도록 쿼리를 고안할 수 있다.
특정 Carrier Movement에 로드된 모든 Cargo를 찾는 쿼리를 원한다면, 쉽게 추가할 수 있다.


받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://www.yongbi.net/rss/response/826

Chapter Six. The Life Cycle of a Domain Object


모든 객체는 생명 주기(life cycle)를 가지고 있다. 다양한 상태를 거쳐서 객체는 태어나고, 일정기간 동안 보관되거나(archived) 삭제되면서 점차적으로 사라진다.

문제는 2가지 범주로 나뉜다.

1. 수명 주기(life cycle) 동안 무결성(integrity) 유지.
2. 수명 주기 관리의 복잡성으로 인해 모델이 수렁에 빠지는 것을 방지.

3가지 패턴을 통해서 이러한 이슈를 다룰 것이다.
첫째, AGGREGATES는 명확한 소유권과 경계를 정의하고, 객체가 혼란스럽고 그물처럼 얽힌 것을 피하면서 모델 자체를 강화한다.
이 패턴은 수명 주기 동안 모든 단계에서 무결성을 유지하는데 중요하다.
둘째, 복잡한 객체와 내부 구조 캡슐화를 유지하는 AGGREGATES를 생성하고 재구성하기 위해 FACTORIES를 사용하여 수명 주기 초기로 돌린다.
셋째, REPOSITORIES는 뒤얽힌 광대한 인프라스트럭쳐를 캡슐화하여 지속적인 객체(persistent object)를 찾고 검색하는 수단을 제공하고, 수명 주기의 중간 단계와 끝 부분을 처리한다.
비록, REPOSITORIES and FACTORIES는 도메인에서 나온 것은 아니지만, 도메인 디자인에서 의미있는 역할을 한다.

AGGREGATES는 수명 주기의 모든 단계에서 불변을 유지해야 하는 범위를 표시한다.
FACTORIES and REPOSITORIES는 특정 수명 주기의 복잡성을 캡슐화하여 AGGREGATES에 작용한다.

Aggregates

(집합)

하나의 독립된 객체에 대해 수정/삭제하고자 하는 요청이 동시에 발생했을 때, 순서를 보장해야 한다.
복잡한 연관성을 가진 모델에서 객체를 변경하는 것에 대한 일관성을 보장하는 것은 어렵다.
개별 객체 뿐만 아니라, 밀접하게 관련된 객체 그룹에 적용되는 불변성을 유지 관리해야 한다.
그러나 조심스런 잠금 계획(locking scheme)은 여러 사용자들이 서로를 무의미하게 간섭하게 하고 시스템을 사용할 수 없게 한다.

우선, 모델 내에서 참조를 캡슐화하기 위한 추상화가 필요하다.
AGGREGATE는 데이터 변경 목적을 위해 하나의 단위로 취급되는 연관된 객체의 클러스터이다.
각각의 AGGREGATE는 Root와 Boundary(경계)를 가지고 있다.
Boundary는 AGGREGATE안에 있는 것을 정의한다.
Root는 AGGREGATE에 포함되어 있는 특별한 단일 ENTITY이다.
Root는 단지 외부 객체가 참조할 수 있는 AGGRETATE의 구성원일 뿐이지만, Boundary내에 있는 객체들은 서로를 참조할 수 있다.
ENTITIES는 Root가 아닌 로컬 ID를 가지지만, 외부 객체가 Root ENTITY 컨텍스트 밖에서는 볼 수 없기 때문에 해당 ID를 AGGREGATE내에서 구별할 수 있어야 한다.

데이터가 변경될 때마다 유지되어야 하는 일관성 규칙(consistency rule)인 불변성(invariant)은 AGGREGATE의 구성원 사이의 관계를 포함한다. AGGREGATES를 채우는 모든 규칙은 항상 최신 상태로 유지되는 것은 아니다.
Event Processing, Batch Processing, 또는 다른 업데이터 메커니즘을 통해서 특정 시간 내에 다른 종속성을 해결할 수 있다.
그러나 AGGREGATE내 적용된 불변성은 각 트랜잭션의 완료와 함께 시행될 것이다.

ENTITIES와 VALUE OBJECTS를 AGGREGATES로 클러스터하고 각각의 경계를 정의하라(Define Boundary around each). 하나의 Entity를 선택하여 각 Aggregate의 Root가 되게 하고, Root를 통해서 경계 내부 객체에 대한 모든 접근을 통제하라.
외부 객체는 단지 Root만을 참조할 수 있도록 하라.
내부 구성원에 대한 일시적인 참조는 단일 동작 내에서만 사용할 수 있다.
Root가 접근에 대해서 통제를 하기 때문에, 내부를 변경하여 공격할 수 없다.
이러한 배열은 Aggregate에 있는 객체에 대한 모든 불변성과 모든 상태 변화에 따른 Aggregate의 불변성을 적용하는 것을 실용적으로 만든다.


Factories


객체나 전체 Aggregate를 만들 때, 내부 구조가 너무 복잡해지거나 너무 많이 드러날 경우, FACTORIES가 캡슐화를 제공한다.
객체의 생성은 그 자체로 주요한 작업이 될 수 있지만, 복잡한 조립(assembly) 작업은 생성된 객체의 책임으로는 적합하지 않다.
그러한 책임을 결합하는 것은 이해하기 어려운 꼴사나운 설계를 만들 수 있다.

When a Constructor Is All You Need

다음 환경에서 public constructor를 선호한다.
- 클래스는 타입이다. 클래스는 흥미로운 계층 구조의 일부가 아니고, 인터페이스 구현으로 여러 가지 형태(다형성)로 사용되지 않는다.
- 클라이언트는 STRATEGY를 선택하는 방법으로 구현을 고려한다.
- 객체의 모든 속성을 클라이언트에서 사용할 수 있기 때문에 클라이언트에 노출된 생성자 내부에 객체 생성이 중첩(nested)되지 않는다.
- 생성은 복잡하지 않다.
- public 생성자는 FACTORY와 동일한 규칙을 따라야 한다. 생성된 객체의 모든 불변성을 만족시키는 원자 연산(automic operation)이어야 한다.

다른 클래스의 생성자 내에서 생성자를 호출하는 것을 피하라.
생성자는 정말로 간단해야 한다.
복잡한 구성, 특히 Aggregate가 그렇게 복잡할 때, Factory를 요구한다.

Designing the Interface

독립형이든 FACTORY METHOD를 사용하든 상관없이 FACTORY의 method 서명을 설계할 때는 2가지 사항을 염두에 두어야 한다.
- 각 동작은 원자적이여야 한다. (must be automic) FACTORY와 단일 상호작용으로 완전한 생산품을 만드는데 필요한 모든 것을 전달해야 한다. 또한, 일부 불변성이 만족스럽지 않은 경우, 생성이 실패했을 때 발생할 수 있는 것이 무엇인지 결정해야 한다. Exception이나 Null을 던질 수 있다. 일관성을 유지하기 위해서, FACTORIES의 실패에 대한 코딩 표준을 채택하는 것을 고려하라.
- FACTORY는 인자(arguments)에 연결될 것이다. 입력 변수를 신중하게 선택하지 않는다면, 난잡한 의존성을 만들 수 있다. 연결성 정도는 인자로 무엇을 하는지에 달려 있다. 생산품에 간단히 연결된다면, 적당한 의존성을 가질 수 있지만, 만약 생성에 사용하기 위해서 인자 중에서 일부를 고른다면, 연결 정도는 더욱 단단해진다.

Repositories


연관성(associations)을 통해서 다른 것과의 관계를 기반으로 객체를 찾을 수 있다. 그러나 생명 주기(life cycle) 중간에 ENTITY나 VALUE를 가로지르는 출발점이 있어야 한다.
객체를 통해서 무엇인지를 하기 위해서는 객체에 대한 참조를 가지고 있어야 한다.
그 참조를 어떻게 얻을 수 있는가? 한가지 방법은 생성 작업이 새로운 객체에 대한 참조를 반환하기 때문에 객체를 생성하는 것이다.
두번째는 연결성(association)을 횡단(traverse)하는 것이다. 이미 알고 있는 한가지 객체에서 시작하여 연관된 객체를 요구한다.
세번째 방법은 관계형 데이터베이스(RDBMS)를 이용하는 것이다. 쿼리를 통해서 데이터베이스에 있는 속성 기반으로 객체를 찾을 수 있다.


클라이언트는 기존 도메인 객체에 대한 참조를 얻는 실제적인 수단이 필요하다. 만약 인프라스트럭처를 통해서 쉽게 구현할 수 있다면, 클라이언트 개발자는 모델을 진흙탕으로 만들면서 더 많은 연관성(association)을 추가할 것이다. 다른 한편으로는 데이터베이스에서 필요한 정확한 데이터를 가져오기 위해서 쿼리를 사용하거나, Aggregate Root를 탐색하기보다는 일부 특정 개체를 가져오기 위해서 쿼리를 사용할 수 있다. 도메인 로직은 쿼리와 클라이언트 코드로 이동하고, ENTITIES와 VALUE OBJECTS는 단지 데이터 컨테이너가 된다. 대부분의 데이터베이스가 인프라스트럭처에 접근하기 위해 적용하는 기술적인 복잡성으로 인해 클라이언트 코드가 빠르게 변하기 때문에, 개발자들은 도메인 레이어를 바보 취급하고 모델과는 아무런 상관이 없게 된다.

영속하는 객체(Persistent Object)는 객체의 속성에 기반한 검색을 통해서 전역으로 접근가능해야 한다.
그러한 접근은 횡단을 통해서 도달하기 쉽지 않은 Aggregate의 Root에 필요하다.
일반적으로는 ENTITIES이고, 때때로 복잡한 내부 구조를 가지고 있는 VALUE OBJECTS이고, 가끔은 열거된 값을 가지는 VALUES이다.
다른 객체에 대한 접근을 제공하면, 중요한 차이점이 없어진다.
자유로운 데이터베이스 쿼리는 실제로 도메인 객체와 Aggregate 캡슐화를 위반할 수 있다.
기술적인 인프라스트럭처와 데이터베이스 접근 메커니즘에 대한 노출은 클라이언트를 복잡하게 만들고, MODEL-DRIVEN DESIGN을 모호하게 한다.

REPOSITORY는 개념적인 집합(보통 모방하여)으로 특정 타입에 대한 모든 객체를 표현한다. REPOSITORY는 더 정교한 쿼리 능력을 제외한 collection처럼 동작한다.
적절한 타입의 객체가 추가되거나 삭제되면, REPOSITORY 뒤에 있는 기계는 데이터베이스에 객체를 삽입하거나 삭제한다.
클라이언트는 일반적으로 특정 속성값인 클라이언트에 의해 지정된 규격에 기반한 객체를 선택하는 쿼리 메소드(query method)를 사용하여 REPOSITORY에 객체를 요청한다. REPOSITORY는 데이터베이스 쿼리와 메타데이터 맵핑에 대해 캡슐화된 기계작업에 의해 요청된 객체를 조회한다.
REPOSITORY는 클라이언트가 요청한 규격이 무엇이건 간에 그 규격에 기반한 객체를 선택하는 다양한 쿼리를 구현할 수 있다.
또한, 해당 규격을 만족시키는 인스턴스의 숫자와 같은 요약 정보도 제공할 수 있다. 특정 숫자 속성에 대해서 일치하는 객체의 전체 숫자와 같은 요약 계산 정보도 제공할 수 있다.

글로벌 액세스가 필요한 객체의 각 타입에 대해서 해당 모든 객체의 in-memory collection에 대한 환상을 제공할 수 있는 객체를 생성하라.
잘 알려진 글로벌 인스턴스를 통해서 액세스를 제공하라. 데이터 저장소에 실제로 데이터를 입력하거나 삭제하는 기능을 캡슐화한 객체를 추가/삭제할 수 있는 method를 제공하라. 어떤 규격에 따라 객체를 선택하는 method를 제공하고, 전체적으로 인스턴스화한 객체나 규격에 만족하는 속성 값을 가진 객체의 collection을 제공하는 method를 제공하여 실제 스토리지와 쿼리를 캡슐화하라. 직접적인 액세스가 필요한 Aggregate root에 대해서만 Repository를 제공하라. 모델에 기반한 클라이언트를 유지하고, Repository에 접근하도록 모든 객체 스토리지를 위임하라.

REPOSITORY 개념은 많은 상황에서 채택 가능하다. 구현 가능성은 다양하지만, 염두에 두어야 하는 몇 가지 항목은 다음과 같다.

- 타입을 추상화하라. REPOSITORY는 특정 타입의 모든 인스턴스를 "포함한다(contains)". 그러나, 각 클래스마다 하나의 REPOSITORY가 필요하다는 것을 의미하는 것은 아니다. 데이터베이스 기술의 다형성(polymorphism)에 대한 부족으로 인해 나타난 제약 조건들에 직면할 수 있다는 것을 염두에 두어야 한다.
- 클라이언트와의 분리에 대해 장점을 취하라. 클라이언트에서 직접적으로 호출하는 것에 비해서 REPOSITORY 구현을 더 자유롭게 변경할 수 있다. 다양한 쿼리 기술과 메모리에 객체를 캐싱하는 것, 영속 데이터 저장 전략을 언제라도 자유롭게 변경하여 성능에 대한 최적화를 할 수 있다.
- 클라이언트에 트랜잭션 컨트롤을 남겨 두라. 비록 REPOSITORY가 데이터베이스에 입력하고 삭제할지라도, 일반적으로는 어떤 것도 커밋(commit)하지 않는다. 클라이언트에서 컨텍스트를 완벽하게 초기화하고 작업 단위를 커밋한다.

The Relationship with FACTORIES

FACTORY는 객체의 생애(life)에 대한 시작(beginning)을 다룬다. REPOSITORY는 객체 생애의 중간과 마지막을 관리한다. 객체가 메모리에 있거나 Object Database에 저장되어 있을 때는 간단하다. 그러나 일반적으로 적어도 일부 객체는 RDB, File, Non object-oriented system에 저장된다. 그러한 경우에 데이터 탐색은 객체 형태로 재구성되어야 한다.

이 경우에 REPOSITORY는 데이터에 기반하여 객체를 생성하기 때문에 많은 사람들은, 사실 기술적인 관점에서, REPOSITORY를 FACTORY로 고려한다.
Domain-Driven Design관점에서 FACTORY와 REPOSITORY는 책임이 구별되어 있다. FACTORY는 새로운 객체를 생성하고, REPOSITORY는 이전의 객체를 찾는다.

받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://www.yongbi.net/rss/response/825