Domain driven design DDD-构造函数中的问题和实体设计中的方法

Domain driven design DDD-构造函数中的问题和实体设计中的方法,domain-driven-design,Domain Driven Design,我有一个关于实体设计的问题。正如我所读到的,在设计DDD实体时,构造函数应该包含实体“存在”所需的值。例如,在我正在处理的域中,类实体在没有节和级别的情况下无法存在: public class Class { public Class(short id, string section, Level level) { ID = id; Section = section; Level = level; } //

我有一个关于实体设计的问题。正如我所读到的,在设计DDD实体时,构造函数应该包含实体“存在”所需的值。例如,在我正在处理的域中,类实体在没有节和级别的情况下无法存在:

public class Class
{
    public Class(short id, string section, Level level)
    {
        ID = id;
        Section = section;
        Level = level;
    }
    //
    // Properties
    //
    public short ID { get; private set; }
    public string Section { get; private set; }
    public Level Level { get; private set; }
    //
    // Methods
    //
    public static IList<Class> GetClassesByTeacher(short teacherID)
    {
        List<Class> classes = new List<Class>();
        classes.Add(new Class(1, "a", null));
        classes.Add(new Class(2, "b", null));
        classes.Add(new Class(3, "c", null));
        return classes;
    }
}
公共类
{
公共类(短id、字符串段、级别)
{
ID=ID;
截面=截面;
级别=级别;
}
//
//性质
//
公共短ID{get;private set;}
公共字符串部分{get;private set;}
公共级别{get;private set;}
//
//方法
//
公共静态IList GetClassesByTeacher(短teacherID)
{
列表类=新列表();
添加(新类(1,“a”,null));
添加(新类(2,“b”,null));
添加(新类(3,“c”,null));
返回类;
}
}
在这里,级别也是一个实体。由于我还没有完成设计,Level的构造函数可能还包含一个实体学年。困扰我的是调用GetClassesByTeacher方法,我需要实例化一个类和其他实体(Level,以及Level构造函数中需要的学年)


这是正确的吗?当我只想调用这个方法时,我觉得很麻烦。还有其他方法吗?我曾考虑将其设为静态,但其他人说可测试性将受到影响。我不确定CQRS是否是我想做的事情的解决方案之一,因为我还没有读到太多关于它的内容,但如果是,除了CQRS之外,还有什么其他技术我可以使用,或者是在使用DDD时,这就是它的实际情况吗?还是我的实体设计不正确?

您应该重新考虑您的域模型,因为您实际上说它似乎有问题

我可以提到单一责任原则(SRP),其中任何类别都应该有一个改变的理由。在您的示例中,如果我们在“类”中添加了一个新字段,我们将自己修改“类”,这是正确的,但是如果我们决定列表应按反转顺序排列,或者如果您需要一个只考虑临时教师的新列表类型,会发生什么情况。。。您应该更改“类”,但“类”与列表无关

要回答您的问题,您可以看看。存储库是您可以要求提供这些类型列表的地方

我可以把班级分成两部分:

  • 一个是“类”模型
  • “类”存储库的其他
总之:

public class Class
{
    public Class(short id, string section, Level level)
    {
        ID = id;
        Section = section;
        Level = level;
    }
    //
    // Properties
    //
    public short ID { get; private set; }
    public string Section { get; private set; }
    public Level Level { get; private set; }
}



public class ClassRepository
{
    private IList<Class> contents;

    //
    // Methods
    //
    public IList<Class> GetClassesByTeacher(short teacherID)
    {
        List<Class> classes = new List<Class>();
        for (Class elem: contents) {
            if (elem.getTeacher().equals(teacherID) {
                classes.Add(elem);
            }
        }
        return classes;
    }
}

repository = new ClassRepository;
level1 = new Level();
repository.Save(new Class(1, "a", level1));
repository.Save(new Class(2, "b", level1));
repository.Save(new Class(3, "c", level1));

result = repository.GetClassesByTeacher(1);
公共类
{
公共类(短id、字符串段、级别)
{
ID=ID;
截面=截面;
级别=级别;
}
//
//性质
//
公共短ID{get;private set;}
公共字符串部分{get;private set;}
公共级别{get;private set;}
}
公共类类存储库
{
私有IList内容;
//
//方法
//
公共IList GetClassesByTeacher(简称teacherID)
{
列表类=新列表();
用于(类元素:目录){
if(elem.getTeacher().equals)(teacherID){
类。添加(元素);
}
}
返回类;
}
}
repository=newclassrepository;
级别1=新级别();
Save(新类(1,“a”,level1));
Save(新类(2,“b”,级别1));
Save(新类(3,“c”,级别1));
结果=repository.GetClassesByTeacher(1);
还有其他一些细节,比如使用ClassRepositoryInterface并使用InMemoryClassRepository实现,你也会错过课堂上的教师信息,只要一位教师驾驶课堂,如果没有,你可能会改变如何按教师过滤,等等


我不是Java开发人员,代码不会编译,但我希望您能理解。

GetClassesByTeacher方法确实属于某种存储库或服务类。您的
实体是用来表示现实世界中该事物的实例。实体不是用来提供实例的(比如说,从一些底层持久性)-它们只是用来表示它们。
ClassRepository
是一种将
Class
实体的实例提供到您的域中的方法

您还提到,
如果没有
级别
,就不可能存在。您在这里谈论的是聚合。在线上有很多关于设计聚合的DDD材料。以下是一些:

编辑: 当一个实体需要另一个实体存在时,它是否应该始终是一个实体 合计

不,仅仅因为实体A依赖于实体B的存在,并不意味着实体A需要属于实体B的集合

现在我有了一个模型,其中有许多实体(如类、级别、, 科目、顾问、教师等)仅在某一学年存在 (一个实体)。对于一个集合来说,它可以变得这么大吗

如此大的聚合可能会导致性能和一致性问题

  • 为什么要提高性能?您可能正在为某个聚合根在内存中加载一个巨大的数据图。对于正在发生的任何工作单元,您很可能只需要处理该聚合的一小部分,而不是所有涉及的实体
  • 什么是一致性问题?聚合越大,在将数据检索到内存时,系统另一部分中的数据越有可能被更改。保存时,可能会发生数据丢失
沃恩·弗农(Vaughn Vernon)在他的三部分系列文章中完全涵盖了这些问题。如果你不熟悉DDD术语,这有点令人生畏,但是我强烈推荐这本书

另外,根据您所说的,实体不应该使用存储库 实体中的某些业务逻辑需要访问数据库才能运行 处理后,是否应直接使用DAL

实体