2017-04-12 1 views
1

현재 "도메인 기반 디자인"을 배우기 위해 노력하고 있습니다.도메인 기반 디자인 학습 및 연습, 일부 지침 찾기

누군가 어떻게 이러한 엔티티를 디자인 할 것인지 궁금합니다. 문제가있는 도메인의 영역을 강조하기 위해 "실제"응용 프로그램을 설명하는 데 오랜 시간이 걸릴 것이므로 객체 모델을 단순화했습니다.

따라서 CustomerInfo 집합에는 항목 목록이 들어 있습니다. 디자인에 문제가있는 것은이 "엔트리"객체입니다.

//Lets call the CustomerInfo the Aggregate Root 
public class CustomerInfo { 
    /* Other properties removed, to simplify question */ 

    public List<Entry> Entries { get; set; } 

} 

항목 개체는 몇 가지 다른 엔터티 목록으로 설명 될 수 있습니다. 여기에주의해야 할 것은 "Entry"가 "오직"이 목록 중 하나에 의해 설명 될 수 있다는 것입니다. 내 도메인에서는 항목에 위젯 목록과 ThisThings 목록이있는 것은 이해가되지 않습니다.

복잡합니다. 엔티티 악세사리, ThatThing, ThisThingTheOtherThing 모두 같은 특성을 가지고 있지만,이 도메인의 맥락에서 그 의미가 매우 다르다.

이것은 현재 도메인 모델입니다. 어느 내가 제대로이 "항목"엔티티를 모델링하는 방법이다와 사투를 벌인거야 어떤 사용자

public class Entry 
{ 
    public Guid EntryId { get; set; } 

    /* Other properties removed, to simplify question */ 

    public List<Widget> Widget { get; set; } 

    public List<Trinket> Trinkets { get; set; } 
    public List<ThatThing> ThatThings { get; set; } 
    public List<ThisThing> ThisThings { get; set; } 
    public List<TheOtherThing> TheOtherThings { get; set; } 
} 


public class Widget 
{ 
    public Guid Widgetid { get; private set; } 
    public string Name { get; private set; } 

    public int Size { get; set; } 
    public string Color { get; set; } 
} 

public class Trinket 
{ 
    public Guid Trinketid { get; private set; } 
    public decimal Cost { get; private set; } 
    public Construction Construction { get; private set; } 
} 

public class ThatThing 
{ 
    public Guid ThatThingid { get; private set; } 
    public decimal Cost { get; private set; } 
    public Construction Construction { get; private set; } 
} 

public class ThisThing 
{ 
    public Guid ThisThingid { get; private set; } 
    public decimal Cost { get; private set; } 
    public Construction Construction { get; private set; } 
} 

public class TheOtherThing 
{ 
    public Guid TheOtherThingId { get; private set; } 
    public string Name { get; private set; } 
    public Construction Construction { get; private set; } 
} 

public class Construction : ValueObject<Construction> 
{ 
    public int Size { get; private set; } 
    public string Weight { get; private set; } 
    public string Unit { get; private set; } 
    public string Form { get; private set; } 
} 

만 목록 중 하나가 채워져 있는지 확인하기 위해 모든 검증을 가지고 내가 좋아하지 않는다.

1) 설계대로 유지하고이 미친 검증에 의존해야합니다.

2)이를 처리하기 위해 다형성 모델을 작성해야합니까?

public interface IWidget{ 
     public Guid Widgetid { get; set; } 

} 

public interface IDifferentWidget:IWidget 
{ 

    public decimal Cost { get; set; } 
    public Construction Construction { get; set; } 
} 

public class Widget:IWidget 
{ 
    public Guid WidgetId { get; private set; } 
    public string Name { get; private set; } 

    public int Size { get; set; } 
    public string Color { get; set; } 
} 

public class Trinket : IDifferentWidget 
{ 
    public Guid WidgetId { get; private set; } 
    public decimal Cost { get; private set; } 
    public Construction Construction { get; private set; } 
} 

public class ThatThing : IDifferentWidget 
{ 
    public Guid WidgetId { get; private set; } 
    public decimal Cost { get; private set; } 
    public Construction Construction { get; private set; } 
} 

public class ThisThing : IDifferentWidget 
{ 
    public Guid WidgetId { get; private set; } 
    public decimal Cost { get; private set; } 
    public Construction Construction { get; private set; } 
} 

public class TheOtherThing : IDifferentWidget 
{ 
    public Guid WidgetId { get; private set; } 
    public decimal Cost { get; private set; } 
    public Construction Construction { get; private set; } 
} 

그런 다음 항목 개체는 같을 것이다, 그러나 ThisThingThatThing이 같은 항목 목록에 추가되는 것을 방지하지 않습니다.

public class Entry 
{ 
    public Guid EntryId { get; set; } 

    /* Other properties removed, to simplify question */ 

    public List<IWidget> Widgets { get; set; } 

} 

3) 내가 완전히 WidgetEntry 같은 다른 항목 개체를 만들 경우, 집계 루트 때문에 공통 인터페이스와 ThisThingEntry 지금과 같습니다

