域驱动设计API问题

我是DDD的新手,我正在开展我的第一个项目,即在线高尔夫郊游注册过程。 我的要求非常简单。 用户注册外出,可以选择添加四人组。 他们还可以赞助一个带有消息和其他一些东西的漏洞,但我想首先勾勒出我们最糟糕的东西。

所以,我的第一个虽然我的聚合包含一个注册实体,四个值对象(包含一个团队名称和4个玩家值对象)。

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

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

我的问题是,聚合的一个内部组件正在暴露给客户端代码,所以我是否为问题打开了自己? 这种简单设计或替代api的任何缺陷?

任何帮助都会很棒,因为我现在处于分析瘫痪的状态!

谢谢

第一次拍摄也不错。 在开始设计时不要过于依赖细节,这一点很重要。 您花多少时间担心设计的稳健性主要是项目重要性的一个因素。 您将在构建时不断学习,随着时间的推移,您的旧代码将需要重构以启用新的详细信息。

举个例子,我认为你的FourSome课有四个玩家。 您是否可以使用具有“大小”约束的Team类,以便Players集合可以限制为该数量的玩家? 是否应该有Player类(或者可能是Player1的类型?)?

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

我鼓励您将每个场景编写成测试(您也可以使用用户故事或用例,但开发人员喜欢编写代码),并在编写测试时填写Registration,Player和Foursome / Team类实现使测试成功。 在扩展系统时,您的测试将会发生变化,您的设计也会发生变化。

增加后1:

域驱动设计旨在成为开发应用程序将使用的类和数据结构的方法,以便“模拟”问题域。 在您的情况下,您正在使用高尔夫郊游注册系统。 因此,当您考虑可能构成此类系统的实体时,您可以描述团队队长如何通过提供他的注册来注册他/她的团队(包括其他玩家)。 注册可能是针对一个事件,事件本身可能有详细信息,如组织者,赞助商等。如您所见,每个“大写”名称都成为您的实体(类)。 至少对于你的设计的初稿。 当您发现有关类之间关系的更多信息时,尤其是它们如何交互(将玩家添加到团队中)时,您将充实您的类的方法。

在此过程中,您可能会无意中引入设计缺陷。 例如,从技术上讲,FourSome是一种仅限于四名玩家的团队。 从团队派生一个类并设置四个玩家的限制是否有意义,或者对团队施加约束是否有意义? 这是一个由……您的业务规则和您决定的设计决策。 采取一种方法而不是另一种方法是错误的吗? 时间会告诉你,因为你需要不断重构才能扩展系统。

在设计过程中您会发现许多模式可能会使您的设计过程变得更容易,但新设计师通常没有经验来识别何时使用它们。 如果你这样做,请向你致敬。 在您第一次设计时,您将找不到完美的设计。 我仍然回顾(现在15年)我认为很棒的设计,并在我年轻的活力中摇头。

你应该描述你的问题(对自己或纸上)。 强调名词。 他们是你的候选class级。 讲述每个用户与系统交互的故事,动词应该让您开始了解每个类的方法。 避免使用注册来完成所有工作,但将类的责任分开到适当的位置。 例如,您可以将球员添加到球队,或者您可以让球员将自己添加到球队。 确定责任所在的位置应由SRP指导(但不是由其指定),以及其他设计指南。

最后,设计中没有正确的答案。 我可以根据我丰富的设计经验告诉你,但我认为应该是有限的高尔夫体验,但最终你的最佳设计取决于function,范围和设计目标(如可扩展性)。 从你最好的镜头开始,针对它进行测试,你的设计缺陷将在你知道之前出现。 :d

修订后2:

我想你正在读Eric Evan的指导。 我不相信他说你不能从四人中揭露玩家。

让我们从Aggregate 定义开始:

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

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

它看起来像Foursome不是一个Value Object,因为它是可变的。 它可能是一个应该由注册聚合根保护的实体。

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

同样,这可能不是您想要的,它实际上取决于您的域模型。 也许你的Aggregate root应该是像FoursomeRegistartion这样的实体。 如果玩家可以在Foursome / Registration边界之外存在,那么玩家可能会聚集在一起。 正如其他人所说,第一次很难使模型正确。 有第一个实现和重构连续。