Chapter Five. A Model Expressed in Software
MODEL-DRIVEN DESIGN의 동력 손실 없이 구현을 하기 위해서는 기본에 대한 재구성이 필요하다.
모델과 구현을 연결하는 것은 상세한 수준에서 수행되어야 한다.
객체 사이의 연관성은 생각하고 그리고 쉽다. 하지만, 객체들을 구현하는 것은 잠재적인 난관이다.
연관성은 중대한 상세 구현 의사 결정이 MODEL-DRIVEN DESIGN의 실행 가능성과 관련하여 얼마나 중요한지를 보여준다.
하나의 패턴이나 다른 패턴을 명확하게 따르는 객체를 정의하는 것은 객체가 덜 모호하고, 견고한 설계를 위한 특정 선택을 할 수 있도록 길을 열어 준다. 그러면, 도메인 측면에서 객체로보다는 행동이나 동작으로 더 명확하게 표현된다.
객체 지향 모델링 전통(object-oriented modeling tradition)과는 약간 다르지만, 일부 Entity나 Value Object에 동작에 대한 책임을 강요하는 것보다 Service로 표현하는 것이 가장 좋다.
Service는 클라이언트 요청에 대해 수행되는 작업이다. 소프트웨어의 기술적인 레이어에는 많은 Service가 있다.
일부 활동(activity)이 소프트웨어가 수행해야만 하는 작업에 따라 모델링되었지만, 상태와 일치하지 않을 때도 또한 도메인에서 나타난다.
관계형 데이터베이스에 저장하는 것과 같이, 객체 모델의 순수성을 파괴해야만 하는 피할 수 없는 상황도 있다.
이런 지저분한 상황을 다루어야 할 때, 코스에 머무르기 위한 몇 가지 가이드라인이 있다.
마지막으로, Module에 대한 논의는 모든 설계 의사 결정이 도메인에 대한 통찰력에 의해 동기부여가 되어야 한다는 점을 납득하게 만들 것이다.
종종 기술적 측정 기준으로 생각되는 높은 응집력과 낮은 결합에 대한 아이디어는 개념 그 자체에 적용될 수 있다.
MODEL-DRIVEN DESIGN에서 Module은 모델의 일부이고, 도메인 내 개념을 반영해야만 한다.
Associations
모델링과 구현 사이의 상호 작용은 객체간의 연관성 때문에 특히 까다롭다.
모델에서 탐색 가능한 모든 연관성에 대해서 동일한 특성을 갖는 소프트웨어어 메커니즘이 있다.
고객과 영업 담당자간의 연관성을 보여주는 모델이 두 가지에 해당한다.
한편으로는 두 사람 사이에 관련있다고 여겨지는 개발자들의 관계를 추상화한다.
다른 한편으로는 두 Java 객체 사이의 객체 포인터나 데이터베이스 조회의 추상화, 또는 일부 유사한 구현에 해당한다.
가능한 관계를 제한하는 것이 중요하다.
양방향 연관성은 두 객체가 같이 있을 때만 이해될 수 있음을 의미한다.
Entities (a.k.a. Reference Objects)
소프트웨어 시스템에서 잘못붙여진 Identity의 경우에 데이터 손상 및 프로그램 오류를 야기시킨다.
여기에 특별한 기술적인 문제들이 있지만, 기본적인 몇 가지 이슈만 살펴보면 다음과 같다.
많은 것들이 속성이 아니라 Identity로 정의되어 있다.
보통 사람은 출생에서 죽음과 그 이상으로 확대되는 Identity를 가지고 있다.
그 사람의 육체적인 속성은 변형되고, 궁극적으로 사라진다.
이름은 변경되고, 재정적인 관계는 왔다 갔다 할 것이다.
변경할 수 없는 개인의 단일 속성은 없다.
하지만, Identity는 지속된다.
나는 5살 때와 동일한 사람인가?
이러한 종류의 형이상학적 질문은 효과적인 도메인 모델을 찾는데 중요하다.
잠깐 재언급해보자 : 만약 내가 5살 때와 동일한 사람이라면, 어플리케이션 사용자가 주의를 기울이는가?
은행 어플리케이션에서 트랜잭션을 고려해 보자.
동일한 날에 동일한 계좌로 동일한 금액을 두번 입금하는 경우에도 트랜잭션을 구분할 수 있고, Identity를 가진 Entity이다.
반면에 두 거래 금액 속성은 아마도 일부 돈 객체의 인스턴스이다. 이러한 값들은 구분하는데 유용하지 않으므로 Identity를 가지지 않는다.
사실, 두 객체는 동일한 속성을 가지지 않아도 동일한 Identity를 가질 수 있다. 또는 반드시 동일한 클래스에 속해야 할 수 있다. 은행 고객이 은행 입출금내역서의 거래를 수표 레지스트리의 거래와 일치시킬 때, 심지어 다른 사람에 의해 다른 날짜에 기록되었을지라도, 특히 동일한 Indentity를 가진 거래와 일치해야 한다.
수표 번호의 목적은 문제가 컴퓨터 프로그램으로 처리되든, 사람 손으로 처리되든 관계없이 이러한 목적으로 유일한 identifier를 제공하는 것이다.
객체가 속성이 아닌 Identity로 구분될 때, 모델 내 그것을 최우선으로 정의한다.
클래스의 정의를 단순하게 유지하고, life cycle의 연속성과 identity에 중점을 둔다.
양식이나 기록에 상관없이 각 객체를 구별하는 방법을 정의한다.
속성과 일치하는 객체를 호출하는 요구사항에 대해서 주의하라.
각 객체에 대한 유일한 결과를 생성하는 것을 보장하는 동작을 정의한다.
고유하게 보장되는 심볼을 추가할 수도 있다.
이 식별 방법은 외부에서 나올 수도 있고, 시스템에서 생성된 임의의 식별자일수도 있지만, 모델의 ID구분과 일치해야 한다.
모델은 같은 것을 의미하는 것을 정의해야만 한다.
Modeling ENTITIES
객체를 모델링할 때, 속성에 대해 생각하는 것이 자연스럽고, 그 동작에 대해 생각하는 것이 매우 즁요하다.
그러나 ENTITIES의 가장 기본적인 책임은 행동이 명확하고 예측 가능하도록 연속성을 확립하는 것이다.
속성이나 동작에 초점을 맞추기보다 ENTITIES 객체의 정의를 추려내서 시스템 내 유일하게 되도록 제한한다.
객체의 속성으로 이루어진 참된 유일한 키가 없을 경우, 또다른 일반적인 해결책은 클래스 내 유일한 심볼(숫자나 문자열과 같은)을 각 인스턴스에 부여하는 것이다.
이 ID 심볼이 한번 생성되어 ENTITY 속성으로 저장되면, 불변으로 지정된다.
심지어 개발 시스템이 이 규칙을 직접 싱행할 수 없는 경우일지라도 결코 변경하지 않아야 한다.
예를 들면, ID 속성은 객체가 데이터베이스로 병합되고, 재구성될 때 보존된다.
때때로 기술적인 Framework는 이 과정에 도움이 되지만, 그렇지 않은 경우에 엔지니어링 규칙이 적용된다.
종종 ID는 시스템에 의해 자동으로 생성된다. 생성 알고리즘은 시스템 내에서 유일함을 보장해야 한다.
동시 처리와 분산 시스템에서 문제가 될 수 있기 때문이다.
여기서 목표는 개발자가 문제를 해결하고 핵심 영역에 대한 우려를 어떻게 좁히는지를 알도록 고려할 부분을 지적하는 것이다.
핵심은 ID가 모델의 특정 측면에 돌쩌귀와 관련되어 있음을 인식하는 것이다. 종종 Identification의 수단은 도메인에 대한 주의깊은 연구를 요구한다.
ID가 자동으로 생성되면, 사용자는 그것을 결코 볼 필요가 없다. 사람 이름으로 기록을 찾도록 하는 연락처 관리 어플리케이션에서처럼, ID는 내부적으로만 필요하다. 프로그램에서는 정확하게 동일한 이름을 가진 2명의 연락처를 간단하고 분명한 방법으로 구분할 수 있어야 한다.
유일한 내부 ID를 통해서 시스템은 이를 수행할 수 있다.
2개의 서로 다른 아이템을 검색한 후, 시스템은 사용자에게 2개의 개별 연락처를 보여줄 것이다. 그러나 ID는 보이지 않는다. 사용자는 회사, 위치, 등등을 기준으로 구분할 수 있다.
마지막으로, 생성된 ID가 사용자에게 중요한 경우도 있다.
소포 배달 서비스를 이용해 패키지를 발송할 때, 회사의 소프트웨어에서 만들어진 Tracking Number(추적 번호)가 제공되고, 패키지를 추적하고 식별하는데 사용할 수 있다.
항공권 티켓과 호텔 예약할 때, 거래에 대한 유일한 식별자인 Confirmation Number(확인 번호)가 제공된다.
어떤 경우에는 ID의 유일함이 컴퓨터 시스템 범위를 넘어서서 적용되어야만 하는 경우도 있다.
예를 들면, 의학적 기록이 서로 분리되어 있는 컴퓨터 시스템을 가지고 있는 2개의 병원 사이에 상호교환 된다면, 이상적으로는 각 시스템이 동일한 환자 ID를 사용하겠지만, 각자의 심볼을 생성하는 경우에는 어렵게 된다.
그러한 시스템들은 종종 다른 기관, 일반적으로 정부관계기관 에서 발행된 식별자를 사용한다.
미국에서는 Social Security Number(사회보장번호)를 사용하여 사람을 식별한다.
그러한 방법은 누구나 이용할 수 있는 방법이 아니다. 모든 사람이 Social Security Number를 가지고 있는 것도 아니고 (특히 아이들이나 미국에 거주하지 않는 사람), 많은 사람들이 사생활(privacy) 이유로 사용하는 것을 반대한다.
덜 비공식적인 상황(비디오 대여)에서 전화번호가 식별자로 사용된다. 그러나 전화번호는 공유될 수 있다. 변경될 수도 있다. 예전 번호가 심지어 다른 사람에게 주어질 수도 있다.
이러한 이유로, 특별하게 부여된 식별자가 종종 사용된다. (항공사의 상용 고객 번호와 같은). 그리고 전화번호나 Social Security number와 같은 다른 속성들은 일치하는지 확인하고 검증하는데 사용된다. 어떤 경우에는 어플리케이션이 외부 ID를 필요로 할 때, 시스템의 사용자는 유일한 ID를 제공해야할 책임이 있고 시스템은 예외 처리를 위한 적절한 도구를 제공해야 한다.
주어진 모든 기술적인 문제들을 감안할 때, 주어진 근본적 개념 문제를 놓치기 쉽다.
두개의 객체가 동일한 것이라는 것이 무엇을 의미하는가?
ID로 각 객체에 도장을 찍거나 2개의 인스턴스를 비교하는 동작을 작성하는 것은 쉽지만, 이러한 ID들이나 동작들이 도메인에서 의미있는 구분에 따르지 않는다면, 더 혼란스럽게 할 뿐이다.
Value Objects
많은 객체에는 개념적인 ID가 없다. 이러한 객체들은 어떤 것의 특징을 설명한다.
모델에서 가장 눈에 띄는 객체는 일반적으로 ENTITIES이고, 각 ENTITY의 Identity를 추적하는 것이 중요하기 때문에 모든 도메인 객체에 Identity를 할당하는 것을 고려하는 것이 좋다. 사실, 일부 Framework는 모든 객체에 유일한 ID를 할당한다.
시스템은 모든 추적에 대해 대처 가능해야 하고, 가능한 많은 성능 최적화가 배제된다.
의미있는 ID를 정의하고, 분산 시스템이나 데이터베이스 스토리지에서 객체를 주적할 수 있는 확실한 방법을 찾기 위해서 분석 작업이 요구된다.
마찬가지로 중요한 것은, 인위적인 ID를 취하는 것은 잘못된 것이라는 것이다. 그것은 모델을 혼란스럽게 하여 모든 객체를 같은 틀에 집어 넣는다.
ENTITIES의 Identity를 추적하는 것은 필수적이지만, 다른 객체에 Identity를 덧붙이면 시스템 성능을 훼손하고, 분석작업이 추가되고, 모든 객체가 동일하게 보이도록 하여 모델을 혼란스럽게 할 수 있다.
소프트웨어 설계는 복잡성과의 끊임없는 싸움이다. 단지 필요할 경우에만 특별한 취급이 적용되도록 구분을 해야한다.
Value Object는 ENTITIES에 대한 정보를 제공한다.
Designing VALUE OBJECTS
VALUE OBJECTS가 어떤 인스턴스인지는 신경쓰지 않는다. 이러한 제약이 없기 때문에 단순하게 설계하거나 성능을 최적화할 수 있는 설계 자유가 제공된다. 복사, 공유, 불변성에 대한 선택을 포함한다. VALUE의 구현이 변경가능하게 되어야 한다면, 공유되지 않도록 해야 한다. 공유 여부와는 상관없이, 가능하면 VALUE OBJECT를 변경할 수 없도록 설계하라.
VALUE OBJECT를 정의하고, 불변하도록 지정하는 것은 일반적으로 다음과 같은 규칙을 따르는 경우이다.
- 모델에서 불필요한 제약을 피하여 개발자가 순수하게 기술적 성능 튜닝을 자유롭게 하도록 한다.
- 필수적인 제약 조건들을 명시적으로 정의하여 개발자가 의미있는 동작을 변경하지 않고, 설계를 조정할 수 있다.
- 그러한 설계는 종종 특정 프로젝트에서 사용되는 기술에 매우 특효가 있다.
Examples
Tuning a Database with VALUE OBJECTS
가장 낮은 수준의 데이터베이스는 디스크의 물리적 위치에 데이터를 위치시켜야 하고, 물리적인 부품이 이동하고, 데이터를 읽는데 시간이 걸린다.
정교한 데이터베이스는 이러한 물리적 주소를 클러스터링하여 하나의 물리적 작업으로 디스크로부터 연관된 데이터를 가져올 수 있다.
객체가 많은 다른 객체들로부터 참조된다면, 그 중 일부는 가까이 위치하지 않을 것이다(동일 페이지 상에서). 따라서 데이터를 가져오는데 추가적인 물리적 작업이 필요하다.
동일 인스턴스로 참조를 공유하는 것보다 복사본을 만들어서 많은 Entity의 속성으로 동작하는 VALUE OBJECT를, 그 Value Object를 사용하는 각 Entity와 동일한 페이지에 저장할 수 있다.
동일한 데이터를 여러 번 복사하여 저장하는 기법은 비정규화(denormalization)라고 하고, 종종 접근 시간(access time)이 스토리지 공간이나 유지보수의 단순성보다 더 중요할 때 사용된다.
별도의 테이블에 대한 연결(association)을 만드는 대신에, 관계형 데이터베이스에서 특정 Value를 Entity를 소유한 테이블에 넣을 수 있다.
분산 시스템에서는 다른 서버에 있는 Value Object에 대한 참조를 보유하는 것은, 메시지에 대한 응답 속도를 느리게 할 것이다.
대신에, 전체 객체의 복사본을 다른 서버에 넘겨야 한다.
Value Object를 다루기 때문에 자유롭게 복사본을 만들 수 있다.
Services
어떤 경우에는, 가장 명확하고 실용적인 설계는 개념적으로 어떤 객체에도 속하지 않는 동작을 포함한다.
문제를 강요하기 보다는, 문제 공간의 자연스러운 윤곽을 따라갈 수 있고, 모델에 명시적으로 SERVICES를 포함할 수 있다.
ENTITY나 VALUE OBJECT에서 자연스러운 Home(집)을 찾을 수 없는 중요한 도메인 동작이 있다.
이 중 일부는 사물이 아니라 본질적으로는 활동(activity)이나 행동(action)이지만, 모델링 패러다임이 객체이기 때문에 어쨌거나 객체에 적합하게 만들려고 할 것이다.
이제 더 많은 공통적인 실수는 행동을 적절한 객체에 맞추는 것을 너무 쉽게 포기하고 절차적인 프로그래밍을 향해서 미끄러져 들어가는 것이다.
객체의 정의에 어울리지 않는 객체로 강제로 작업을 할 때, 객체는 개념적인 명확성을 잃어버리고 이해하기 어렵게 되거나 리팩토링된다.
복잡한 작업은 단순한 객체의 역할을 애매하게 감추어서 쉽게 수렁에 빠질 수 있다.
이러한 작업들은 종종 많은 도메인 객체와 함께, 객체를 조정하고, 객체가 실행되도록 하기 때문에 추가된 책임은 모든 객체에 의존성을 만들어 내고, 독립적으로 이해할 수 있는 개념을 얽히게 한다.
때때로 서비스는 모델 객체로 가장하고, 어떤 동작을 하는 것 이상의 의미는 없는 객체로 나타난다.
이 "행위자(doers)"는 "Manager"와 같은 이름으로 끝난다.
그것들은 호스트하는 작업을 넘어서 도메인 안에서 어떤 의미를 가지지 않고, 상태도 가지지 않는다.
여전히 최소한 이런 솔루션은 실제 모델 객체를 엄망으로 만들지 않고 이러한 구분되는 행동을 home(집)에 제공한다.
도메인의 일부 개념은 객체로 모델링하는 것이 자연스럽지 않다.
요구되는 도메인 기능을 Entity나 Value의 책임으로 강제하는 것은 모델 기반 객체(model-based object)의 정의를 왜곡시키거나, 의미없는 인공적인 객체들이 추가된다.
Service는 Entity나 Value Object가 하는 것처럼 상태를 캡슐화하지 않고, 모델에서 단독으로 동작하는 인터페이스로서 제공되는 작업이다.
Services는 기술적인 Framework에서 공통적인 패턴이다. 하지만, 도메인 레이어에도 적용될 수 있다.
Name Service는 다른 객체들과의 관계를 강조한다. Entities와 Value Object와는 다르게, Service는 클라이언트를 위해 무엇을 할 수 있는지에 대한 관점에서 정의된다.
Service는 Entity보다는 Activity(활동)을 위해서, 명사라기 보다는 동사로 이름지어지는 경향이 있다.
Service는 추상적이고 의도적인 정의를 가질 수 있다.
Service는 정의된 책임을 가져야 하고, 책임과 그것을 수행하는 인터페이스는 도메인 모델의 일부로 정의되어야 한다.
동작 이름은 Ubiquitous Language로부터 가져오거나 그것을 소개하는 이름이 되어야 한다.
파라미터와 결과는 도메인 객체여야 한다.
Service는 신중하게 사용되어야 한다. 그리고 모든 동작에 대해서 Entity와 Value Object를 제거할 수 없다.
훌륭한 서비스는 3가지 특징을 가지고 있다.
- 동작은 Entity나 Value Object의 자연스런 일부가 아니라 도메인 개념과 관련되어 있다.
- 인터페이스는 도메인 모델의 다른 요소들로 정의되어 있다.
- 동작은 상태를 관리하지 않는다. (operation is stateless)
상태를 관리하지 않는다는 것은, 클라이언트에서 특정 Service 인스턴스의 개별적인 히스토리에 대해 고려하지 않고, Service의 인스턴스를 사용할 수 있다는 것을 의미한다.
도메인에서 중요한 프로세스나 변환이 Entity나 Value Object의 책임이 아닐 경우에, Service로 선언된 독립형 인처페이스로 모델에 작업을 추가하라.
모델의 언어로 인터페이스를 정의하고, 동작에 대한 이름이 Ubiquitous Language의 일부인지 확인하라.
Service를 Stateless로 만들어라.
SERVICES and the Isolated Domain Layer
(서비스와 격리된 도메인 레이어)
이 패턴은 도메인에서 그 자체적으로 중요한 의미를 갖는 Service에 중점을 두지만, 물론 Service가 도메인 레이어에서만 사용되는 것은 아니다.
도메인 레이어에 속한 서비스와 다른 레이어에 속한 서비스를 구별하고, 구별을 명확하게 하기 위해서 책임 요소를 고려해야 한다.
문자적으로 논의된 대부분의 Service는 순수하게 기술적이고 인프라 스트럭처 레이어에 속한다.
고메인과 어플리케이션 Service는 이러한 인프라스트럭처 Service와 협업한다.
어플리케이션 Service와 도메인 Service를 구별하는 것이 더 어려울 수 있다.
어플리케이션 레이어는 Notification의 순서에 대해서 책임을 지고, 도메인 레이어는 처리량(threshold)이 임계 값에 도달했는지 결정하는 것에 대해 책임을 진다.
많은 도메인이나 어플리케이션 Service는 Entities와 Values의 집단위에 구축되어 실제로 무엇인가를 얻고자 하는 도메인의 잠재력을 조직화하는 스크립트처럼 동작한다.
Entities와 Value Objects는 종종 너무 세분화되어 있어서 도메인 레이어의 기능에 편리한 접근을 제공한다.
여기서는 도메인 레이어와 어플리케이션 레이어 사이에 매우 미세한 라인이 있다.
예를 들면, 뱅킹 어플리케이션이 분석할 수 있는 스프레드시트 파일로 거래를 변환하여 내보낼 수 있다면, 해당 내보내기는 어플리케이션 Service이다.
뱅킹 도메인에서는 "파일 형식"은 의미가 없고, 포함된 비즈니스 규칙도 없다.
다른 한편으로는 하나의 계좌에서 다른 계좌로 돈을 옮기는 기능은 도메인 Service이다. 왜냐하면, 중요한 비즈니스 룰(rule)을 내포하고 있고 (예를 들면, 계좌와 관련된 신용거래와 직불 거래(credit and debit)), "현금 이체(fund transfer)"는 의미있는 뱅킹 용어이기 때문이다. 이경우에는 Service는 저절로 그렇게 된 것이 아니다. 2개의 계좌(account) 객체에 대부분의 일을 하도록 요구했을 것이다. 하지만, 계좌 객체에 "이체(transfer)" 동작을 하도록 하는 것은 그 동작이 2개의 계좌와 일부 글로벌 규칙을 포함하고 있기 때문에 처리하기 곤란하다.
아마도 2개의 Entry에 Rule과 이체 이력을 더하여 나타내기 위해서 현금 이체(Fund Transfer) 객체를 만들고 싶어할지도 모른다. 그러나, 여전히 은행간 네트워크에서 Service를 호출하는 부분이 남아 있다. 게다가 대부분의 개발 시스템에서 도메인 객체와 외부 리소스 사이의 직접적인 인터페이스를 하는 것은 곤란하다. 결과적으로 Fund Transfer 객체를 리턴하는 모델에 대한 용어에 입력하는 FACADE로 그러한 외부 Service를 변장시킬 수 있다.
하지만, 우리가 가지고 있는 중개자가 무엇이건 간에, 심지어 그 중개자가 우리에게 속해 있지 않다고 하더라도, 그러한 Service들은 계좌 이체에 대한 도메인 책임을 수행한다.
Granularity
(세분화)
비록 이 패턴 토론이 Service로 모델링하는 개념 표현력을 강조했지만, 클라이언트를 entity, Value Object로부터 분리하는(decoupling) 것뿐만 아니라, 도메인 레이어의 인터페이스에 대한 세분화를 컨트롤하는 수단으로도 가치가 있다.
중간 정도의 세분화(medium-grained), stateless Service는 단순한 인터페이스 뒤에 중요한 기능을 캡슐화하기 때문에 대형 시스템에서 재상용하기 더 쉽다.
작게 세분화된 객체는 분산 시스템에서 비효율적인 메시징을 초래할 수 있다.
앞서 논의한 것처럼, 잘 세분화된 도메인 객체는 도메인 객체의 동작이 조정된 어플리케이션 레이어 내 도메인으로부터 지식의 누수가 있을 수 있다.
매우 상세한 상호 작용의 복잡성은 어플리케이션 레이어에서 처리되기 때문에 도메인 지식이 어플리케이션이나 User 인터페이스 코드로 살며시 들어가서 도메인 레이어에서 손실 될 수 있다.
사려 깊은 도메인 서비스 도입은 레이어간 선명한 라인을 유지하는데 도움이 된다.
이런 패턴은 클라이언트 제어와 다재다능함보다 인터페이스 단순성을 선호한다.
대형 혹은 분산 시스템에서 컴포넌트 패키징에 매우 유용한 중간 정도의 세분화 기능을 제공한다.
때로 Service는 도메인 개념을 표현하는 가장 자연스러운 방법이다.
Access to SERVICES
J2EE와 CORBA와 같은 분산시스템 아키텍처는 Service 사용에 대한 협약과 분산 및 접근 기능을 추가한 Service에 대한 특별한 Publishing(출판) 메커니즘을 제공한다.
그러나 그러한 framework는 프로젝트에서 항상 사용되는 것은 아니다.
심지어 사용되더라도, 동기부여가 단순히 논리적인 관심사 분리(logical separation of concerns)일 때 지나치게 될 수 있다.
Service에 접근을 제공하는 수단은 특정 책임을 부각시키는 설계 결정만큼 중요하지는 않다.
"행위자(doer)" 객체는 Service의 인터페이스 구현으로 만족스러울 수 있다.
간단한 Singleton은 접근을 제공하기 위해 쉽게 작성될 수 있다.
코딩 규칙(Coding Conventions)은 이런 객체가 의미있는 도메인 객체가 아닌, Service 인터페이스에 대한 전달 메커니즘에 불과하다는 것을 명확하게 할 수 있다.
정교한 아키텍처는 분산 시스템에 정말로 필요하거나 framework의 기능을 활용할 필요가 있는 경우에만 사용해야 한다.
Modules
Module은 오래전에 만들어진 설계 요소이다. 기술적인 고려사항들이 있찌만, 인지 과부하가 모듈화의 주요 동기부여 사항이다. Module은 사람들에게 2가지 모델 관점을 제시한다.
- 전체에 압도당하지 않고, Module 내 세부 사항을 알 수 있다.
- 내부를 상세히 몰라도 Module 사이의 관계를 알 수 있다.
도메인 레이어에서 모듈은 더 커다른 스케일의 도메인을 이야기하는 모델의 의미있는 부분으로 나타난다.
누구나 모듈을 사용하지만, 모델의 완전한 자격을 갖춘 부분으로는 거의 활용하지 않는다.
코드는 기술적인 아키텍처 측면에서부터 개발자들의 작업 할당에 이르기까지 모든 종류의 범주(Category)로 분류된다.
심지어 리팩토링을 많이 하는 개발자들조차도 프로젝트 초기에 염두에 둔 Module을 사용하여 스스로 만족하는 경향이 있다.
Module 사이에 낮은 결합력과 높은 응집력이 있어야 한다는 것은 사실이다.
결합과 응집에 대한 설명은 연관성과 상호작용에 대한 분산에 기반하여 기계적으로 판단할 수 있는 기술적인 메트릭처럼 들리게 하는 경향이 있다.
그러나 그것은 단지 Module로 분류되는 코드(Code)가 아니라 개념이다.
사람이 한번에 얼마나 많이 생각할 수 있는지에 대해서는 한계가 있다. (따라서 낮은 커플링-결합-이 필요하다.)
아이디어의 일관적이지 않은 파편들은 구분되지 않고 섞여 있는 수프처럼 이해하기 어렵다. (따라서 높은 응집력이 필요하다.)
낮은 결합과 높은 응집력은 개별 객체 못지 않게 모듈에도 적용하는 일반적인 설계 원칙이다. 하지만, 이러한 더 큰 단위의 모델링이나 설계에 중요하다.
모듈 사이의 낮은 결합은 비용을 최소화하고, 다른 모듈들과 상호 동작하는 참조를 최소화하여 하나의 모듈에 대한 내용을 분석가능하게 한다.
동시에, 훌륭한 모델 요소는 시너지를 내고, 잘 선택된 모듈은 특히 풍부한 개념적 관계로 모델 요소들을 묶는다.
연관된 책임들을 가진 객체에 대한 이러한 높은 응집력은 복잡성의 규모를 가진 사람의 마음을 쉽게 다룰 수 있게 하여 단일 모듈 내 전념하기 위한 모델링과 설계 작업을 가능하게 한다.
시스템의 이야기를 말하고 있 응집력 있는 개념을 포함하는고 Module을 선택하라.
이것은 Module 사이의 낮은 결합을 야기하지만, 그렇지 않은 경우에는 개념을 풀기 위한 모델을 변경하는 방법을 찾거나, 혹은 의미 있는 방법으로 요소들을 함께 모으는 Module의 기반이 될 수 있는 보다 상위의 개념을 찾아야 한다.
서로 독립적으로 이해하고, 추론할 수 있는 개념적 의미에서 낮은 결합을 추구하라.
고수준(high-level) 도메인 개념에 따라 파티션할 때까지 모델을 수정(refine)하고 코드와 일치하는 것이 잘 분리된 것이다.
Module 이름은 Ubiquitous Language의 일부분이 되게 하고, Module과 그 이름은 도메인의 통찰력을 반영해야 한다.
Agile MODULES
Module은 모델의 나머지와 함께 상호 진화해야 한다.
이것은 모델과 코드와 함께 Module을 올바르게 리팩토링하는 것을 의미한다.
하지만, 이러한 리팩토링은 자주 일어나지는 않는다.
Module을 변경하는 것은 광범위한 코드 업데이트를 필요로 하는 경향이 있다.
그러한 변경은 팀 커뮤니케이션에 지장을 줄 수 있고, 소스 코드 관리 시스템과 같은 개발 도구에 장애물을 던질 수 있다.
결과적으로, Module 구조와 이름은 종종 클래스가 수행하는 것보다 훨씬 더 초기의 모델 형태를 반영하게 된다.
The Pitfalls of Infrastructure-Driven Packaging
(인프라 스트럭처 기반 패키징의 함정)
기술 환경에 필수적이거나 실제적으로 개발을 도와주는 최소한의 기술 분할 규칙(technical partitioning rules)을 선택하라.
예를 들어, 복잡한 데이터 지속성 코드(persistence code)를 객체의 동작 측면과 분리하는 것은 리팩토링을 쉽게 할 수 있다.
다른 서버에 코드를 분산하기 위한 실제적인 의도가 없다면, 동일 객체가 아니라면, 모든 코드를 동일한 Module내의 동일한 개념적인 객체에 구현하라.
패키징을 사용하여 도메인 레이어를 다른 코드에서 분리하라.
그렇지 않으면, 모델과 디자인을 선택하는 것을 지원하는 방식으로 도메인 개발자가 도메인 객채를 패키징하도록 가능한 한 많은 자유를 남겨 두라.
모듈화는 설계가 더 커지고 복잡해짐에 따라 더 중요해진다.
도메인 모델의 각 개념은 구현 요소에 반영되어야 한다.
일부 도메인 Service와 구성 Module과 함께 entity, Value Object, 연관성은 구현과 모델 사이에서 직접적인 대응 지점이다.
구현에서 객체, 포인터, 검색 메커니즘은 직접적으로, 분명하게 모델 요소와 매핑되어야 한다.
만약 그렇지 않으면, 코드를 정리하고 다시 돌아가 모델을 변경하거나 둘 모두 수행해야 한다.
추가하려는 항목이 나타내는 개념과 밀접하게 연관이 없는 도메인 객체에 어떤 것을 추가하는 유혹에 저항하라.
이러한 설계 요소는 해야 할 일이 있다. - 모델을 표현해야 한다.
수행되어야 하는 다른 도메인 관련 책임과 시스템 작업에 필요한 관리되어야 하는 다른 데이터가 있지만, 그것들은 이러한 객체에 해당하지 않는다.
Modeling Paradigms
Why the Object Paradigm Predominates
(왜 객체 패러다임이 먼저 자리잡았는가?)
팀이 객체 패러다임을 선택한 많은 이유는 기술적인 것이 아니다. 심지어 객체의 고유한 가치 때문도 아니다.
하지만, 문밖에서 객체 모델링이 단순함과 정교함의 훌륭한 균형을 이루고 있기 대문이다.
만약 모델링 패러다임이 너무 수수께끼 같다면, 개발자가 그것을 마스터하기 충분하지 않기 때문에 잘못 사용할 것이다.
팀의 비기술적ㅇ니 구성원이 최소한 패러다임의 기초를 파악하지 못한다면, 모델을 이해하지 못하고 Ubiquitous Language가 없어진다.
객체 지향 설계(Object-Oriented Design)의 기본은 대부분의 사람들에게 자연스럽게 나타난다.
일부 개발자가 모델링의 미묘함을 놓치지만, 심지어 비기술자조차도 객체 모델에 대한 다이어그램을 따를 수 있다.
Nonobjects in an Object World
(객체 세상에서의 비 객체)
도메인 모델이 객체 모델이 되어야만 하는 것은 아니다. 예를 들어, 프롤로그에서 구현된 Model-Driven Designs에서 논리적인 규칙과 사실로 이루어진 모델도 있다.
모델 패러다임은 사람들이 도메인에 대해 생각하기 좋아하는 특정 방법을 다루기 위해서 고안되었다.
프로젝트에서 지배적인 모델 패러다임이 무엇이건 간에, 다른 패러다임에서 훨씬 더 쉽게 표현할 수 있는 도메인의 일부 영역이 될 수 있다.
댓글을 달아 주세요
댓글 RSS 주소 : http://www.yongbi.net/rss/comment/894