Factories

Entity와 Aggregate는 보통 생성자에서 만들기는 너무 복잡해질 정도로 커지는 경향이 있다.

보통 객체를 요구하는 쪽에서 여러 인자들과 함께 생성자를 호출한다. 이러면 객체를 생성할 때 너무 많은 정보들을 알게 된다. 즉, 객체에 대한 클라이언트들이 객체 생성에 관한 정보를 알게 되는 것이고, 이 것은 도메인 객체와 Aggregate에 대한 Encapsulation을 깨트린다.

실제에서도 플라스틱, 고무, 철, 실리콘 등으로 프린터를 직접 만들진 않는다. 이런 복잡한 것들을 포함시키면 이해하기 어려운 설계가 된다. 따라서 복잡한 객체 생성 과정을 캡슐화하는 새로운 개념이 필요하다. 그게 바로 Factory다.

객체 생성 과정을 원자화Atomic하는 것은 중요하다. 그렇지 않으면, 만들다 만 객체를 생성할 수도 있다. 특히 Aggregate의 경우에 그렇다. 객체 생성이 실패하면, 예외를 발생시켜서 잘못된 값이 전달되지 않도록 해야한다.

따라서 복잡한 객체나 Aggregate 객체를 생성하는 책임을 별도의 객체로 위임하는 것이 좋다. 인터페이스를 제공하여 클라이언트가 구현 클래스에 직접 접근할 필요가 없도록 하라.

Factory와 관련된 GoF의 Design Pattern 중에 Factory Method 패턴과 Abstract Factory 패턴이 있다. 디자인 관점이 아니라 도메인 모델링 관점에서 살펴보겠다.

사용자 삽입 이미지참조 : http://en.wikipedia.org/wiki/Factory_method_pattern

Factory Method는 다른 객체를 생성하는데 필요로 하는 정보를 가지고 숨겨둔 객체 메소드이다. 이것은 클라이언트가 Aggregate에 속한 객체를 얻고 싶을 때 유용하다. Aggregate 루트에 객체 생성을 책임지는 메소드를 추가하면 된다.

사용자 삽입 이미지참조 : http://www.dofactory.com/Patterns/PatternAbstract.aspx

Abstract Factory는 객체 생성이 더 복잡하거나 객체 생성이 여러 개의 객체 생성을 포함하고 있는 경우에 사용한다. 예를 들어, Aggregate를 생성할 때, 그에 대한 책임을 별도의 Factory 객체에게 위임할 수 있다. Factory를 만드는 것은 객체 캡슐화를 위배하기 때문에 주의깊게 사용해야 한다.

Entity Factory와 Value Object Factory의 차이점. Value Object는 변하지 않는immutable 객체기 때문에, 한 번 만들어 지면 변하지 않아야 한다. Entity 는 immutable하지 않다. 매번 변하며, 식별성identity을 가지고 있다.

다음의 경우 Factory보다는 생성자를 사용하는게 좋다.

  • 객체 생성이 복잡하지 않을 때.
  • 객체 생성이 다른 객체 생성 과정을 포함하고 있지않으며, 오로지 인자로 넘어온 값들만 필요로 할 때.
  • 클라이언트가 구현 과정에 관심이 있을 때.
  • 계층구조가 없는 단순한 타입일 때. 어떤 구현체를 제공해야 할지 선택할 필요가 없기 때문에..

Entity 객체를 가져올 때 주의해야 할 것은 DB의 identity와 객체의 identity를 맞춰주어야 한다는 것이다.

Aggregates

Aggregate는 객체의 소속Ownership과 경계Boundary를 정의하기 위해 사용하는 도메인 패턴이다.

모델은 많은 도메인 객체들을 포함하고 있을 것이며, 이 도메인 객체들은 서로 거미줄처럼 연결되어 있을 것이다. 실제 도메인에서의 연관 관계는 코드와 DB에도 그대로 반영되어야 한다. 마치 한 사람의 고객이 계좌 하나를 가지고 있을 때 Customer와 Account 테이블처럼 말이다.

