您如何为Domain Driven Design建立角色/关系模型?

时间:2021-12-14 00:09:42

If I have three entities, Project, ProjectRole and Person, where a Person can be a member of different Projects and be in different Project Roles (such as "Project Lead", or "Project Member") - how would you model such a relationship?

如果我有三个实体,Project,ProjectRole和Person,其中Person可以是不同项目的成员并且处于不同的项目角色(例如“项目负责人”或“项目成员”) - 您将如何建模这样的关系?

In the database, I currently have the following tablers: Project, Person, ProjectRole Project_Person with PersonId & ProjectId as PK and a ProjectRoleId as a FK Relationship.

在数据库中,我目前有以下表格:Project,Person,ProjectRole Project_Person,其中PersonId&ProjectId为PK,ProjectRoleId为FK关系。

I'm really at a loss here since all domain models I come up with seem to break some "DDD" rule. Are there any 'standards' for this problem?

我真的很茫然,因为我提出的所有领域模型似乎打破了一些“DDD”规则。这个问题有“标准”吗?

I had a look at a Streamlined Object Modeling and there is an example what a Project and ProjectMember would look like, but AddProjectMember() in Project would call ProjectMember.AddProject(). So Project has a List of ProjectMembers, and each ProjectMember in return has a reference to the Project. Looks a bit convoluted to me.

我看了一个Streamlined Object Modeling,有一个例子,Project和ProjectMember会是什么样子,但是Project中的AddProjectMember()会调用ProjectMember.AddProject()。所以Project有一个ProjectMembers列表,每个ProjectMember都有一个Project的引用。看起来有点费解我。

update

After reading more about this subject, I will try the following: There are distinct roles, or better, model relationships, that are of a certain role type within my domain. For instance, ProjectMember is a distinct role that tells us something about the relationship a Person plays within a Project. It contains a ProjectMembershipType that tells us more about the Role it will play. I do know for certain that persons will have to play roles inside a project, so I will model that relationship.

在阅读了有关此主题的更多信息后,我将尝试以下内容:在我的域中存在特定角色类型的不同角色或更好的模型关系。例如,ProjectMember是一个独特的角色,告诉我们一个人在项目中扮演的关系。它包含一个ProjectMembershipType,它告诉我们有关它将扮演的角色的更多信息。我确实知道人们必须在项目中扮演角色,所以我将模仿这种关系。

ProjectMembershipTypes can be created and modified. These can be "Project Leader", "Developer", "External Adviser", or something different.

可以创建和修改ProjectMembershipTypes。这些可以是“项目负责人”,“开发人员”,“外部顾问”或其他不同的东西。

A person can have many roles inside a project, and these roles can start and end at a certain date. Such relationships are modeled by the class ProjectMember.

一个人可以在项目中拥有许多角色,这些角色可以在特定日期开始和结束。这种关系由ProjectMember类建模。

public class ProjectMember : IRole
{
    public virtual int ProjectMemberId { get; set; }
    public virtual ProjectMembershipType ProjectMembershipType { get; set; }

    public virtual Person Person { get; set; }
    public virtual Project Project { get; set; }
    public virtual DateTime From { get; set; }
    public virtual DateTime Thru { get; set; }
    // etc...
}

ProjectMembershipType: ie. "Project Manager", "Developer", "Adviser"

ProjectMembershipType:即。 “项目经理”,“开发人员”,“顾问”

public class ProjectMembershipType : IRoleType
{
    public virtual int ProjectMembershipTypeId { get; set; }
    public virtual string Name { get; set; }
    public virtual string Description { get; set; }

    // etc...
}

5 个解决方案

#1


You're modeling a many-to-many relationship: a project can have many people working on it, and a person can work on multiple projects.

您正在建模多对多关系:一个项目可以有很多人在处理它,一个人可以处理多个项目。

You're modeling the relation as a Project Role, which in addition to serving as a bi-directional link from Person <-> Project, also records a RoleType and start/end of that Person filling that RoleType on that Project. (Notice how the English work "that" stands in for the database FK or, in code, a pointer/reference?)

您将关系建模为项目角色,除了作为Person < - > Project的双向链接之外,还记录RoleType以及在该项目上填充该RoleType的Person的开始/结束。 (请注意英语是如何工作的“那个”代表数据库FK,或者在代码中代表指针/引用?)

