2009-03-27 5 views
5

Si tengo tres entidades, Project, ProjectRole y Person, donde una Persona puede ser miembro de diferentes Proyectos y estar en diferentes Roles de Proyecto (como "Project Lead" o "Project Member"), ¿cómo modelaría tales ¿una relación?¿Cómo se modelan los roles/relaciones con el Diseño Dirigido por Dominio en mente?

En la base de datos, actualmente tengo los siguientes tablers: Project, Person, ProjectRole Project_Person con PersonId & ProjectId como PK y ProjectRoleId como una relación FK.

Estoy realmente perdido aquí ya que todos los modelos de dominio que se me ocurren parecen romper alguna regla de "DDD". ¿Hay algún 'estándar' para este problema?

Eché un vistazo a un Modelado simplificado de objetos y hay un ejemplo de cómo se verían Project y ProjectMember, pero AddProjectMember() en Project llamaría ProjectMember.AddProject(). Entonces Project tiene una Lista de Miembros del Proyecto, y cada ProjectMember a cambio tiene una referencia al Proyecto. Parece un poco complicado para mí.

actualización

Después de leer más sobre este tema, voy a tratar lo siguiente: Hay distintas funciones, o mejor, relaciones de modelos, que son de un cierto tipo de papel dentro de mi dominio. Por ejemplo, ProjectMember es un rol distinto que nos dice algo acerca de la relación que una persona juega dentro de un proyecto. Contiene un ProjectMembershipType que nos dice más sobre el rol que jugará. Sé con certeza que las personas tendrán que desempeñar papeles dentro de un proyecto, por lo que modelaré esa relación.

ProjectMembershipTypes se pueden crear y modificar. Estos pueden ser "Líder de proyecto", "Desarrollador", "Asesor externo" o algo diferente.

Una persona puede tener muchos roles dentro de un proyecto, y estos roles pueden comenzar y finalizar en una fecha determinada. Tales relaciones son modeladas por la clase 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. "Gerente de Proyecto", "desarrolladores", "asesor"

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

    // etc... 
} 

Respuesta

1

Está modelando una relación de varios a muchos: un proyecto puede tener muchas personas trabajando en él, y una persona puede trabajar en múltiples proyectos.

Está modelando la relación como Rol de proyecto, que además de servir como un enlace bidireccional de Persona < -> Proyecto, también registra un Tipo de rol y comienza/termina de esa Persona que llena ese Tipo de rol en ese Proyecto . (Observe cómo los ingleses trabajan "eso" para la base de datos FK o, en el código, un puntero/referencia?)

Debido a esas FK, podemos en la base de datos seguir el gráfico desde Person, mediante Project Role, al Proyecto:

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

O podemos seguirlo en la otra dirección, a partir del proyecto:

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

Idealmente, nos gustaría ser capaz de hacer lo mismo en el código C#. Así que sí, queremos que una Persona tenga una lista, y Proyecto tenga una lista, y un ProjectRole haga referencia a una Persona y un Proyecto.

Sí, Project::addPerson(Person&) realmente debería estar Project::addProjectRole(ProjectRole&), a menos que decidimos que Project::addPerson(Person&) es un método de conveniencia de la forma:

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

Un ProjectRole no tiene una lista, que tiene: una referencia a una persona y una referencia a un Proyecto. También tiene, como valores, una fecha de inicio, una fecha de finalización y un RoleType (que es una enumeración o una instancia de clase que imita un valor de enumeración, es decir, solo hay un objeto por tipo de enumeración, y es apátrida, inmutable e idempotente, y por lo tanto compartible entre muchos ProjectRoles).

Ahora, esto no debería significar que recuperar una Persona de la base de datos debería hacer que toda la base de datos se reificara en el gráfico de objetos en el código; los proxies perezosos que recuperan solo en el uso pueden salvarnos de eso. Entonces, si solo estamos preocupados con la Persona, y no sus Roles (y Proyectos, podemos simplemente recuperar la Persona. (NHibernate, por ejemplo, creo que hace esto más o menos perfectamente).

Básicamente , Creo que:

1) Esta es una forma estándar de representar relaciones de muchos a muchos; 2) Es estándar para una relación tener datos adicionales (cuándo, qué tipo de) y; 3) tienes la idea correcta, y estás siendo muy concienzudo a la hora de recibir comentarios.

0

¿no es confundir la "Descripción" de un papel con el papel que una persona tiene en un proyecto? Puede ser útil agregar el concepto "RoleDescription" (una "clase de rol", por así decirlo) y los objetos "RoleInstance" que se refieren a personas reales en proyectos.

