2017-04-12 32 views
1

我目前正在努力學習「領域驅動設計」。學習和實踐領域驅動設計,尋找一些指導

我在想如何有人設計這些實體。我簡化了對象模型,因爲需要很長時間才能解釋「REAL」應用程序來突出顯示我遇到的問題所在的域。

因此,CustomerInfo聚合包含一個條目列表。這是我在設計時遇到的「Entry」對象。

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

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

} 

Entry對象「可以」由幾個不同的實體列表來描述。這裏的警告是「條目」可以「僅」由這些列表中的一個來描述。在我的域名中,一個條目有一個Widgets列表和ThisThings列表是沒有意義的。

讓事情變得複雜。 的實體飾品ThatThingThisThingTheOtherThing都具有相同的特性,但在該領域的背景下它們的含義是非常不同的。

這是我目前的域模型。我不喜歡,因爲我有這一切的驗證,以確保用戶

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 DESIGN *********** ************/

我仍然相信一個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

你就是在描述一個名爲Table per Concrete Type(TPC)的EF繼承模型。您可以在基類中定義類繼承和共享代碼,但每個類實際上都由其自己的數據庫表(共享模式被複制到每個表中)支持。如果你想了解更多關於它的信息:https://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-3-table-per-concrete-type -tpc-和選擇策略的指南。 –

+0

嗨,佩德羅,你可以把它應用到TPC,但那不是我的問題。數據持久性在這裏不是問題。我只是專注於域建模,但感謝您的輸入..非常感謝! – GregL

回答

2

問題是你還沒有設計一個合適的Aggregate root - 戰術Domain driven design模式。

在你的情況下,Entry應該是一個確保自己的不變量的Aggregate root。我確定的不變量是Entry不應該只有一種Thing添加到其內部列表。所以,你的代碼應該反映這種不變性。

話雖這麼說,在Entry應該有一個事物的私人列表中,成爲單獨的列表,或只是一個混合列表,根據列表/列表的使用來實現。這將阻止客戶端代碼在沒有任何驗證的情況下將項目添加到列表中。然後Aggregate應該有一個addThing公共方法(用您的Ubiquitous language更正確的名稱替換該名稱)。該方法必須驗證所述不變量並拒絕任何重複。

使用或不使用abstraction不取決於DDD,而取決於使用Things。問問你自己:這種抽象能幫助我遵循OOP原則嗎(見SOLID)?在你的情況是不清楚,因爲我不知道你如何使用內Aggregate或在客戶端代碼。

+0

康斯坦丁感謝您的意見!用修改後的域模型更新了我的問題。你會發現現在一個「Entry」完全控制了Widgets和「Things」列表......你怎麼看?就SOLID而言,在「數據表示」方面,這些實體的方式與「類似於PrintJob或Scheduler類」的實體類型相反。我想我可以說他們大多是CRUD操作。我確實有一些功能,但主要是在「Entry」類中...... Entry類中將使用「Widget」和「Things」實體來幫助進行計算,但事實就是如此。 – GregL

+0

@GregL這更像它!請記住,聚合不允許其狀態變爲無效。他們通過將可變屬性設置爲'private'並通過仔細選擇訪問這些屬性的命名方法來確保這一點。 –

+0

謝謝先生!非常高興有一個社區來反彈想法!我發現轉變爲這種思維方式並偏離「以數據爲中心」的實體是具有挑戰性的,但我發現我確實喜歡以這種方式進行設計! – GregL

2

簡短的回答是,你不能像這樣抽象出你的問題。例如,什麼使得Widget和ThisThing如此相似以至於它們可以一起列出,但是ThatThing不可能是?

試想,像這樣

class Dog: IMamal { 

} 

class Cat: IMamal { 
} 

interface IMamal : IAnimal { 

} 

class Chicken : IAnimal { 
} 

在這裏,我發現,狗和貓是simmilar中,如果我談論的動物,我會引用這些類型的動物mamals的。

因此,與您的領域專家交談,並嘗試弄清楚某些事情是怎樣被調用的。通過這種方式,您可以定義一個界面,將某些事物分組在一起,但不是其他人。

如果您無法通過與您的領域專家溝通來找到他們爲什麼屬於他們的方式,那麼他們應該是2個單獨的名單。

如果你的域名真的這樣描述它,那麼多態性應該只會出現。如果我以我的動物爲例,一個Mamal可能有一個Walk()方法,而一個Bird可能有一個Fly()方法和一個Hop()方法(如果一個birt不飛) 可能沒有多態Move()方法由於沒有生物學家會描述動物的動作,他們總是稱它爲步行或飛行(這裏只是爲了爭論,它應該是領域專家,他描述的實體全都擁有「姓名」,而不是程序員看到「名稱」「標籤」和「描述」是相同類型的字段(正如Mike指出的那樣,巧合的合併是這裏要避免的)

+0

謝謝巴達維亞,你最後一段確實幫助我思考了另一種方式......只是因爲「Entry」可能有一個Widgets列表或ThatThings列表,但兩者不是一個比「域模型」更重要的業務規則,概念。我的領域專家無法想出一個有意義的名字來將(飾品,ThatThing,ThisThing和TheOtherThings)歸類在一起,所以應該告訴我,儘管這些實體具有相同的「字段類型」和名稱,但它們實際上代表了不同的領域實體在這方面。 – GregL

+0

我還想補充一點,強制類遵循通用接口可能很容易導致「巧合內聚」,其中不同類具有一些不應該存在的共同代碼。偶然的凝聚力被認爲是模塊中凝聚力最差的一種。 –