//Lets call the CustomerInfo the Aggregate Root 
public class CustomerInfo { 
/* Other properties removed, to simplify question */ 

    public List<IEntry> Entries { get; set; } 

} 

를 내가의를 고려 한 다른 옵션을 고려 이 도메인 제한을 다루는 유일한 해결책 "항목"은 "목록"중 하나에 의해 설명 될 수 있습니다.은 # 3입니다.

모든 지침을 매우 높이 평가하며, estion!

/******************************* REVISED DOMAIN 디자인 *********** ************/

나는 여전히 내 도메인에 사용자가에 추가하는 다양한 항목에 의해 CustomerInfo을 설명하는 의미가 있습니다 때문에 CustomerInfo가 집계 될 것으로 판단 "CustomerInfo"엔티티를 빌드하십시오.

//Lets call the CustomerInfo the Aggregate Root 
public class CustomerInfo { 

    public Guid CustomerId { get; private set; } 

    private List<Entry> _entries; 
    public IEnumerable<Entry> Entries => _entries; 

    private CustomerInfo(Guid customerId /* Other properties removed, to 
    simplify question */){ } 

    public CustomerInfo Create(/* Other properties removed, to simplify 
           question  */) { 
    return new CustomerInfo(Guid.NewGuid()); 
     } 

    /*This is how the entity will control the state of the various lists of 
    entities that describe it.*/ 
    public Entry UpdateEntry(/* Parameters removed for simplicity */) { 

    } 

    public Entry AddEntry(/* Parameters removed for simplicity */) { 

    } 

    public Entry RemoveEntry(/* Parameters removed for simplicity */) { 

    } 
    } 



public class Entry { 
    public Guid EntryId { get; set; } 

    /* Other properties removed, to simplify question */ 
    private List<Widget> _widgets; 
    public IEnumerable<Widget> Widgets => _widgets; 

    private List<Trinket> _trinkets; 
    public IEnumerable<Trinket> Trinkets => _trinkets; 

    private List<ThatThing> _thatThing; 
    public IEnumerable<ThatThing> ThatThings => _thatThing; 

    private List<ThisThing> _thisThings; 
    public IEnumerable<ThisThing> ThisThings => _thisThings; 

    private List<TheOtherThing> _theOtherThing; 
    public IEnumerable<TheOtherThing> TheOtherThings => _theOtherThing; 


    private Entry(guid EntryId /*This constructor will take more parameters,   
    it's simplified for my question*/) { } 

    //Create a new instance of a Entry entity 
    public Entry Create(/* Parameters removed for simplicity */) { 
     return new Entry(Guid.NewGuid()); 
    } 

    //This is how the entity will control the state of the various lists of   
    entities that describe it. 
    public Widget UpdateWidget() { 

    } 

    public Widget AddWidget() { 

    } 

    public Widget RemoveWidget() { 

    } 

    private bool CanAddAWidget() { 
     /* Logic to prevent a widget from being add if any of the other   
      lists have items*/ 
    } 

    public ThisThing UpdateThisThing() 
    { 

    } 

    public ThisThing AddThisThing() 
    { 

    } 

    public ThisThing RemoveThisThing() 
    { 

    } 

    private bool CanAddAThisThing() 
    { 
    /* Logic to prevent a widget from being add if any of the other lists  
    have items*/ 
    } 

} 
+0

이 질문은 http://codereview.stackexchange.com/에 더 적합합니까? – VoiceOfUnreason

+0

당신은 TPC (Table per Concrete Type)라는 EF 상속 모델을 설명하고 있습니다. 기본 클래스에서 클래스 상속을 정의하고 코드를 공유 할 수 있지만 각 클래스는 실제로 자체 데이터베이스 테이블에 의해 백업됩니다 (공유 스키마는 각 테이블에 복사됩니다). 그것에 대해 자세히 알고 싶다면 https://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-3-table-per-concrete-type을 참조하십시오. - 전략적 - 지침 - 선택 전략. –

+0

안녕 페드로, 당신은 TPC에 이것을 적용 할 수 있지만 그건 내 질문에 관한 것입니다.데이터 지속성은 여기에서 중요하지 않습니다. 도메인 모델링에 초점을 맞추고 있지만 입력에 대해 감사드립니다. 매우 감사드립니다! – GregL

답변

2

문제는 당신이 적절한 Aggregate root을 설계하지 않은 것입니다 (마이크는 지적 conincidental coheision 여기 방지하기위한 것입니다) - 전술 Domain driven design 패턴

귀하의 경우에는 Entry은 자체 불변 부를 보장하는 Aggregate root이어야합니다. 내가 발견 한 불변량은 Entry이 내부 목록에 Thing이라는 하나의 종류 만 있으면 안된다는 것입니다. 그래서, 당신의 코드는 그 불변성을 반영해야합니다.

