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

时间:2014-06-10 14:47:44

标签: domain-driven-design

我在实体设计方面遇到了问题。正如我所读到的,当您设计DDD实体时,构造函数应该包含实体“存在”所需的值。例如,在我正在处理的域中,如果没有Section和Level,则Class实体不能存在:

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;
    }
}

此处Level也是一个实体。由于我还没有完成设计,Level的构造函数也可能包含一个实体SchoolYear。困扰我的是我调用GetClassesByTeacher方法,我需要实例化一个类以及其他实体(Level,以及Level的构造函数中需要的SchoolYear)。

这是正确的吗?我想当我想调用这个方法时,这很麻烦。还有其他方法吗?我认为它是静态的,但其他人说可测性会受到影响。我不确定CQRS是否是我想要做的解决方案之一,因为我还没有读过太多关于它的内容,但如果是的话,除了我可以使用的CQRS之外是否还有其他技术,或者是去DDD的时候,这是怎么回事?或者我的实体设计不正确?

2 个答案:

答案 0 :(得分:4)

你应该重新考虑你的域名模型,因为你实际上说它似乎有些不对劲。

我可以参考单一责任原则(SRP),其中任何类都应该有一个改变的理由。在您的示例中,如果我们向&#39; Class&#39;添加新字段会发生什么,我们将修改&#39; Class&#39;它本身就是正确的,但是如果我们认为列表应该是逆转顺序,或者如果你需要一种只考虑临时教师的新类型的清单会发生什么......你应该改变“等级”。但是&#39; Class&#39;与列表无关。

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

我可能将Class分成两部分:

  • 一个用于&#39; Class&#39;模型
  • 其他用于&#39; Class&#39;存储库

总结:

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);

还有其他细节,比如使用ClassRepositoryInterface并使用InMemoryClassRepository实现,只要一个老师开课,你就会错过课堂上的教师信息,如果不是,你可能会改变老师过滤等的方法等等。

我不是Java开发人员,代码也无法编译,但我希望你能理解。

答案 1 :(得分:3)

GetClassesByTeacher方法确实属于某种存储库或服务类。您的Class实体旨在表示现实世界中该事物的实例。实体并不意味着提供实例(例如,来自某些底层持久性) - 它们仅用于表示它们。 ClassRepository可以将Class实体的实例提供到您的域中。

您还提到如果没有ClassLevel就不能存在。你在这里谈论聚合。关于设计聚合,在线有很多DDD材料。这是一对夫妇:

修改

  

当一个实体需要另一个实体存在时,它应该始终是一个   聚合

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

  

现在我有一个模型,其中有许多实体(如Class,Level,   主题,顾问,教师等)仅存在于某一学校   (一个实体)。聚合可以获得那么大吗?

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

  • 为什么要表现?您可能会在内存中为某些聚合根加载大量数据。对于正在发生的任何工作单元而言,您很可能只需要处理该聚合的一小部分而不是所有涉及的实体。
  • 什么是一致性问题?聚合越大,系统的另一部分在检索到内存时就越有可能更改数据。保存时,可能会发生数据丢失。

Vaughn Vernon在他的三部分Effective Aggregate Design系列中完全涵盖了这些问题。如果您不熟悉DDD术语,这有点令人生畏,但我强烈推荐此阅读

  

另外,根据您的说法,实体不应使用存储库。如果   实体中的某些业务逻辑需要数据库访问权限   处理后,应该直接使用DAL吗?

实体不应该对存储库或服务(或其他任何事情)了解任何信息。它们不应该依赖于系统中的任何其他“层”。一切都可以了解您的实体,但您的实体不应该知道其他任何事情。向我提供一个示例,说明为什么您希望实体调用存储库,也许我可以提供更好的答案。