Because of those FKs, we can in the database follow the graph from Person, through Project Role, to Project:

由于这些FK,我们可以在数据库中遵循从Person,Project Role到Project的图形:

select a.person_id, b.project_role_id, c.project_id
from person a join project_role b on (a.id = b.person_id)
join project c on (b.project_id = c.id)
where a.person_id = ?

Or we can follow it in the other direction, from Project:

或者我们可以从项目的另一个方向跟随它:

select a.person_id, b.project_role_id, c.project_id
from person a join project_role b on (a.id = b.person_id)
join project c on (b.project_id = c.id)
where c.project_id = ?

Ideally, we'd like to be able to do the same in the C# code. So yes, we want a Person to have a list, and Project to have a list, and a ProjectRole references to a Person and a Project.

理想情况下,我们希望能够在C#代码中执行相同的操作。所以是的,我们希望一个Person有一个列表,Project需要一个列表,一个ProjectRole引用一个Person和一个Project。

Yes, Project::addPerson( Person& ) should really be Project::addProjectRole( ProjectRole& ), unless we decide that Project::addPerson( Person& ) is a convenience method of the form:

是的,Project :: addPerson(Person&)应该是Project :: addProjectRole(ProjectRole&),除非我们确定Project :: addPerson(Person&)是表单的便捷方法:

void Project::addPerson( Person& p ) {
  this.addProjectRole( new ProjectRole( p, &this, RoleType::UNASSIGNED ) ;
}

A ProjectRole doesn't have a list, it has-a reference to a Person and a reference to a Project. It also has, as values, a start date, an end date, and a RoleType (which either is an enum, or a class instance that mimics an enum value -- that is, there is only one object per enum type, and it's stateless, immutable and idempotent, and thus sharable among many ProjectRoles).

ProjectRole没有列表,它具有对Person的引用和对Project的引用。它还有一个开始日期,一个结束日期和一个RoleType(它是枚举或模仿枚举值的类实例) - 也就是说,每个枚举类型只有一个对象,它是无国籍,不可变和幂等,因而在许多ProjectRoles中可以共享。

Now this shouldn't mean that retrieving a Person from the database should cause the whole database to be reified in the object graph in the code; lazy proxies that retrieve only on use can save us from that. Then if we're only currently concerned with the Person, and not his Roles (and Projects, we can just retrieve the Person. (NHibernate, for instance, I think does this more-or-less seamlessly.)

现在这并不意味着从数据库中检索Person应该导致整个数据库在代码中的对象图中被确定;只在使用时检索的惰性代理可以从中拯救我们。然后,如果我们目前只关注Person,而不是他的角色(和项目,我们可以只检索Person。(例如,NHibernate,我认为这或多或少是无缝的。)

Basically, I think that:

基本上,我认为:

1) This is a standard way of representing many-to-many relations; 2) It's standard for a relation to have additional data (when, what kind of) and; 3) you've pretty much got the right idea, and are just being rightly conscientious in getting feedback here.

1)这是表示多对多关系的标准方式; 2)关系的标准是有额外的数据(何时,何种)和; 3)你几乎得到了正确的想法,并且正确地在这里获得反馈是正确的。

#2


Here's how I would handle it:

这是我将如何处理它:

class Person
{
  string Name { get; set; }
  IList<Role> Roles { get; private set; }
}

class Role
{
  string Name { get; set; }
  string Description { get; set; }
  IList<Person> Members { get; private set; }
}

class Project
{
  string Name { get; set; }
  string Description { get; set; }
  IList<ProjectMember> Members { get; private set; }
}

class ProjectMember
{
  Project Project { get; private set; }
  Person Person { get; set; }
  Role Role { get; set; }
}

The ProjectMember class brings them all together. This model gives you the flexibility to assign the same Person to different Projects with different Roles (e.g. he might be a Developer on ProjectA, and a Tester on ProjectB).

ProjectMember类将它们组合在一起。此模型使您可以灵活地将同一个人分配给具有不同角色的不同项目(例如,他可能是ProjectA上的开发人员,以及ProjectB上的测试人员)。

Please don't create role specific classes - that lesson has been learnt already.

请不要创建特定于角色的课程 - 该课程已经学习过。

I've created a sample app to demonstrate this (it includes relationships too):

