我的回答不涵盖详细的实现或代码,因为你似乎是在使用它作为了解DDD更多的练习。请记住,在大多数情况下,你不会第一次就把模型做对,需要演化模型。当你 "玩弄" 它时,某些严格的部分将变得更加灵活易变(如Eric的比喻所说的园艺手套)。随着你对领域有了新的认识,你会发现需要向模型中引入新的概念。使用 "简单的例子" 也有危险,例如可能缺乏深度。但是有时需要简单的例子来掌握DDD,而且幸运的是,我们也可以演化样例;)
我听过Eric Evans提到的一件事是,如果领域感觉不正确或者你无法表达模型中的某些东西,那么你可能会遗漏一个概念。自然地,如果你在领域中有了概念,你就可以 "感受到" 或者找到验证自然发生的地方。
在设置上下文之后,我的建议如下:
Enterprise Patterns and MDA有一些复杂的模式,但你可以从Inventory archetype的CapacityManager中获得指导。不幸的是,第278页上的模型在网上不可用,但请查看ServiceInventory archetype。
教练向学生销售他们的服务。(上次我检查过,教练有薪水:)。如果我将你的示例映射到Inventory archetype以获得想法,我将使用:
- Inventory - 仪器/课程列表
- ServiceType - 仪器/课程详情、开始结束等
- ServiceInventoryEntry - 仪器+可用位置(使用容量管理器)
- ServiceInstance - 报名 - 上课地点(已预定,取消,完成)
- CapacityManager(由ServiceInventoryEntry使用)
- ReservationRequest - 报名请求
我认为添加的关键概念是CapacityManager:你可以调用ServiceInventoryEntry::getCourses()方法,它将使用CapacaityManager(服务或类)来显示/计算可用的教师或返回默认教师。因此,根据上下文多次调用它:默认或提出一系列可用位置/座位/教师。
使用这个模型,您应该能够找到自然的位置(何时何地)进行验证。来自《流畅的对象建模》的指导是将验证放在数据所在的位置。虽然不是硬性规定,但对象有正确的关注点和数据组合在一起的自然倾向。例如,容量管理器知道有关注册和仪器的信息。(来自MDA-CapacityManger:通过释放ServiceInstances管理容量利用率)
为了获得聚合根,请查看您要执行的交易/更改,以便确保它们强制执行不变式(规则)。在您的示例中,我会将ServiceType(Course)作为值对象,ServiceInventoryEntry和ReservationRequests作为聚合根。(取决于您希望采取多么复杂的规则)。您还可以根据MDA书籍将学生和教师添加为参与方。我倾向于使用存储库来获取我的聚合根,然后依赖于控制反转,如您引用的Jimmy的书中所述。
我喜欢MDA模式的原因是它让我想到了我或业务可能没有想象到的用例和模型概念。但是,要小心只对需要的内容进行建模,因为MDA模式可能很大甚至诱人。好处是它们被设计为模块化或“可降级”的。
因此,简而言之:
- 您的聚合根应确保您的领域处于有效状态(规则/不变式)
- 将验证放在数据所在的位置。您的模型将指导您。