2009-03-27 8 views
5

Si j'ai trois entités, Project, ProjectRole et Person, où une personne peut être membre de différents projets et être dans différents rôles de projet (tels que "Project Lead" ou "Project Member") - comment modéliseriez-vous une relation?Comment modélisez-vous les rôles/relations avec Domain Driven Design?

Dans la base de données, j'ai actuellement les tableurs suivants: Project, Person, ProjectRole Project_Person avec PersonId & ProjectId comme PK et un ProjectRoleId comme une relation FK.

Je suis vraiment perdu ici car tous les modèles de domaines que je viens de proposer semblent casser une règle "DDD". Existe-t-il des «normes» pour ce problème?

J'ai jeté un coup d'oeil à une modélisation d'objet rationalisée et il y a un exemple à quoi ressemblerait un Project et ProjectMember, mais AddProjectMember() dans Project appelait ProjectMember.AddProject(). Donc Project a une liste de ProjectMembers, et chaque ProjectMember en retour a une référence au Projet. Ça me semble un peu compliqué.

mise à jour

Après avoir lu plus sur ce sujet, je vais essayer le suivant: Il y a des rôles distincts, ou mieux, les relations de modèle, qui sont d'un certain type rôle dans mon domaine. Par exemple, ProjectMember est un rôle distinct qui nous dit quelque chose au sujet de la relation qu'une personne joue dans un projet. Il contient un ProjectMembershipType qui nous en dit plus sur le rôle qu'il jouera. Je sais avec certitude que des personnes devront jouer des rôles dans un projet, alors je vais modéliser cette relation.

ProjectMembershipTypes peuvent être créés et modifiés. Ceux-ci peuvent être "chef de projet", "développeur", "conseiller externe", ou quelque chose de différent. Une personne peut avoir plusieurs rôles dans un projet et ces rôles peuvent commencer et se terminer à une certaine date. De telles relations sont modélisées par la classe 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. « Chef de projet », « Développeur », « conseiller »

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

    // etc... 
} 

Répondre

1

Vous modélisez une relation plusieurs-à-plusieurs: un projet peut avoir de nombreuses personnes travaillant dessus et une personne peut travailler sur plusieurs projets. Vous modélisez la relation en tant que rôle de projet qui, en plus de servir de lien bidirectionnel à partir de la personne < -> Projet, enregistre également un RoleType et le début/la fin de cette personne remplissant ce type de rôle sur ce projet . (Notez comment le travail anglais "cela" représente la base de données FK ou, dans le code, un pointeur/référence?)

En raison de ces FK, nous pouvons dans la base de données suivre le graphique de la personne, à travers le rôle de projet, le projet:

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

Ou nous pouvons le suivre dans l'autre sens, du projet:

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

Idéalement, nous aimerions pouvoir faire la même chose dans le code C#. Alors oui, nous voulons qu'une personne ait une liste, et que Project ait une liste, et une référence ProjectRole à une personne et un projet.

Oui, Project::addPerson(Person&) devrait vraiment être Project::addProjectRole(ProjectRole&), à moins que nous décidons que Project::addPerson(Person&) est une méthode pratique de la forme:

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

Un ProjectRole ne dispose pas d'une liste, il a-une référence à une personne et une référence à un projet. Il a également, comme valeurs, une date de début, une date de fin et un RoleType (qui est soit une énumération, soit une instance de classe qui imite une valeur enum, c'est-à-dire qu'il n'y a qu'un seul objet par type enum, et apatride, immuable et idempotente, et donc partageable entre de nombreux ProjectRoles).Maintenant cela ne devrait pas signifier que l'extraction d'une personne de la base de données devrait entraîner la réification de la base de données entière dans le graphe d'objet dans le code; Les proxies paresseux qui récupèrent seulement sur l'utilisation peuvent nous sauver de cela. Ensuite, si nous ne sommes actuellement concernés que par la Personne, et non par ses Rôles (et Projets, nous pouvons simplement récupérer la Personne. (NHibernate, par exemple, je pense que cela est plus ou moins transparent.)

Fondamentalement , Je pense que:

1) Ceci est une façon standard de représenter plusieurs-à-plusieurs relations; 2) Il est standard pour une relation d'avoir des données supplémentaires (quand, quel genre de) et; 3) vous avez à peu près la bonne idée, et vous êtes justement consciencieux pour obtenir des commentaires ici.

0

N'êtes-vous pas confondre la « Description » d'un rôle avec le rôle d'une personne a dans un projet? L'ajout du concept "RoleDescription" (une sorte de "classe de rôle") et des objets "RoleInstance" faisant référence à des personnes réelles dans les projets peuvent aider.

+0