모델이 복잡해지는 것을 막기 위해서 최대한 간단하고 이해 가능한 수준으로 작성하려 할 것이다. 그러기 위해서 많은 시간을 모델들 사이의 관계를 제거하거나 단순화 하는데 소비한다. 하지만 1:1, 1:다, 다:다 등의 관계를 단순화하는 작업이 그리 간단하지 않은 것 같다.

Invariant도 마찬가지로 신경써야한다. Invariant는 데이터가 변경되더라도 유지되어야 할 규칙을 이야기한다. 하지만 복잡한 관계에서 변경은 도메인 객체에 해를 끼칠 수도 있다. 그래서 신중한 롹킹으로 여러 사용자들이 데이터를 동시에 사용할 수 있도록 한다.

그래서 Aggregate가 필요하다.

사용자 삽입 이미지
Aggregate는 데이터 변경에 영향을 받는 객체들을 하나의 유닛으로 묶어놓은 그룹이다. Root에 해당하는 Entity 객체를 통해서 외부와 내부의 객체들 사이를 경계짓고 있다. 경계 내부에 또 다른 Entity가 있을 때 해당 Entity의 identity는 aggregate 내부에서만 통용되는 지역적인 identity이다.

이렇게 되어있을 때 무경성을 지키는것이 훨씬 편하다. 외부에서 내부에 있는 객체들에 바로 접근할 수 없으며, 최상위 Entity 객체를 통해서 조작되기 때문이다.

또한 내부의 객체에 대한 복사본을 외부로 전달하는 것이 가능하다. 이렇게 되면, 외부에서 해당 객체를 지지고 볶아서 구워 삶아 먹든 별 문제가 없을 것이다.

내부의 객체들은 외부에 있는 다른 Aggregate의 Entity를 참조할 수 있다.
최상위 Entity는 Gloval Identity를 가지고 있다.

Modules

크고 복잡한 애플리케이션에서 모델은 갈수록 커지는 경향이 있다. 그러다가어느 시점에 도달하면 더이상 전체를 파악하기도 힘들고 그들의 관계를 이해할 수도 없을 만큼 복잡해진다.

모듈은 관련된 개념과 작업들을 묶어서 복잡도Complexity를 줄이기 위해 사용한다.

모듈을 사용하는 또 다른 이유는 코드의 품질과 관련이 있다. 높은 응집도Cohesion와 낮은 결합도Coupling을 유지하는 코드가 좋은 코드다.  모듈을 사용하면 응집도를 높이고 결함도를 떨어트린다. 왜냐면 모듈화 함으로써 기능적으로나 논리적으로 관련된 녀석들을 묶어주기 때문에 응집도는 높아지며, 모듈들은 모두 잘 정의된 인터페이스를 가지고 있기 떄문에 결함도를 낮춰준다.

사용자 삽입 이미지
모듈 이름은 유비쿼터스 언어의 일부에서 발췌해야 한다.

모듈을 한 번 만들고 그 역할을 정해두면, 그 내부에 많은 변화가 생기더라도 보통 모듈을 자주 변경하지 않는다. 하지만 프로젝트를 진행하면서 모듈을 얼려두지 말고 진화시키는 것을 권장한다. 모듈을 리팩토링하는 비용이 엄청나다는 것을 알지만 그래도 모듈을 유지하기 위해 돌아가는 방법을 선택하는 것 보다는 모듈을 변경하는게 좋다.

Services

DDD Quickly 읽다가 정리합니다.

유비쿼터스 언어를 조사하다 보면 명사는 객체로 동사는 객체의 메소드로 연관시키는 경우가 많다. 그러나 그 중 몇몇은 특정 도메인 객체 어디에도 속하지 않는듯한 동사가 있다. 이러한 것들을 서비스로 등록하라.

예를들어, 예금이체 같은 동사는 Account 라는 두 개의 객체 중 보내는 객체 쪽에 둬야 할까 받는 쪽에 둬야 할까?

서비스는 절대로 도메인 객체에 지녀야 할 행위를 가지고 있어서는 안 된다.

