我目前正在努力学习" Domain Driven Design"。
我想知道有人会如何设计这些实体。我简化了对象模型,因为它需要很长时间来解释" 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对象&#34;可以&#34;由几个不同的实体列表描述。这里需要注意的是&#34; Entry&#34;只能&#34;只有&#34;由其中一个列表描述。在我的域中,条目有一个Widgets列表和ThisThings列表是没有意义的。
使事情复杂化。 实体 Trinket , ThatThing , ThisThing 和 TheOtherThing 都具有相同的属性,但在此域的上下文中他们的意思非常不同。
这是我目前的域名模型。这是我不喜欢的,因为我有所有这些验证,以确保用户只填充其中一个列表
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; }
}
我所挣扎的是如何对此进行模拟&#34; Entry&#34;实体得当。
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; }
}
然后,Entry实体看起来像,但不会阻止将 ThisThing 和 ThatThing 添加到同一条目列表中。
public class Entry
{
public Guid EntryId { get; set; }
/* Other properties removed, to simplify question */
public List<IWidget> Widgets { get; set; }
}
3)我是否应该完全像WidgetEntry创建不同的Entry实体,ThisThingEntry具有通用接口,因此Aggregate Root看起来像这样:
//Lets call the CustomerInfo the Aggregate Root
public class CustomerInfo {
/* Other properties removed, to simplify question */
public List<IEntry> Entries { get; set; }
}
考虑到我考虑过的不同选项,解决此域名约束的唯一解决方案&#34; Entry&#34;只能&#34;只有&#34;由其中一个列表描述是#3
任何指导都会非常感激,为冗长的问题道歉!
/ ******************************************************************************************************************************** *********** /
我仍然认为 CustomerInfo 应该是聚合,因为在我的域中,用户添加的各种条目来描述 CustomerInfo 以构建&#是有意义的34; CustomerInfo&#34;实体。
//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 :(得分:2)
简短的回答是你无法像这样抽象出你的问题。例如,是什么让Widget和ThisThing如此相似以至于它们可以列在一起但是ThatThing不能?
就这样考虑一下
class Dog: IMamal {
}
class Cat: IMamal {
}
interface IMamal : IAnimal {
}
class Chicken : IAnimal {
}
在这里,我发现狗和猫是相似的,如果我谈论动物,我会把那些动物称为mamals。
请与您的领域专家交谈并尝试弄清楚某些事情的组合。这样你就可以定义一个界面,将某些东西组合在一起而不是其他东西
如果您无法通过与您的域名专家交谈找不到他们属于一起的方式,那么他们应该选择2个单独的列表,这是一个不错的选择。
如果您的域名真的以这种方式描述,那么Polimorphism应该真正遵循。如果我采用我的动物示例,Mamal可能有一个Walk()方法,而Bird可能有一个Fly()方法和一个Hop()方法(如果一个birt不会飞) 可能没有多态的Move()方法,因为没有生物学家会描述动物的移动,他们总是把它称为行走或飞行(仅为了争论这里,它应该是领域专家将实体描述为所有人一个“名字”,而不是那个看到“名字”“标签”和“描述”的程序员是同一种领域。(正如迈克指出的那样,巧合的是这里要避免的事情)
答案 1 :(得分:2)
问题是您没有设计合适的Aggregate root
- 战术Domain driven design
模式。
在您的情况下,Entry
应该是Aggregate root
,以确保其自己的不变量。我已经确定的不变量是Entry
不应该只在其内部列表中添加一种Thing
。所以,你的代码应该反映出这种不变量。
话虽这么说,Entry
应该有一个私有列表,可以作为单独的列表实现,也可以只使用一个混合列表,具体取决于列表/列表的用法。这将阻止客户端代码在没有任何验证的情况下将项目添加到列表中。然后,Aggregate
应该有一个addThing
公共方法(用Ubiquitous language
中更恰当的名称替换该名称)。此方法必须验证所述不变量并拒绝任何重复。
使用与否abstraction
不取决于DDD,而取决于Things
的使用。问问自己:这种抽象是否有助于我遵循OOP原则(参见SOLID)?在您的情况下不明确,因为我不知道您如何在things
或客户端代码中使用Aggregate
。