Je n'ai aucune idée, c'est possible, je ne suis pas sûr de savoir ce que vous voulez dire – kitsune

+0

J'ai peur de sortir de l'ère de la base de données relationnelle mais j'ai mangé trop d'OOD ... Je fais référence à la 'Deuxième Solution' La réponse de Jamie. – xtofl

0

Ce que vous avez est une relation plusieurs-à-plusieurs avec des données supplémentaires, le rôle. Nous avons une structure similaire, sauf dans notre cas, une personne peut avoir plusieurs rôles sur un projet, donc j'ai lutté avec les mêmes questions. Une solution consiste à créer une classe ProjectPerson qui étend personne et ajoute la propriété rôle:

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

Votre classe Project a maintenant une collection de ProjectPerson mais la classe Person a une collection de projet, car il ne fait pas de sens étendre la classe Project pour ajouter un rôle. Vous devrez effectuer un travail supplémentaire (recherchez la personne dans la collection ProjectPerson) pour trouver le rôle d'un projet du point de vue de la personne.

Une deuxième solution est la manière standard de gérer des relations plusieurs-à-plusieurs avec des données supplémentaires. Créez une classe ProjectRole et modélisez-la en tant que partie de plusieurs relations un-à-plusieurs provenant de Project et Person. Autrement dit, Project et Person ont chacun une collection de ProjectRole.

Il est important de considérer dans quelle mesure votre stratégie d'accès aux données va soutenir le modèle dans le choix d'une solution. Vous voulez éviter les scénarios dans lesquels le chargement de la collection nécessite un ou plusieurs déplacements dans la base de données pour chaque objet de la collection.

+0

Je déconseille la classe 'ProjectPerson', car tôt ou tard vous vous retrouverez avec un 'BillablePerson', un 'InternshipPerson' et un 'TerriblePerson', tous se référant à la même personne 'réelle'. Je resterais avec has-a des relations au lieu de la solution d'héritage. – xtofl

+0

Je ne comprends pas votre argument: comment la solution proposée entraînerait-elle la création d'extensions supplémentaires de Person? Un ProjectPerson est juste une personne dans un rôle dans le contexte d'un projet. En termes de base de données, il est récupéré en joignant la table de la personne à la table de liens many-to-many. –

3

Voilà comment je gérer:

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

Le ProjectMember classe les réunit tous. Ce modèle vous donne la possibilité d'affecter la même personne à différents projets avec des rôles différents (par exemple, il peut s'agir d'un développeur sur ProjectA et d'un testeur sur ProjectB).

Veuillez ne pas créer de classes spécifiques à un rôle - cette leçon a déjà été apprise.

J'ai créé un sample app pour démontrer (il comprend des relations trop):

  1. Run "bin \ debug \ RolesRelationshipsSample.exe"
  2. Double-cliquez sur les icônes de la bibliothèque pour créer des entités
  3. glisser/déposez-les à affecter les relations appropriées

ne hésitez pas à jouer avec le code. J'espère que vous le trouverez utile.

+0

Merci! Je suis allé dans une direction similaire à la vôtre, s'il vous plaît vérifier ma question mise à jour. – kitsune

+0

@Vijay Patel Pourquoi ne pas créer des classes spécifiques aux rôles? Et si chaque rôle peut effectuer différentes actions dans le projet? – richard

+0

@Richard: De mon exemple, le sous-classement de "Rôle" n'est pas un problème. Je pense que je faisais allusion plus au scénario de Jamie Ide ci-dessous http://stackoverflow.com/a/689618/80369 –

0

Il semble qu'il y ait deux entités principales - Project et Project Member. Le membre du projet a les attributs "Rôle de membre" et "Nom de membre". Chacun de ces attributs peut appartenir à un domaine, c'est-à-dire un ensemble de valeurs qui peuvent être conservées dans les tables de recherche à la fois pour des raisons de commodité et pour être utilisées pour la recherche. Il est supposé que quelqu'un a besoin d'informations sur tous les membres du projet exerçant un rôle/travail particulier.

Remarque. Les tables de recherche peuvent avoir des entrées ajoutées mais n'auraient normalement pas la valeur d'une entrée modifiée. Une fois qu'une valeur est sélectionnée dans la table de recherche, elle est considérée comme un appareil permanent de la table propriétaire - dans ce cas, la table Membre du projet.

Je ne m'attendrais pas à voir une entité ou une table 'Personne' dans une entreprise autre que la commodité comme une table de recherche comme dans le cas ci-dessus. Les services RH garderont une liste des employés qui ont des informations spécifiques requises par Paie etc. mais il n'y a rien de fondamental autour des gens que l'entreprise devra savoir. NB Localisez le processus métier pour identifier une entité - ne le maquillez pas.