我已经创建了一个示例应用程序来演示这个(它也包括关系):

  1. Run "bin\debug\RolesRelationshipsSample.exe"
  2. Double-click the library icons to create entities
  3. 双击库图标以创建实体

  4. Drag/drop them to assign the appropriate relationships
  5. 拖放它们以分配适当的关系

Feel free to play with the code. Hope you find it useful.

随意玩代码。希望你觉得它有用。

#3


Aren't you confusing the "Description" of a role with the role a person has in a project? Adding the "RoleDescription" concept (a 'role-class' so to speak), and "RoleInstance" objects referring to actual persons in projects may help.

您是否混淆角色的“描述”与一个人在项目中的角色?添加“RoleDescription”概念(可以说是“角色类”)和“RoleInstance”对象引用项目中的实际人员可能会有所帮助。

#4


What you have is a many-to-many relationship with additional data, the role. We have a similar structure except in our case a person may have multiple roles on a project, so I struggled with the same questions. One solution is to create a ProjectPerson class that extends Person and adds the role property:

你所拥有的是与其他数据(角色)的多对多关系。我们有一个类似的结构,除了在我们的情况下,一个人可能在项目中有多个角色,所以我在同样的问题上挣扎。一种解决方案是创建一个扩展Person的ProjectPerson类并添加role属性:

public class ProjectPerson : Person
{
    public string Role { get; set; }
}

Your Project class now has a collection of ProjectPerson but the Person class has a collection of Project because it doesn't make sense to extend the Project class to add role. You'll have to do some additional work (look up the Person in the ProjectPerson collection) to find the role on a Project from the Person's perspective.

您的Project类现在有一个ProjectPerson集合,但Person类有一个Project集合,因为扩展Project类以添加角色没有意义。您将不得不做一些额外的工作(在ProjectPerson集合中查找Person),从Person的角度查找项目中的角色。

A second solution is the standard way to handle many-to-many relationships with additional data. Create a ProjectRole class and model it as the many side of two one-to-many relationships from Project and Person. That is, both Project and Person each have a collection of ProjectRole.

第二种解决方案是处理与其他数据的多对多关系的标准方法。创建一个ProjectRole类,并将其建模为Project和Person中两个一对多关系的多个方面。也就是说,Project和Person都有一个ProjectRole集合。

It's important to consider how well your data access strategy will support the model in choosing a solution. You want to avoid scenarios where loading the collection requires one or more trips to the database for each object in the collection.

在选择解决方案时,考虑数据访问策略在支持模型方面的效果非常重要。您希望避免加载集合需要一次或多次访问集合中每个对象的数据库的情况。

#5


It appears that there are two main entities - Project and Project Member. The Project Member has the attributes 'Member Role' and 'Member Name'. Either of these attributes may belong to a domain ie a set of values that can be maintained in lookup tables both for convenience and to use for searching. It is assumed that someone requires information about all project members carrying out a particular role/job.

似乎有两个主要实体 - 项目和项目成员。项目成员具有“成员角色”和“成员名称”属性。这些属性中的任何一个都可以属于域,即为了方便和用于搜索而可以在查找表中维护的一组值。假设某人需要有关执行特定角色/工作的所有项目成员的信息。

Note. Lookup tables can have entries added but would not normally have the value of an entry changed. Once a value is selected from the lookup table then it is considered a permanent fixture of the owning table - in this case the Project Member table.

注意。查找表可以添加条目,但通常不会更改条目的值。从查找表中选择一个值后,它将被视为拥有表的永久固定 - 在本例中为Project Member表。

I wouldn't expect to see a 'Person' entity or table in any business other than the convenience as a lookup table as in the case above. HR departments will keep a list of employees that have specific information that is required by Payroll etc. but there is nothing fundamental abut People that the business will need to know. NB Locate the business process to identify an entity - don't make it up.

我不希望在任何业务中看到“人”实体或表,而不是像上面的情况那样作为查找表的便利性。人力资源部门将保留一份员工清单,其中包含工资单等所需的特定信息,但企业需要了解的人员基本没有根据。 NB找到业务流程以识别实体 - 不要弥补。

#1


You're modeling a many-to-many relationship: a project can have many people working on it, and a person can work on multiple projects.

您正在建模多对多关系:一个项目可以有很多人在处理它,一个人可以处理多个项目。

You're modeling the relation as a Project Role, which in addition to serving as a bi-directional link from Person <-> Project, also records a RoleType and start/end of that Person filling that RoleType on that Project. (Notice how the English work "that" stands in for the database FK or, in code, a pointer/reference?)

