Как вы моделируете роли / отношения с учетом доменного дизайна? - PullRequest
5 голосов
/ 27 марта 2009

Если у меня есть три объекта: Project, ProjectRole и Person, где Person может быть участником разных проектов и находиться в разных ролях проекта (например, «Project Lead» или «Member Project») - как бы вы моделировали такие отношения?

В базе данных в настоящее время у меня есть следующие таблицы: Project, Person, ProjectRole Project_Person с PersonId и ProjectId в качестве PK и ProjectRoleId в качестве отношения FK.

Я действительно в растерянности, так как все модели доменов, которые я придумываю, похоже, нарушают какое-то правило "DDD". Существуют ли «стандарты» для этой проблемы?

Я взглянул на модернизированное объектное моделирование, и есть пример того, как Project и ProjectMember будут выглядеть, но AddProjectMember () в Project будет вызывать ProjectMember.AddProject (). Таким образом, у Project есть Список членов ProjectMember, и каждый ProjectMember в свою очередь имеет ссылку на Project. Выглядит немного запутанным для меня.

обновление

После прочтения этой темы я попытаюсь сделать следующее: Существуют различные роли или, что лучше, модельные отношения, которые имеют определенный тип роли в моем домене. Например, ProjectMember - это особая роль, которая говорит нам кое-что об отношениях, которые человек играет в проекте. Он содержит ProjectMembershipType, который говорит нам больше о роли, которую он будет играть. Я точно знаю, что люди должны будут играть роли внутри проекта, поэтому я буду моделировать эти отношения.

ProjectMembershipTypes можно создавать и изменять. Это могут быть «Лидер проекта», «Разработчик», «Внешний консультант» или что-то другое.

Человек может иметь много ролей внутри проекта, и эти роли могут начинаться и заканчиваться в определенную дату. Такие отношения моделируются классом 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: т.е. «Руководитель проекта», «Разработчик», «Консультант»

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

    // etc...
}

Ответы [ 5 ]

3 голосов
/ 28 марта 2009

Вот как бы я справился с этим:

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

Класс ProjectMember объединяет их всех. Эта модель дает вам гибкость в назначении одного и того же человека в разные проекты с разными ролями (например, он может быть разработчиком в ProjectA и тестером в ProjectB).

Пожалуйста, не создавайте ролевые классы - этот урок уже выучен.

Я создал пример приложения , чтобы продемонстрировать это (оно также включает отношения):

  1. Выполнить " bin \ debug \ RolesRelationshipsSample.exe "
  2. Дважды щелкните значки библиотеки, чтобы создать объекты
  3. Перетащите их, чтобы назначить соответствующие отношения

Не стесняйтесь играть с кодом. Надеюсь, вы найдете это полезным.

1 голос
/ 05 апреля 2009

Вы моделируете отношения многие-ко-многим: над проектом может работать много людей, а человек может работать над несколькими проектами.

Вы моделируете отношение в качестве роли проекта, которая, помимо того, что служит двунаправленной ссылкой из Person <-> Project, также записывает RoleType и начало / конец этого Person, заполняя этот RoleType в этом проекте. (Заметьте, как в английском языке работает «что» означает FK базы данных или в коде указатель / ссылка?)

Из-за этих FK мы можем в базе данных проследить график от Person через роль Project до 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 = ?

Или мы можем следовать по нему в другом направлении, из проекта:

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 = ?

В идеале, мы хотели бы сделать то же самое в коде C #. Так что да, мы хотим, чтобы у Person был список, а у Project - список, а ProjectRole ссылается на Person и Project.

Да, Project::addPerson( Person& ) действительно должно быть Project::addProjectRole( ProjectRole& ), если только мы не решим, что Project::addPerson( Person& ) - это удобный метод формы:

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

У ProjectRole нет списка, у него есть ссылка на Person и ссылка на Project. Он также имеет в качестве значений дату начала, дату окончания и RoleType (который является либо перечислением, либо экземпляром класса, который имитирует значение перечисления, то есть существует только один объект на тип перечисления, и это не имеющие состояния, неизменные и идемпотентные, и, следовательно, разделяемые среди многих ролей Project).

Теперь это не должно означать, что получение Person из базы данных должно привести к тому, что вся база данных будет преобразована в граф объектов в коде; Ленивые прокси, которые извлекаются только при использовании, могут спасти нас от этого. Тогда, если мы в настоящее время занимаемся только Человеком, а не его Ролями (и Проектами, мы можем просто извлечь Человека. (Например, NHibernate, я думаю, делает это более или менее плавно).

В принципе, я думаю, что:

1) Это стандартный способ представления отношений «многие ко многим»; 2) Стандартно для отношения иметь дополнительные данные (когда, какого рода) а также; 3) Вы в значительной степени поняли правильную идею и просто добросовестно получаете обратную связь здесь.

0 голосов
/ 29 мая 2010

Похоже, что есть две основные сущности - Проект и Участник проекта. Участник проекта имеет атрибуты «Роль участника» и «Имя участника». Любой из этих атрибутов может принадлежать домену, то есть набору значений, которые могут поддерживаться в справочных таблицах как для удобства, так и для использования для поиска. Предполагается, что кому-то требуется информация обо всех участниках проекта, выполняющих определенную роль / работу.

Примечание. В таблицы поиска могут быть добавлены записи, но обычно значение записи не изменяется. После того, как значение выбрано из таблицы поиска, оно считается постоянным атрибутом таблицы-владельца - в данном случае таблицы «Участник проекта».

Я бы не ожидал увидеть сущность или таблицу «Персона» в любом бизнесе, кроме удобства, в качестве справочной таблицы, как в случае выше. Кадровые отделы будут вести список сотрудников, которые имеют конкретную информацию, которая требуется для расчета заработной платы и т. Д., Но в этом нет ничего принципиального о людях, которые бизнес должен знать NB. Определите бизнес-процесс, чтобы определить сущность, а не придумывайте его.

0 голосов
/ 27 марта 2009

То, что у вас есть, это отношение «многие ко многим» с дополнительными данными, ролью. У нас похожая структура, за исключением того, что в нашем случае человек может иметь несколько ролей в проекте, поэтому я боролся с одними и теми же вопросами. Одним из решений является создание класса ProjectPerson, который расширяет Person и добавляет свойство role:

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

В вашем классе Project теперь есть коллекция ProjectPerson, а в классе Person есть коллекция Project, поскольку нет смысла расширять класс Project для добавления роли. Вам нужно будет проделать дополнительную работу (найти человека в коллекции ProjectPerson), чтобы найти роль в проекте с точки зрения человека.

Второе решение - это стандартный способ обработки отношений «многие ко многим» с дополнительными данными. Создайте класс ProjectRole и смоделируйте его как многовариантность двух отношений один-ко-многим из Project и Person. То есть и Project, и Person имеют коллекцию ProjectRole.

Важно учитывать, насколько хорошо ваша стратегия доступа к данным будет поддерживать модель при выборе решения. Вы хотите избежать сценариев, когда загрузка коллекции требует одной или нескольких поездок в базу данных для каждого объекта в коллекции.

0 голосов
/ 27 марта 2009

Разве вы не путаете «описание» роли с ролью, которую человек играет в проекте? Может помочь добавление концепции «RoleDescription» (так сказать «класс ролей») и объектов «RoleInstance», ссылающихся на реальных людей в проектах.

...