1. 서비스에 의해 수행되는 작업은 Entity나 Value Object에 속하지 않는 도메인 컨셉이다.
2. 수행되는 작업은 도메인에 있는 다른 객체를 참조한다.
3. 작업은 상태 정보를 유지하지 않는다. Stateless

서비스를 사용하면서도 도메인 계층을 독립시키는 것이 중요하다.

사용자 삽입 이미지
서비스는 위 그림처럼 별도의 계층으로 존재하는 것이 아니라, 도메인 객체들과 연관된 서비스라면 도메인 계층에 두고, 애플리케이션 계층과 관련이 있다면 애플리케이션 계층에 둔다고 합니다. 그래서 서비스를 대체 어디에 두어야 할지 결정하는 일이 좀 어렵다고 나와있네요.

예금이체 같은 경우는 도메인 계층에 둘 것이고, 도메인과 관련되어 있진 않지만 애플리케이션에 꼭 필요한 역할…트랜잭션이나 로깅 같은 것들은 애플리케이션 계층에 두면 되겠군요. 이런 것들은 근데 Aspect로 처리하면 될 것 같고 흠.. 폼에 입력된 데이터를 검증할 Validator 같은 녀석들은 어떨까요. 이런것도 애플리케이션 계층에 속하겠죠?

내일은 Module…

DAO vs Repository

http://aeternum.egloos.com/1160846 이 글을 참조하세요. 아래 내용은 권해드리지 않는 내용입니다.

나도 DDD 하고싶어 모델링 적용기
Repository Pattern vs. Transparent Persistence

위 두 개의 글의 공통점은 DAO와 Repository의 차이에 대한 고민입니다. 저도 잘 모릅니다. 저는 사실 찬욱군이 쓴 글에 있는 링크를 따라가보지도 않았으며, 얇은 DDD책(DDD Quickly)도 다 읽지 못 했고, 그나마 읽었던 부분들도 잊혀져가고 있습니다.(다시 읽으려고 마음먹었습니다.)

그래도.. 나름대로 생각해 봤습니다. 어차피 작명의 문제이고 누군가 어떤 의도를 가지고 작명을 했을테니 그 뜻은 분명 단어 속에 숨어있겠죠.

DAO는 Data Access Object의 약자로 데이터(베이스)에 접근하는 객체를 나타냅니다. DB 접근하는 일을 분리해 냈습니다. SRP 객체지향 원칙의 사례로 등장했었던 적이 있었는데 어떤 글인지는 모르겠습니다.

Repository는 저장소입니다. 어떤 객체들을 저장하는 장소를 뜻할 것이며, 아마도 DB 자체를 또는 Table 자체 또는 ‘영속성이 보장되는 저장소’를 객체화한 표현이 아닐까 생각해봅니다. 이것이 필요한 객체는 바로 도메인 객체겠죠.

이정도 정보를 가지고 이 둘의 차이를 살펴보기엔 정말 막연합니다. 그래서 Finder라는 녀석까지 같이 보겠습니다. Finder는 findByXXX 류의 메소드들을 가진 클레스로 이 녀석 역시 DB나 영속성을 보장하는 어떠한 저장소에 접근을 합니다.

기존의 DAO에는 Finder가 하는 일까지 같이 포함하고 있었습니다. 그리고 DDD를 구현한(건지.. 구현 하도록 돕는 건지.. Anyway) ROO 라는 프레임워크에서는 Repository와 Finder로 분리해 두었습니다. 조금 더 세밀하게 SRP를 적용했다고 볼 수 있습니다.

따라서 저는 이렇게 결론을 냈습니다.

사용자 삽입 이미지
그럼 DAO와 Finder를 동시에 두면 어떻게 되는걸까? 라는 생각을 해볼 수 있는데요. DAO는 위에서도 언급했다시피, 데이터에 접근하는 객체입니다. Finder는 무언가를 찾아주는 녀석이죠. 그럼 무언가를 찾아주는 녀석이 매번 데이터에 접근해서 찾아야 하는데 결국 Finder도 DAO라고 볼 수 있지 않을까요? 그럴 때는 가능하다면, 기존의 DAO 이름을 Repository로 변경하는 것이… 좋을 것 같다는 생각을 해봅니다.