2016-07-02 3 views
0

나는 DDD를 연습하고, 나는이 현재처럼 보이는 아주 간단한 예를 가지고 :저장소가 DDD로 다른 저장소에 액세스 할 수 있습니까?

Polling 
    getEventBus() -> Bus 
    getEventStorage() -> Storage 
    getMemberRepository() -> MemberRepository 
    getCategoryRepository() -> CategoryRepository 
    getBrandRepository() -> BrandRepository 
    getModelRepository() -> ModelRepository 
    getVoteRepository() -> VoteRepository 

MemberRepository 
    MemberRepository(eventBus, eventStorage) 
    registerMember(id, uri) 
     -> MemberRegistered(id, uri, date) 
     -> MemberRegistrationFailed //when id or uri is not unique 
    isMemberWithIdRegistered(id) 
    isMemberWithUriRegistered(uri) 

CategoryRepository 
    CategoryRepository(eventBus, eventStorage) { 
    addCategory(id, name) 
     -> CategoryAdded(id, name, date) 
     -> CategoryAdditionFailed //when id or name is not unique 
    isCategoryWithIdAdded(id) 
    isCategoryWithNameAdded(name) 
}; 

BrandRepository 
    CategoryRepository(eventBus, eventStorage) { 
    addBrand(id, name) 
     -> BrandAdded(id, name, date) 
     -> BrandAdditionFailed //when id or name is not unique 
    isBrandWithIdAdded(id) 
    isBrandWithNameAdded(name) 
}; 

ModelRepository 
    ModelRepository(eventBus, eventStorage) 
    addModel(id, name, categoryId, brandId) 
     -> ModelAdded(id, name, categoryId, brandId, date) 
     -> ModelAdditionFailed //when id or name is not unique and when category or brand is not recognized 
    isModelWithIdAdded(id) 
    isModelWithNameAdded(name) 

VoteRepository 
    VoteRepository(eventBus, eventStorage) 
    addVote(memberId, modelId, vote, uri) 
     -> MemberVoted(memberId, modelId, vote, uri, date) 
     -> VoteFailed //when the member already voted on the actual model and when memberId or modelId is not recognized 

여기 폴링 시스템을 개발하고 싶습니다, 그래서 우리는이 폴링 도메인을 호출 할 수 있습니다 생각합니다. 회원, 카테고리, 브랜드, 모델 및 표가 있습니다. 각 회원은 모델에 한 번만 투표 할 수 있으며 각 모델에는 브랜드와 카테고리가 있습니다. 예를 들어 inf3rnoShoe : Mizuno - Wave Rider 1910으로 투표 할 수 있습니다. 왜냐하면 그는 실제로 그것을 좋아하기 때문입니다.

내 문제는

addModel(id, name, categoryId, brandId) 
    -> ModelAdded(id, name, categoryId, brandId, date) 
    -> ModelAdditionFailed //when id or name is not unique and when category or brand is not recognized 

로하고

addVote(memberId, modelId, vote, uri) 
    -> MemberVoted(memberId, modelId, vote, uri, date) 
    -> VoteFailed //when the member already voted on the actual model and when memberId or modelId is not recognized 

부품. ModelAddtion을 고집해 봅시다.

categoryId와 brandId가 유효한지 확인하려면 CategoryRepository.isCategoryWithIdAdded(categoryId)BrandRepository.isBrandWithIdAdded(brandId) 메서드를 호출해야합니다. ModelRepository에서이 방법에 액세스 할 수 있습니까? 컨테이너를 주입하고 getCategoryRepository() -> CategoryRepositorygetBrandRepository() -> BrandRepository 방법을 사용해야합니까? DDD에 의해 이것을 올바르게 해결하는 방법은 무엇입니까?

갱신 :

어떻게 당신이 정말로이 기능을하지 않았을 외래 키 제약하고 DB 엔진을 필요 했어 경우 도메인이 유효성 검사를 해결할 것인가?

답변

1

세련된 견해에서 두 개의 저장소에 액세스 할 필요가 없습니다. 도메인의 누락 부분이 이것을 단순화하는 데는 어느 정도 시간이 걸릴 수 있기 때문에 나는 순수하다고 말합니다.

내 머리의 상단에서

, 나는 다음과 같은 자신을 질문합니다 :

  • 당신이 그 실체가 존재하는지 확인해야합니까? (isCategoryWithIdAddedisBrandWithIdAdded). 저장소 엔진에 따라이를 적용 할 수 있습니까 (예 : 필수 외래 키). 나는 이것이 성능 측면에서 볼 때 더 빠르기 때문에 이것이 나의 접근 방식이라고 생각한다.
  • 해당 엔티티가 다른 곳에 존재하도록 할 수 있습니까? 일부 DDD 구현은 응용 프로그램 서비스가 호출되면 데이터가 올바른 것으로 가정합니다.
  • 마지막으로이 기능은 존재하지 않을지라도 2 가지를 연결할 수 있습니까? ('피해'는 무엇이겠습니까).