+0

No tengo idea, eso es posible, no estoy seguro de lo que quieres decir – kitsune

+0

Me temo que soy de la era de la base de datos relacional pero comí demasiado OOD ... Me refiero a la 'Segunda Solución' en La respuesta de Jamie. – xtofl

0

Lo que tiene es una relación muchos a muchos con datos adicionales, el rol. Tenemos una estructura similar, excepto en nuestro caso, una persona puede tener múltiples funciones en un proyecto, por lo que tuve problemas con las mismas preguntas. Una solución es crear una clase que se extiende ProjectPerson persona y agrega la propiedad papel:

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

Su clase de proyecto ahora tiene una colección de ProjectPerson pero la clase Persona tiene una colección de proyecto, ya que no tiene sentido extender la clase Proyecto para agregar rol. Tendrá que hacer un trabajo adicional (busque la persona en la colección ProjectPerson) para encontrar el rol en un proyecto desde la perspectiva de la persona.

Una segunda solución es la forma estándar de manejar relaciones de muchos a muchos con datos adicionales. Cree una clase ProjectRole y ejemplifíquela como el lado más grande de dos relaciones uno a muchos desde Project y Person. Es decir, tanto Project como Person tienen una colección de ProjectRole.

Es importante considerar qué tan bien su estrategia de acceso a datos apoyará al modelo al elegir una solución. Desea evitar escenarios en los que la carga de la colección requiera uno o más viajes a la base de datos para cada objeto en la colección.

+0

Recomendaría en contra de la clase 'ProjectPerson', ya que tarde o temprano terminarás con un 'BillablePerson', un 'InternshipPerson' y un 'TerriblePerson', todos refiriéndose a la misma persona 'real'. Me quedaré con las relaciones has-a en lugar de con la herencia. – xtofl

+0

No entiendo su argumento: ¿cómo impulsaría la solución propuesta la creación de extensiones adicionales de Persona? Un ProjectPerson es solo una Persona en un rol en el contexto de un Proyecto. En términos de base de datos, se recupera uniendo la tabla de personas a la tabla de enlaces de muchos a muchos. –

3

Así es como me gustaría manejarlo:

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

El clase ProjectMember trae a todos juntos. Este modelo le brinda la flexibilidad de asignar la misma Persona a diferentes Proyectos con diferentes Funciones (por ejemplo, puede ser Desarrollador en Proyecto A y Tester en Proyecto B).

No cree clases específicas de roles: esa lección ya se aprendió.

He creado un sample app para demostrar esto (que incluye las relaciones también):

  1. Ejecutar "bin \ debug \ RolesRelationshipsSample.exe"
  2. doble clic en los iconos de la biblioteca para crear entidades
  3. arrastrar/soltar a asignar las relaciones apropiadas

dude en jugar con el código. Esperamos que te sea útil.

+0

¡Gracias! Entré en una dirección similar a la tuya, por favor revisa mi pregunta actualizada. – kitsune

+0

@Vijay Patel ¿Por qué no crear clases específicas de roles? ¿Qué pasa si cada rol puede realizar diferentes acciones en el proyecto? – richard

+0

@Richard: De mi ejemplo, la subclasificación de "Rol" no es un problema. Creo que estaba aludiendo más al escenario de Jamie Ide a continuación http://stackoverflow.com/a/689618/80369 –

0

Parece que hay dos entidades principales: proyecto y miembro del proyecto. El miembro del proyecto tiene los atributos 'Rol de miembro' y 'Nombre de miembro'. Cualquiera de estos atributos puede pertenecer a un dominio, es decir, un conjunto de valores que se pueden mantener en las tablas de búsqueda tanto por conveniencia como para su uso en la búsqueda. Se supone que alguien necesita información sobre todos los miembros del proyecto que desempeñan un rol/trabajo particular.

Nota. Las tablas de búsqueda pueden tener entradas agregadas, pero normalmente no cambiarían el valor de una entrada. Una vez que se selecciona un valor de la tabla de búsqueda, se considera un accesorio permanente de la tabla propietaria, en este caso, la tabla Miembro del proyecto.

No esperaría ver una entidad o tabla 'Persona' en ningún negocio que no sea la conveniencia de una tabla de búsqueda como en el caso anterior. Los departamentos de recursos humanos mantendrán una lista de los empleados que tienen información específica requerida por Payroll, etc., pero no hay nada fundamental entre las personas que la empresa deba saber. NB Localice el proceso comercial para identificar una entidad; no lo invente.

Cuestiones relacionadas