즉, Entry개인 개인 목록이 있어야하며, 목록/목록 사용법에 따라 개별 목록 또는 단일 혼합 목록으로 구현됩니다. 그러면 클라이언트 코드가 검증없이 목록에 항목을 추가 할 수 없게됩니다. 그런 다음 AggregateaddThing public 메소드가 있어야합니다 (해당 이름을 Ubiquitous language의 더 적절한 이름으로 바꿉니다). 이 메소드는 불변성을 검증하고 중복을 거부해야합니다.

abstraction은 DDD가 아니라 Things의 사용에 따라 달라집니다. 이 추상화가 OOP 원리 (SOLID 참조)를 따르는 데 도움이되는지 질문하십시오. 귀하의 경우에는 Aggregate 또는 고객 코드에있는 things을 어떻게 사용하는지 모르므로 명확하지 않습니다.

+0

입력에 대한 Constantin 감사합니다! 수정 된 도메인 모델로 내 질문을 업데이트했습니다. 이제는 "엔트리"가 위젯과 "물건"목록을 완벽하게 제어한다는 것을 알 수 있습니다. 어떻게 생각하십니까? 솔리드 (SOLID)에 관한 한, "데이터 표현 (Data Representation)"측면에서보다 무거운이 엔티티는 PrintJob 또는 Scheduler 클래스와 같이 "무언가를하는"엔티티 유형에 반대합니다. 나는 그들이 대부분 CRUD 작업이라고 말할 수 있다고 생각합니다. 몇 가지 기능이 있지만 대부분 "Entry"클래스에 있습니다 ... Entity 클래스에서 "Widget"및 "Things"엔티티가 계산에 도움이되지만 실제로 사용됩니다. – GregL

+0

@ GregL 더 좋아요! 집계자는 자신의 주를 무효화하는 것을 허용하지 않습니다. 이것들은 변경 가능한 프로퍼티를 private로 만들고, 그 프로퍼티에 접근하는 신중하게 선택된 명명 된 메소드로이를 보장한다. –

+0

감사합니다. 아이디어를 얻는 커뮤니티를 갖게되어 너무 좋습니다 !!! 이런 사고 방식으로 벗어나 "Data Focused"엔티티에서 벗어나기 란 쉽지 않습니다.하지만 실제로 이런 식으로 설계하는 것을 즐깁니다. – GregL

2

짧은 대답은 이와 같이 질문을 추상화 할 수 없다는 것입니다. 예를 들어, 위젯과 ThisThing이 함께 나열 될 수 있지만 ThatThing이 될 수없는 것은 무엇일까?

그냥 여기에 내가 개와 고양이는 내가 mamals 같은 동물의 그런 종류를 참조 것 동물에 대해 이야기하는 경우는 점에서에 simmilar 것으로 나타났습니다이

class Dog: IMamal { 

} 

class Cat: IMamal { 
} 

interface IMamal : IAnimal { 

} 

class Chicken : IAnimal { 
} 

처럼 생각.

도메인 전문가에게 이야기하고 어떤 특정 그룹이 호출되는지 파악해보십시오. 그렇게하면 특정 사물을 그룹화하고 다른 그룹을 그룹화하지 않는 인터페이스를 정의 할 수 있습니다.

도메인 전문가와 이야기하여 그들이 속한 방법을 찾을 수 없다면 2 개의 별도 목록이어야하는 것이 좋습니다.

도메인이 실제로 그렇게 설명하는 경우에만 Polimorphism을 수행해야합니다. 동물 샘플을 가져 가면 Mamal에는 Walk() 메서드가 있고 Bird에는 Fly() 메서드와 Hop() 메서드가있을 수 있습니다 (birt가없는 경우) 다형성 Move() 메서드가 없을 수 있습니다. 어떤 생물 학자도 동물이 움직인다는 것을 기술하지 않으므로 항상 동물을 걷거나 날기라고 부른다. (단지 논쟁을 위해서, 엔티티를 "이름"을 가진 것으로 기술하는 도메인 전문가가 아니라 프로그래머가 아니라고 기술하는 도메인 전문가 여야한다. . "이름" "라벨"과 "설명"필드의 같은 종류의 것을 볼 수

+0

바타 비아에게 감사드립니다. 마지막 단락은 정말로이 다른 방법에 대해 생각하도록 도왔습니다 ... "항목"에 위젯 목록 또는 ThatThings 목록이있을 수는 있지만 두 가지 모두가 "도메인 모델"이 아닌 비즈니스 규칙입니다. 개념. 내 도메인 전문가는 (Trinket, ThatThing, ThisThing 및 TheOtherThings)을 함께 분류 할 수있는 의미있는 이름을 제시 할 수 없으므로 이러한 엔터티가 동일한 "필드 유형"및 이름을 갖고 있음에도 불구하고 실제로는 다른 이 문맥에서 도메인 엔티티. – GregL

+0

공통 인터페이스를 따르는 클래스를 강제하면 쉽게 "우연의 일치"로 이어질 수 있다고 덧붙이고 싶습니다. 다른 클래스에는 실제로는 없어야하는 공통 코드가 있습니다. 동시 적 응집력은 모듈에서 최악의 응집 유형으로 간주됩니다. –

관련 문제