DDD의 어휘처럼 모델이라는 도메인에 뭔가있는 것이 너무 혼란 스럽습니다. = D

+0

그들 중 일부를 같은 것을 보일 것입니다 이미 존재하는 카테고리, 브랜드 또는 제품 (모델)을 누군가가 추가하려고 시도하면 복제로 이어질 수 있습니다. 인식되지 않은 회원 또는 제품 ID로 투표를 허용해도 시스템에 큰 영향을 미치지 않으며 투표 성능이 향상됩니다. 제품 추가도 마찬가지입니다. DB가 마이 그 레이션을 통해 코드가 손상 될 수 있기 때문에 스토리지 엔진에 불변성을 적용시키는 것이 좋은 방법이라고는 생각하지 않습니다. Btw. 우리는 현재 이벤트 저장소에 대해 이야기하고 있으므로 외래 키를 사용하는 것은 옵션이 아닙니다. – inf3rno

+0

정말 흥미로운 점은 CQRS를 사용하기 때문에 도메인 객체가 필요하지 않으며 아직 작성하고 읽을 필요가 있다는 것입니다. :-) 나는 리포지토리에 유효성 검사를 옮길 수있다. 나는 이것이 최선의 방법이 아니라는 것을 알고있다. :-) – inf3rno

+0

좋아요, 다른 관점으로 봅시다. 외래 키 제약이 정말로 필요하고 db 엔진에이 기능이 없다면 어떻게 도메인에서이 문제를 해결할 수 있습니까? :-) – inf3rno

3

컴퓨터 과학에 2 가지 어려운 문제가 있습니다. 캐시 무효화, 이름 지정, 오류 하나씩 해제, 따옴표 표시 ... .... 다시 들어올 것입니다. DDD 자체의 유비쿼터스 언어에서 사용되는

저장소는 일반적으로 여기에 표현하려고하는 것을 의미하지 않는다.

에릭 에반스 (Blue Book, 6 장)는 다음과 같이 썼습니다.

도메인 디자인을 복잡하게 만들 수있는 기술적 인 복잡성을 노출시키는 또 다른 전환은 저장소로의 전환과 저장소에서의 전환입니다. 이 전이는 다른 도메인 디자인 구조의 책임입니다. 저장소

아이디어는 클라이언트에서 내부 작업을 모두 숨겨서 클라이언트 코드가 데이터가 개체 데이터베이스에 저장되어 있는지 관계형 데이터베이스 또는 단순히 메모리에 보관됩니다.

즉, 저장소의 인터페이스는 지속성 구성 요소에 의해 구현되는 계약을 정의합니다.

MemberRepository 
    MemberRepository(eventBus, eventStorage) 
     registerMember(id, uri) 
     -> MemberRegistered(id, uri, date) 
     -> MemberRegistrationFailed //when id or uri is not unique 

다른 한편 도메인 모델을 수정 한 것 같습니다. "registerUser"는 도메인 이벤트처럼 보이는 MemberRegistered, MemberRegistrationFailed 명령의 의미를 가지고 있습니다.이 이벤트는 집합체이며 도메인 내의 특정 불변 조건을 보호하는 엔티티입니다.

집계 중 하나의 이름을 "저장소"는 모든 사람을 혼란스럽게 할 것입니다. 집계의 이름은 실제로 우리가 구현을 설명하기 위해 사용하는 패턴 언어가 아니라, 제한된 컨텍스트의 유비쿼터스 언어에서 가져와야합니다. 나는 카테고리 ID와 brandId가 유효한지 여부를 확인하려면

, 나는 CategoryRepository.isCategoryWithIdAdded(categoryId)BrandRepository.isBrandWithIdAdded(brandId) 메소드를 호출해야합니다. ModelRepository에서이 메소드에 액세스 할 수 있습니까?

CategoryRepository, BrandRepositoryModelRepository 모든 집계 것으로, 위와 같이 가정 할 때, 대답은 없다 아니, 아니,없고.

아니요 : 도메인을 올바르게 모델링 한 경우 변경 사항이 비즈니스 invariant와 일치하는지 확인하는 데 필요한 모든 상태가 변경되는 집계의 경계 내에 포함되어야합니다. 예를 들어, 스레드에 모델을 추가하는 것이 의미하는 반면 모델에 필요한 브랜드는 스레드에서 제거됩니다. 이들은 별도의 트랜잭션으로, 모델이 일관성 불변성을 유지할 수 없음을 의미합니다.

아니요 : 입력을 살균하여 오류 발생을 줄이기위한 동기가 있다면 논리는 실제로 도메인 모델이 아닌 응용 프로그램 구성 요소에 속합니다. 명령 매개 변수가 모델 상태에 대한 유효한 변경을 유도하도록하는 것은 도메인 모델의 책임입니다. 올바른 매개 변수가 전달되는지 확인하는 것은 응용 프로그램의 책임입니다. 너티 검사는

없음 말했다 도메인 모델

외부에 속하는 : 집계 서로 직접 액세스하지 않아야하는 도메인 모델; 집계를 전달하는 대신 도메인 모델에서 실행해야하는 쿼리를 나타내는 도메인 서비스를 전달합니다.

