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

C# 学习和实践领域驱动设计,寻求一些指导,c#,entity-framework,domain-driven-design,C#,Entity Framework,Domain Driven Design,我目前正在学习领域驱动设计 我想知道有人会如何设计这些实体。我简化了对象模型,因为解释真正的应用程序需要很长时间才能突出我遇到问题的领域 因此CustomerInfo聚合包含一个条目列表。我在设计这个入口对象时遇到了麻烦 //Lets call the CustomerInfo the Aggregate Root public class CustomerInfo { /* Other properties removed, to simplify question */ p

我目前正在学习领域驱动设计

我想知道有人会如何设计这些实体。我简化了对象模型,因为解释真正的应用程序需要很长时间才能突出我遇到问题的领域

因此CustomerInfo聚合包含一个条目列表。我在设计这个入口对象时遇到了麻烦

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

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

}
然后条目实体看起来像,但不阻止ThisThing和ThatThing被添加到同一条目列表中

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

    /* Other properties removed, to simplify question */

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

 }
3我是否应该创建完全类似WidgetEntry的不同条目实体,此ThingEntry具有一个公共接口,以便聚合根目录如下所示:

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

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

 }
考虑到我所考虑的不同选项,解决此域约束条目的唯一解决方案只能由以下列表之一描述:3

任何指导都将不胜感激,为冗长的问题道歉

/*******************************修正域设计***********************/

我仍然认为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*/
    }

}

简而言之,你不能像这样把问题抽象掉。例如,是什么使得一个小部件和一个ThisThings如此相似,以至于它们可以一起列出,而一个ThisThings却不能

<>就这样考虑< /P>
class Dog: IMamal {

}

class Cat: IMamal {
}

interface IMamal : IAnimal {

}

class Chicken : IAnimal {
}
在这里,我发现狗和猫是相似的,如果我谈论动物,我会把这些动物称为哺乳动物

因此,请咨询您的领域专家,并尝试找出某些组的东西被称为什么。通过这种方式,您可以定义一个接口,该接口将某些内容分组在一起,而不是其他内容

如果你无法通过与你的领域专家交谈找到它们在一起的原因,那么最好将它们分为两个单独的列表

只有当你的领域真的这样描述它时,Polimorphism才会出现。如果我拿我的动物为例,一只Mamal可能有一种步行法,一只鸟可能有一种飞行法,如果一只鸟不飞,则有一种跳跃法
可能不存在多态移动方法,因为没有生物学家会描述动物移动,他们总是将其称为行走或飞行,只是为了在这里进行论证,应该是领域专家将实体描述为所有具有名称的实体,而不是程序员将名称标签和描述视为同一类字段。正如Mike所指出的,在这里要避免的是连贯性,简单的回答是,你不能像这样把你的问题抽象掉。例如,是什么使得一个小部件和一个ThisThings如此相似,以至于它们可以一起列出,而一个ThisThings却不能

<>就这样考虑< /P>
class Dog: IMamal {

}

class Cat: IMamal {
}

interface IMamal : IAnimal {

}

class Chicken : IAnimal {
}
在这里,我发现狗和猫是相似的,如果我谈论动物,我会把这些动物称为哺乳动物

因此,请咨询您的领域专家,并尝试找出某些组的东西被称为什么。通过这种方式,您可以定义一个接口,该接口将某些内容分组在一起,而不是其他内容

如果你无法通过与你的领域专家交谈找到它们在一起的原因,那么最好将它们分为两个单独的列表

只有当你的领域真的这样描述它时,Polimorphism才会出现。如果我拿我的动物为例,一只Mamal可能有一种步行法,一只鸟可能有一种飞行法,如果一只鸟不飞,则有一种跳跃法
可能不存在多态移动方法,因为没有生物学家会描述动物移动,他们总是将其称为行走或飞行,只是为了在这里进行论证,应该是领域专家将实体描述为所有具有名称的实体,而不是程序员将名称标签和描述视为同一类字段。正如Mike指出的,这里要避免的是协同一致性问题,问题是您没有设计一个合适的聚合根-一个战术领域驱动的设计模式

在您的情况下,条目应该是一个聚合根,以确保其自身的不变量。我已经确定的不变量是,条目不应该只在其内部列表中添加一种内容。因此,您的代码应该反映该不变量

也就是说,条目应该有一个私有的事物列表,根据列表的使用情况实现为单独的列表或一个混合列表。这将阻止客户端代码在没有任何验证的情况下将项目添加到列表中。然后,聚合应该有一个addThing公共方法,用您的通用语言中更合适的名称替换该名称。此方法必须验证所述不变量并拒绝任何重复项

是否使用抽象并不取决于DDD,而是取决于事物的使用。扪心自问:这个抽象是否有助于我遵循OOP原则?在你的cas里
e不清楚,因为我不知道如何在聚合或客户机代码中使用这些东西。