您将关系建模为项目角色,除了作为Person < - > Project的双向链接之外,还记录RoleType以及在该项目上填充该RoleType的Person的开始/结束。 (请注意英语是如何工作的“那个”代表数据库FK,或者在代码中代表指针/引用?)

Because of those FKs, we can in the database follow the graph from Person, through Project Role, to Project:

由于这些FK,我们可以在数据库中遵循从Person,Project Role到Project的图形:

select a.person_id, b.project_role_id, c.project_id
from person a join project_role b on (a.id = b.person_id)
join project c on (b.project_id = c.id)
where a.person_id = ?

Or we can follow it in the other direction, from Project:

或者我们可以从项目的另一个方向跟随它:

select a.person_id, b.project_role_id, c.project_id
from person a join project_role b on (a.id = b.person_id)
join project c on (b.project_id = c.id)
where c.project_id = ?

Ideally, we'd like to be able to do the same in the C# code. So yes, we want a Person to have a list, and Project to have a list, and a ProjectRole references to a Person and a Project.

理想情况下,我们希望能够在C#代码中执行相同的操作。所以是的,我们希望一个Person有一个列表,Project需要一个列表,一个ProjectRole引用一个Person和一个Project。

Yes, Project::addPerson( Person& ) should really be Project::addProjectRole( ProjectRole& ), unless we decide that Project::addPerson( Person& ) is a convenience method of the form:

是的,Project :: addPerson(Person&)应该是Project :: addProjectRole(ProjectRole&),除非我们确定Project :: addPerson(Person&)是表单的便捷方法:

void Project::addPerson( Person& p ) {
  this.addProjectRole( new ProjectRole( p, &this, RoleType::UNASSIGNED ) ;
}

A ProjectRole doesn't have a list, it has-a reference to a Person and a reference to a Project. It also has, as values, a start date, an end date, and a RoleType (which either is an enum, or a class instance that mimics an enum value -- that is, there is only one object per enum type, and it's stateless, immutable and idempotent, and thus sharable among many ProjectRoles).

ProjectRole没有列表,它具有对Person的引用和对Project的引用。它还有一个开始日期,一个结束日期和一个RoleType(它是枚举或模仿枚举值的类实例) - 也就是说,每个枚举类型只有一个对象,它是无国籍,不可变和幂等,因而在许多ProjectRoles中可以共享。

Now this shouldn't mean that retrieving a Person from the database should cause the whole database to be reified in the object graph in the code; lazy proxies that retrieve only on use can save us from that. Then if we're only currently concerned with the Person, and not his Roles (and Projects, we can just retrieve the Person. (NHibernate, for instance, I think does this more-or-less seamlessly.)

现在这并不意味着从数据库中检索Person应该导致整个数据库在代码中的对象图中被确定;只在使用时检索的惰性代理可以从中拯救我们。然后,如果我们目前只关注Person,而不是他的角色(和项目,我们可以只检索Person。(例如,NHibernate,我认为这或多或少是无缝的。)

Basically, I think that:

基本上,我认为:

1) This is a standard way of representing many-to-many relations; 2) It's standard for a relation to have additional data (when, what kind of) and; 3) you've pretty much got the right idea, and are just being rightly conscientious in getting feedback here.

1)这是表示多对多关系的标准方式; 2)关系的标准是有额外的数据(何时,何种)和; 3)你几乎得到了正确的想法,并且正确地在这里获得反馈是正确的。

#2


Here's how I would handle it:

这是我将如何处理它:

class Person
{
  string Name { get; set; }
  IList<Role> Roles { get; private set; }
}

class Role
{
  string Name { get; set; }
  string Description { get; set; }
  IList<Person> Members { get; private set; }
}

class Project
{
  string Name { get; set; }
  string Description { get; set; }
  IList<ProjectMember> Members { get; private set; }
}

class ProjectMember
{
  Project Project { get; private set; }
  Person Person { get; set; }
  Role Role { get; set; }
}

The ProjectMember class brings them all together. This model gives you the flexibility to assign the same Person to different Projects with different Roles (e.g. he might be a Developer on ProjectA, and a Tester on ProjectB).

ProjectMember类将它们组合在一起。此模型使您可以灵活地将同一个人分配给具有不同角色的不同项目(例如,他可能是ProjectA上的开发人员,以及ProjectB上的测试人员)。