Model.addModel(brandId, brandLookupService) { 
    if (brandLookupService.isValid(brandId)) { 
     // ... 
    } 
} 

간접이 추가 비트 집합이 주어진 트랜잭션 내에서 변경되고있는 대한 모호성을 제거합니다. 커버 아래에있는 BrandLookupService 자체는 BrandRepository에서 브랜드의 읽기 전용 표현을로드 할 수 있습니다.

물론 모델이 브랜드를 참조하는 경우에도 브랜드가 변경 될 수 있다는 우려는 아직 해결되지 않았습니다. 즉, 트랜잭션 경계가 그려지므로이 디자인에는 잠재적 인 데이터 경쟁이 있습니다.

외래 키 제약 조건이 필요하고 db 엔진에이 기능이 없다면 도메인에서이 유효성 검사를 어떻게 해결할 수 있습니까?

두 가지 옵션 :

1) 집계 경계를 다시 그립니다.

도메인 모델에서 적용되는 외래 키 제약 조건이 필요하다면 "외래"키가 아닙니다. 두 비트의 상태를 모두 포함하는 집계에 대한 로컬 키.

2) 요구 사항

우디 다한 변경, 내가 this talk 생각은 때때로 길 사업이 (현재) 단순히 제대로 확장되지 않는 실행하고 사업 자체가해야 할 수도 있다고 지적 원하는 결과를 얻기 위해 변경하십시오.

집계가 무엇인지 확실하지 않습니다.

다른 방식으로 시도해 보겠습니다. 어떻게 구현합니까?

예를 들어 inf3rno는 Mizuno - Wave Rider 19를 10으로 투표 할 수 있습니다. 왜냐하면 그가 정말로 좋아하기 때문입니다.

위의 디자인에서는 VoteRepository을 사용했습니다. 명사는 유비쿼터스 언어에서 가져 오지 않기 때문에 우리는 "저장소"를 사용하고 싶지 않습니다. 이 도메인을 이전에 폴링 도메인이라고 했으므로 Poll을 엔터티로 사용해 봅시다. Poll 엔티티는 "한 사람, 한 표"불변을 강요 할 책임이 있습니다.

은 그래서

class Poll { 
    private PollId id; 
    private Map<MemberId,Vote> recordedVotes; 

    public void recordVote(MemberId memberId, Vote vote) { 
     if (recordedVotes.containsKey(memberId)) { 
      throw VoteFailed("This member already voted. No backsies!"); 
     } 
     recordedVotes.put(memberId, vote); 
    } 
} 

같은 그리고 투표를 기록하는 코드를 볼려고하는 것은, 예를 영향을 미칠 것

// Vote is just a value type, we can create one whenever we need to 

Vote vote = Vote.create(10); 

// entity ids are also value types that we can create whenever 
// we want. In a real program, we've probably done both of these 
// lookups already; Poll and Member are entities, which implies that 
// their identity is immutable - we don't need to worry that 
// MemberId 3a7fdc5e-36d4-45e2-b21c-942a4f68e35d has been assigned 
// to a different member. 

PollId pollId = PollId.for("Mizuno - WaveRider 19") 
MemberId memberId = MemberId.for("inf3rno"); 

Poll thePoll = pollRepository.get(pollId); 
thePoll.recordVote(memberId, vote); 
pollRepository.save(thePoll); 
+0

나는이 디자인에 의해 집계가 필요 없기 때문에 무언가가 잘못되었다고 생각합니다 ... 잘 이해하면 응용 프로그램 서비스에서이 코드와 같은 것이 필요합니다 :'modelRepository.save (aModelFactory.createModel (brandId, brandLookupService , ...))'. 집계가 여기에 무엇인지 확실하지 않습니다. 내가 필요한 것은 카테고리, 브랜드, 모델, 회원 등록을 추가하고 각 모델에 한 번만 투표하게하는 것입니다. 게시자의 게시물에 따르면 모델은 여기에 집계되어야하지만 예를 들어 추가 할 수 있어야합니다. 브랜드 추가 및 브랜드 추가는 어떤 모델에도 적용되지 않습니다. 브랜드를 어떻게 추가해야합니까? – inf3rno

+1

본 버논 (Vaughn Vernon)과 같은 많은 훌륭한 DDD 실무자가 집계를 다른 집계에 대한 인수로 전달합니다. 다른 AR이 ID를 붙잡을뿐 아니라 ID를 전달하는 것보다 훨씬 자주 UL을 표현합니다. 'poll.recordVote (member, ...)'는'poll.recordVote (memberId, ...)'보다 UL에 더 부합됩니다. – plalx

+0

@ inf3rno 집계가 무엇인지 이해하고 집계를 이해하는 것이 DDD 전술 패턴의 핵심이라고 생각하지 않습니다. [Effective Aggregate Design] (http://dddcommunity.org/library/vernon_2011/)의 세 부분을 읽으십시오. – plalx

관련 문제