学习和实践领域驱动设计,寻找一些指导

时间:2017-04-12 19:06:34

标签: c# entity-framework domain-driven-design

我目前正在努力学习" 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*/
    }

}

2 个答案:

答案 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