Oop 实体关系设计:相互排斥的Has-A关系

Oop 实体关系设计:相互排斥的Has-A关系,oop,entity,domain-driven-design,relationship,invariants,Oop,Entity,Domain Driven Design,Relationship,Invariants,我正在为一个可以与不同类型的其他对象建立关系的对象确定最佳设计方法。但一次只能属于一种类型。相互排斥的一对多has-A关系是我能想到的描述这种情况的最好的短语 例如: 我有项目和程序。每个任务可以包含多个任务。一项任务可以属于一个项目或一个过程,但不能同时属于多个。任务可以从项目移动到过程,反之亦然 因此,在任何时候,任务都与项目有一对多的关系,或者与过程有一对多的关系 目前,我正在考虑这样一项任务: class Task(id, name, project_id, procedure_i

我正在为一个可以与不同类型的其他对象建立关系的对象确定最佳设计方法。但一次只能属于一种类型。相互排斥的一对多has-A关系是我能想到的描述这种情况的最好的短语

例如:

我有项目和程序。每个任务可以包含多个任务。一项任务可以属于一个项目或一个过程,但不能同时属于多个。任务可以从项目移动到过程,反之亦然

因此,在任何时候,任务都与项目有一对多的关系,或者与过程有一对多的关系

目前,我正在考虑这样一项任务:

   class Task(id, name, project_id, procedure_id):
       if project_id and procedure_id:
           throw Exception("A Task can not be assigned to a Project and a Procedure at the same time")

       self.id = id
       self.name = name
       self.project_id = project_id
       self.procedure_id = procedure_id

   def move_to_project(project_id):
       self.procedure_id = None
       self.project_id = project_id

   ...
任务将保护不变量,因为它只能属于单个项目或过程。项目和过程上的工厂方法可以创建具有相关关系ID的任务

任务与它们都不在同一个聚合中,因此我想通过标识对关系进行建模

在我看来,保护这个不变量的唯一方法就是以这种方式对任务建模。否则,我最终会得到一个巨大的程序/项目/任务集合


这似乎是一种明智的方法,或者有人对值得探索的替代设计方法有什么建议吗?

如果任务不能同时与项目和过程相关联,我就不会有一个具有两个ID的构造函数。我将有一个用于项目id,另一个用于过程id。我将使用根据UL命名的工厂方法。这样,您可以构造一个与项目或过程关联的任务,但不能同时与两者关联。

如果一个任务不能同时与项目和过程关联,我就不会有一个具有两个ID的构造函数。我将有一个用于项目id,另一个用于过程id。我将使用根据UL命名的工厂方法。这样,您可以构造与项目或过程关联的任务,但不能同时与两者关联。

您应该在TaskService应用程序层上具有TaskService.AssignToProject()和TaskService.AssigneToProcedure()方法。若任务是一个单独的聚合,则意味着它可以在无需程序或项目的情况下过自己的生活。它们可能会空一段时间。调用TaskService.AssignToProject()或TaskService.AssigneToProcedure()时,应分配ProcedureId或ProjectId。因此,在TaskService.AssignToProject()中,您可以从db获取任务和项目,并调用Task.AssignTo(项目)。然后验证ProcedureId是否为null,并执行所需操作(抛出异常或UnassignFromProcedure())。分配完成后,将引发TaskAssignedToProject域事件,捕获它并在另一个事务调用ProjectService.CommitTask()中执行。可以将该任务添加到Project aggregate上CommittedTasks的内部集合。

在TaskService应用程序层上应该有TaskService.AssignToProject()和TaskService.AssignToProcedure()方法。若任务是一个单独的聚合,则意味着它可以在无需程序或项目的情况下过自己的生活。它们可能会空一段时间。调用TaskService.AssignToProject()或TaskService.AssigneToProcedure()时,应分配ProcedureId或ProjectId。因此,在TaskService.AssignToProject()中,您可以从db获取任务和项目,并调用Task.AssignTo(项目)。然后验证ProcedureId是否为null,并执行所需操作(抛出异常或UnassignFromProcedure())。分配完成后,将引发TaskAssignedToProject域事件,捕获它并在另一个事务调用ProjectService.CommitTask()中执行。您可以将该任务添加到Project aggregate上的已提交任务的内部集合中。

谢谢。我正在使用Python,因此无法在构造函数上使用方法重载。但是是的,创建任务的工厂方法可以做到这一点。ie项目将有一个工厂方法assign\u task(task\u id,task\u name)。程序方法与工厂方法类似。谢谢。我正在使用Python,因此无法在构造函数上使用方法重载。但是是的,创建任务的工厂方法可以做到这一点。ie项目将有一个工厂方法assign\u task(task\u id,task\u name)。程序方法将有一个类似的工厂方法。是的,完全正确。我还计划在希望创建任务并将其立即关联时,在实例中包括方便工厂方法,例如project.add_task()。我喜欢添加事件来创建任务的内部集合。我相信我在沃恩·弗农(Vaughn Vernon)的AgilePM例子中看到了类似的情况,即积压。听起来我的思路是对的。谢谢你的确认。@Steven完全正确。您不能在Project aggregate中为CommittedTasks重用Task类,因为它可能需要与Project AssignedTask相关的完全唯一的行为,例如某些点计算,它们将根据这些点进行分组或排序。project.add_task()-确保清除“创建、更新、删除”术语。使用无处不在的语言。感觉“添加”是一种气味:)是的,没错。我还计划在希望创建任务并将其立即关联时,在实例中包括方便工厂方法,例如project.add_task()。我喜欢添加事件来创建任务的内部集合。我相信我在沃恩·弗农(Vaughn Vernon)的AgilePM例子中看到了类似的情况,即积压。听起来我的思路是对的。谢谢你的确认。@Steven完全正确。您不能在Project aggregate中为CommittedTasks重用Task类,因为它可能需要与Project AssignedTask相关的完全唯一的行为,例如某些点计算,它们将根据这些点进行分组或排序。project.add_task()-确保清除“创建、更新、删除”术语。使用无处不在的语言。感觉“添加”是一种气味:)