问题在于,您没有设计一个合适的聚合根-一个战术领域驱动的设计模式

在您的情况下,条目应该是一个聚合根,以确保其自身的不变量。我已经确定的不变量是,条目不应该只在其内部列表中添加一种内容。因此,您的代码应该反映该不变量

也就是说,条目应该有一个私有的事物列表,根据列表的使用情况实现为单独的列表或一个混合列表。这将阻止客户端代码在没有任何验证的情况下将项目添加到列表中。然后,聚合应该有一个addThing公共方法,用您的通用语言中更合适的名称替换该名称。此方法必须验证所述不变量并拒绝任何重复项


是否使用抽象并不取决于DDD,而是取决于事物的使用。扪心自问:这个抽象是否有助于我遵循OOP原则?在您的情况下,我不清楚,因为我不知道如何在聚合或客户机代码中使用这些东西。

这个问题更适合吗?您描述的是一种称为Table per CONTACT Type TPC的EF继承模型。您可以在基类中定义类继承和共享代码,但每个类实际上都有其自己的数据库表支持。共享模式被复制到每个表中。如果你想了解更多:。嗨,佩德罗,你可以把这个应用到TPC上,但这并不是我真正的问题。这里不关心数据持久性。我只是专注于领域建模,但感谢您的投入……非常感谢!这个问题更适合吗?您描述的是一种称为Table per-Concrete类型TPC的EF继承模型。您可以在基类中定义类继承和共享代码,但每个类实际上都有其自己的数据库表支持。共享模式被复制到每个表中。如果你想了解更多:。嗨,佩德罗,你可以把这个应用到TPC上,但这并不是我真正的问题。这里不关心数据持久性。我只是专注于领域建模,但感谢您的投入……非常感谢!谢谢你,巴达维亚,你的最后一段真的帮助我以另一种方式思考这个问题。。。仅仅因为一个条目可以有一个小部件列表或一个小部件列表,但不是两者都有,这更像是一个业务规则,而不是一个域模型概念。我的域名专家想不出一个有意义的名字来分类这个小饰品,那东西,这个东西和其他东西结合在一起,所以应该告诉我,即使这些实体有相同的字段类型和名称。在这个上下文中,它们实际上代表不同的域实体。我还想补充一点,强制类遵循公共接口可能很容易导致巧合的内聚,不同的类有一些不应该存在的通用代码。巧合内聚被认为是模块中最糟糕的内聚类型。谢谢你,巴达维亚,你的最后一段真的帮助我从另一个角度思考了这个问题。。。仅仅因为一个条目可以有一个小部件列表或一个小部件列表,但不是两者都有,这更像是一个业务规则,而不是一个域模型概念。我的域名专家想不出一个有意义的名字来分类这个小饰品,那东西,这个东西和其他东西结合在一起,所以应该告诉我,即使这些实体有相同的字段类型和名称。在这个上下文中,它们实际上代表不同的域实体。我还想补充一点,强制类遵循公共接口可能很容易导致巧合的内聚,不同的类有一些不应该存在的通用代码。巧合内聚被认为是模块中最糟糕的内聚类型。Constantin感谢您的输入!用修改过的域模型更新了我的问题。您将看到,现在一个条目完全控制小部件和事物列表。。。你怎么认为?就SOLID而言,这些实体在数据表示方面要重得多,而不是像PrintJob或Scheduler类这样的实体类型。我想我可以说他们大部分是积垢操作。我有一些功能,但它主要是在入门类。。。小部件和事物实体将在Entry类中用于帮助计算,但实际上就是这样。@GregL更像它!请记住,聚合不允许其状态无效。他们通过将可变属性设置为私有并使用精心选择的命名方法访问这些属性来确保这一点。谢谢先生!!!很高兴有一个社区来激发创意!!!我发现向这种思维方式转变并远离以数据为中心的实体很有挑战性,但我发现我实际上喜欢用这种方式设计!康斯坦丁:谢谢你的投入!用修改过的dom更新了我的问题
ain模型。您将看到,现在一个条目完全控制小部件和事物列表。。。你怎么认为?就SOLID而言,这些实体在数据表示方面要重得多,而不是像PrintJob或Scheduler类这样的实体类型。我想我可以说他们大部分是积垢操作。我有一些功能,但它主要是在入门类。。。小部件和事物实体将在Entry类中用于帮助计算,但实际上就是这样。@GregL更像它!请记住,聚合不允许其状态无效。他们通过将可变属性设置为私有并使用精心选择的命名方法访问这些属性来确保这一点。谢谢先生!!!很高兴有一个社区来激发创意!!!我发现向这种思维方式转变并远离以数据为中心的实体很有挑战性,但我发现我实际上喜欢用这种方式设计!
class Dog: IMamal {

}

class Cat: IMamal {
}

interface IMamal : IAnimal {

}

class Chicken : IAnimal {
}