Please don't create role specific classes - that lesson has been learnt already.

请不要创建特定于角色的课程 - 该课程已经学习过。

I've created a sample app to demonstrate this (it includes relationships too):

我已经创建了一个示例应用程序来演示这个(它也包括关系):

  1. Run "bin\debug\RolesRelationshipsSample.exe"
  2. Double-click the library icons to create entities
  3. 双击库图标以创建实体

  4. Drag/drop them to assign the appropriate relationships
  5. 拖放它们以分配适当的关系

Feel free to play with the code. Hope you find it useful.

随意玩代码。希望你觉得它有用。

#3


Aren't you confusing the "Description" of a role with the role a person has in a project? Adding the "RoleDescription" concept (a 'role-class' so to speak), and "RoleInstance" objects referring to actual persons in projects may help.

您是否混淆角色的“描述”与一个人在项目中的角色?添加“RoleDescription”概念(可以说是“角色类”)和“RoleInstance”对象引用项目中的实际人员可能会有所帮助。

#4


What you have is a many-to-many relationship with additional data, the role. We have a similar structure except in our case a person may have multiple roles on a project, so I struggled with the same questions. One solution is to create a ProjectPerson class that extends Person and adds the role property:

你所拥有的是与其他数据(角色)的多对多关系。我们有一个类似的结构,除了在我们的情况下,一个人可能在项目中有多个角色,所以我在同样的问题上挣扎。一种解决方案是创建一个扩展Person的ProjectPerson类并添加role属性:

public class ProjectPerson : Person
{
    public string Role { get; set; }
}

Your Project class now has a collection of ProjectPerson but the Person class has a collection of Project because it doesn't make sense to extend the Project class to add role. You'll have to do some additional work (look up the Person in the ProjectPerson collection) to find the role on a Project from the Person's perspective.

您的Project类现在有一个ProjectPerson集合,但Person类有一个Project集合,因为扩展Project类以添加角色没有意义。您将不得不做一些额外的工作(在ProjectPerson集合中查找Person),从Person的角度查找项目中的角色。

A second solution is the standard way to handle many-to-many relationships with additional data. Create a ProjectRole class and model it as the many side of two one-to-many relationships from Project and Person. That is, both Project and Person each have a collection of ProjectRole.

第二种解决方案是处理与其他数据的多对多关系的标准方法。创建一个ProjectRole类,并将其建模为Project和Person中两个一对多关系的多个方面。也就是说,Project和Person都有一个ProjectRole集合。

It's important to consider how well your data access strategy will support the model in choosing a solution. You want to avoid scenarios where loading the collection requires one or more trips to the database for each object in the collection.

在选择解决方案时,考虑数据访问策略在支持模型方面的效果非常重要。您希望避免加载集合需要一次或多次访问集合中每个对象的数据库的情况。

#5


It appears that there are two main entities - Project and Project Member. The Project Member has the attributes 'Member Role' and 'Member Name'. Either of these attributes may belong to a domain ie a set of values that can be maintained in lookup tables both for convenience and to use for searching. It is assumed that someone requires information about all project members carrying out a particular role/job.

似乎有两个主要实体 - 项目和项目成员。项目成员具有“成员角色”和“成员名称”属性。这些属性中的任何一个都可以属于域,即为了方便和用于搜索而可以在查找表中维护的一组值。假设某人需要有关执行特定角色/工作的所有项目成员的信息。

Note. Lookup tables can have entries added but would not normally have the value of an entry changed. Once a value is selected from the lookup table then it is considered a permanent fixture of the owning table - in this case the Project Member table.

注意。查找表可以添加条目,但通常不会更改条目的值。从查找表中选择一个值后,它将被视为拥有表的永久固定 - 在本例中为Project Member表。

I wouldn't expect to see a 'Person' entity or table in any business other than the convenience as a lookup table as in the case above. HR departments will keep a list of employees that have specific information that is required by Payroll etc. but there is nothing fundamental abut People that the business will need to know. NB Locate the business process to identify an entity - don't make it up.

我不希望在任何业务中看到“人”实体或表,而不是像上面的情况那样作为查找表的便利性。人力资源部门将保留一份员工清单,其中包含工资单等所需的特定信息,但企业需要了解的人员基本没有根据。 NB找到业务流程以识别实体 - 不要弥补。