领域驱动设计API问题

本文关键字:API 问题 | 更新日期: 2023-09-27 18:06:41

我是DDD的新手,我正在做我的第一个项目,这是一个在线高尔夫外出注册过程。我的要求很简单。用户注册郊游,可以选择添加一个四人组。他们也可以赞助一个带有消息和其他一些东西的洞,但我想先散列我们的四个人的东西。

因此,我的第一个聚合包含一个注册实体,四个值对象(其中包含一个球队名称和4个球员值对象)。

在设计api时,我正在考虑以下伪代码:

Registration reg = new Registration();
Foursome foursome = reg.CreateFoursome("My Team");
foursome.Player1.Assign("John Doe", 5, ShirtSize.XL);
reg.Register();

我的问题是,聚合的一个内部组件暴露给客户端代码,所以我打开我自己的问题吗?这个简单的设计或替代api有什么缺陷吗?

任何帮助都将是伟大的,因为我现在处于分析瘫痪状态!

谢谢

领域驱动设计API问题

让我们从聚合定义开始:

一组相关联的对象,它们被视为对象的一个单元数据更改的目的。外部引用被限制为一个指定为根的聚合的成员。一组一致性rules适用于聚合的边界。

Aggregate是一组你不希望多个用户同时编辑的对象,因为它会破坏域不变量。Aggregate也是一个生命周期单位。如果不知道这些不变量、一致性和生命周期规则是什么,就很难回答你的问题。在同一注册上创建两个foursome会不好吗?将Foursome与未分配Player1无效/不一致?不调用注册对象上的注册将"破坏"它?如果其中一个答案是正确的,那么你就不应该像那样暴露你的对象。此代码应该隐藏在聚合中。

看起来也像Foursome不是一个值对象,因为它是可变的。它可能是一个应该被注册聚合根保护的实体。

// PlayerInfo is a value object
public static Registration CreateNew(String foursomeName, PlayerInfo player1, ...) {
    if (foursomeName == null) {
        throw new ArgumentNullException("foursomeName");
    }
    if (player1 == null) {
        throw new ArgumentNullException("player1");
    }
    Registration reg = new Registration();
    Foursome foursome = reg.CreateFoursome("My Team");
    foursome.Player1.Assign(player1);
    if(player2 != null) {
        foursome.Player2.Assign(player2);
    }
    reg.Register();
    // return consistent and valid Registration instance
    return reg;
}

再次强调,这可能不是您想要的,这实际上取决于您的领域模型。也许你的聚合根应该是一个像foursomeregition这样的实体。如果玩家能够存在于四人组/注册边界之外,那么他们自己也可能是聚合体。正如其他人所说,很难在第一次就做出正确的模型。有一个第一次实现,并不断地重构。

第一次拍还不错。当开始设计时,不要太在意小细节,这一点很重要。花费多少时间来担心设计的健壮性将是决定项目重要性的主要因素。你将在构建过程中不断学习,随着时间的推移,你的旧代码将需要重构以启用新的细节。

举个例子,你的FourSome类我假设有四个玩家。你是否可以使用一个具有大小约束的Team类,这样玩家集合就可以被限制为该数量的玩家?是否应该有一个Player类(或者也许是Player1的类型?)?

这些问题的答案将对系统的可扩展性产生影响。

我鼓励您将每个场景都写入测试中(您也可以使用用户故事或用例,但开发人员喜欢编写代码),并且在编写测试时,填写注册,玩家和四人组/团队类实现以使测试成功。当你扩展你的系统时,你的测试会改变,你的设计也会改变。

新增1:

领域驱动设计旨在成为一种方法,用于开发应用程序将使用的类和数据结构,以便对问题领域进行"建模"。在您的案例中,您使用的是Golf Outing Registration系统。因此,当你考虑组成这样一个系统的实体时,你可以描述一个队长如何通过提供他的注册来注册他/她的团队(包括其他玩家)。注册可能是针对Event的,Event本身可能具有诸如Organizer、Sponsor等详细信息。如您所见,每个"大写"的名称都成为您的实体(类)。至少对你的设计初稿来说是这样。当你发现更多关于类之间关系的信息时,特别是关于它们如何相互作用(一个玩家被添加到一个团队中),你将充实你的类的方法。

在此过程中,您可能会无意中引入设计缺陷。例如,从技术上讲,四人组是一种由四名球员组成的团队。从Team中派生类并设置4名玩家的限制是否有意义,或者对Team施加约束是否有意义?这是一个由……决定的设计决策。你的业务规则和你自己。选择一种方法而不是另一种方法是错误的吗?时间会证明一切,因为您需要不断地重构以扩展系统。

在你的设计过程中,你会发现许多模式可以使你的设计过程更容易,但一个新的设计师通常没有经验来识别何时使用它们。如果你做到了,那就太好了。你不可能在第一次设计时就找到一个完美的设计。回首过去(15年了),我仍然会想起那些我认为很棒的设计,并对自己年轻时的活力摇头。

你应该描述你的问题(对自己或在纸上)。在名词下面划线。它们是你的候选班级。为每个用户与系统的交互讲述一个故事,动词应该让您开始了解每个类上的方法。避免使用Registration来完成所有的工作,而是将类的职责分离到合适的地方。例如,您可以将玩家添加到团队中,或者让玩家将自己添加到团队中。在其他设计指导中,应该由SRP指导(但不是命令)确定责任所在。

最后,在设计中没有正确答案。我可以根据我丰富的设计经验告诉你我认为它应该是什么,但最终你的最佳设计取决于功能,范围和设计目标(如可扩展性)。从你最好的尝试开始,针对它编写测试,你的设计缺陷会在你意识到之前出现。: D

Post-Revision 2:我认为你对Eric Evan的指导读得太多了。我不相信他是在说你不能揭露《四人组》中的玩家