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

StackOverflow https://stackoverflow.com/questions/689436

Вопрос

Если у меня есть три сущности: «Проект», «Роль проекта» и «Человек», где «Человек» может быть участником разных проектов и выполнять разные роли в проекте (например, «Руководитель проекта» или «Участник проекта») — как бы вы смоделировали такие отношения ?

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

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

Я рассмотрел упрощенное моделирование объектов, и там есть пример того, как будут выглядеть Project и ProjectMember, но AddProjectMember() в Project будет вызывать ProjectMember.AddProject().Таким образом, у Project есть список ProjectMembers, а каждый 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...
}

Тип членства в проекте:то есть.«Менеджер проекта», «Разработчик», «Консультант»

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

    // etc...
}
Это было полезно?

Решение

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

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

Благодаря этим FK мы можем в базе данных следовать графику от человека через роль проекта к проекту:

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

Или мы можем пойти в другом направлении, из 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 = ?

В идеале нам хотелось бы иметь возможность делать то же самое в коде 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 (который либо является перечислением, либо экземпляром класса, имитирующим значение перечисления, то есть существует только один объект для каждого типа перечисления, и это без сохранения состояния, неизменяемый и идемпотентный и, следовательно, доступный для использования многими ролями проекта).

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

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

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

Другие советы

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Примечание.В таблицы поиска можно добавлять записи, но обычно их значение не изменяется.Как только значение выбрано из таблицы поиска, оно считается постоянным элементом таблицы-владельца — в данном случае таблицы участников проекта.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top