How do you model roles / relationships with Domain Driven Design in mind? - domain-driven-design

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?
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.
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?
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.
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.
ProjectMembershipTypes can be created and modified. These can be "Project Leader", "Developer", "External Adviser", or something different.
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.
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"
public class ProjectMembershipType : IRoleType
{
public virtual int ProjectMembershipTypeId { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
// etc...
}

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).
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):
Run "bin\debug\RolesRelationshipsSample.exe"
Double-click the library icons to create entities
Drag/drop them to assign the appropriate relationships
Feel free to play with the code. Hope you find it useful.

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?)
Because of those FKs, we can in the database follow the graph from Person, through Project Role, to 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.
Yes, Project::addPerson( Person& ) should really be Project::addProjectRole( ProjectRole& ), unless we decide that Project::addPerson( Person& ) is a convenience method of the form:
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).
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.)
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.

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.

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:
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.
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.
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.

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.
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.

Related

Does entity really can belong to only one aggregate?

I am learning DDD and just faced a problem that I can't solve.
Assume we have following domain:
public class Hotel : AggregateRoot {
public List<Room> Rooms { get; private set; }
}
public class Room : Entity {
public string Name { get; set; }
public int Number { get; set; }
}
and now we want to model RoomReservations.
public class RoomReservationRecord : Aggregate {
public string CustomerName { get; set; }
public Room Room { get; set; } // <- this is problem
public DateTime DateFrom { get; set; }
public DateTime DateTo { get; set; }
}
as clearly visible, 2 aggregates contains (share) single entity. It makes sense from business perspective, however from DDD perspective, it looks like 2 aggregates share the same entity.
Is this approach correct, or it violates "Entity can be part of single Aggregate" rule?
Or is there better (obvious way) to model such requirement?
I will try to point out a few things but at the end of the day this is a modeling exercise and often there are compromises to be made.
Invariants - A big part (probably the primary) of why you would group objects in an object graph is to make sure that certain rules are enforced. So if it was a business rule that NO ROOM CAN EVER BE DOUBLE BOOKED, then Hotel would probably be an aggregate root with rooms and reservations as entities on it.
Something like this... but this has some drawbacks...
EG.
class Hotel
{
//members (eg. Rooms and Reservations) ...
public Hotel(string name, ICollection<Room> rooms)
{
//...
}
bool TryMakeReservation(Reservation reservation)
{
// if booked already return false
return true;
}
}
class Room
{
//members ...
public Room(int number, bool isBooked)
{
//..
}
}
class Reservation {
public string CustomerName { get; private set; }
public int RoomNumber { get; private set; }
public DateTime DateFrom { get; private set; }
public DateTime DateTo { get; private set; }
Reservation(Parameters)
{
// ...
}
}
NOTE: To use this model you would need to lock the hotel down each time you make a booking!
This might not be acceptable for a busy hotel. There are ways around this like reserving it for 5 minutes before automatically releasing it unless a reservation is confirmed. Or storing a list of events and if 2 reservations for the same room exist over the same period without a checkout event, kick off a process to notify someone to deal with the double booking.
Contexts - it might be that the list of rooms and the actual bookings are in separate domains. Think about how often bookings are done from multiple sources like AirBnb, Booking.com, hotel website, and/or at the counter in person or over the phone. It might not make sense for bookings and the persistence of available rooms to be in the same domain. What about cleaning schedules. A room needs to be cleaned before it is available but is this really handled in the Booking context?
Performance - as mentioned, sometimes the model we want just isn't possible because of the physics of getting that amount of data queried from a datastore. Users, Product Owners et.c tend not to care how clean your model is if it affects performance too much.
Repositories - following on from the above point, since an aggregate should be a consistent type (ie. data in it should never be in an incorrect state) then when you fetch your aggregate it should be consistent. If Hotel has a repository but contains a room, and Room is an aggregate root and has its own repository, and repositories are calling repositories, I would say you are failing at scaling complexity. The main point of DDD is giving a set of patterns and practices that help you deal with complexity. If by applying DDD principles you increased the complexity by a step that is never capitalized by the future smaller steps in complexity as new features are added, then DDD was probably not the right tool to use at that point in the project.
To speak to the linked article on Ids. Just not using primitive types can mitigate a lot of the problems here. Focusing on finding Value Objects can give a lot of clarity and really help express your domain. Even if you don't use DDD it is a valuable practice, which is why I wrote a series on them without ever mentioning Value Objects... I think.
I hope this helped. DDD, more than even FP to me I guess, has so many super valuable ideas in it that help create maintainable code that scales to requirements. Better than that it has a focus on soft elements outside the code like collaboration and shared a language that brings even more value but they are just guidelines that need to be applied because you want a certain gain (and are willing to pay any incurred costs). They are not rules to apply and very seldom is it just a wrong and right path.

Query data across multiple repositories in DDD

I am using multiple aggregate roots inside a DDD bounded context.
For example
public class OrderAggregate
{
public int ID {get;set;}
public string Order_Name {get;set;}
public int Created_By_UserID {get;set;}
}
public class UserAggregate
{
public int ID {get;set;}
public string Username {get;set;}
public string First_Name {get;set;}
public string Last_Name {get;set;}
}
I am using SQL relational base to persists domain objects. Each aggregate root matches one repository.
In case I would like to find an order that was created by John Doe (seach accross multiple aggregates) what would be a DDD way to go?
add First_Name and Last_Name into OrderAggregate in order to add FindByUserFirstLastName method in OrderRespository, but that could raise data consistency issue between two aggregate roots
create a raw sql query and access DB directly in order to span search accross "repositories"
use "finders" in order to join entities directly from DB
replicate data necessary for query to be completed to a new aggregate root such as
public class QueryOrderAggregate
{
public int ID { get; set; }
public string Order_Name { get; set; }
public int Created_By_UserID { get; set; }
public string First_Name { get; set; }
public string Last_Name { get; set; }
}
In case I would like to find an order that was created by John Doe (seach accross multiple aggregates) what would be a DDD way to go?
Almost the same way that it goes with accessing an aggregate...
You create a Repository that provides a (whatever the name for this view/report is in your domain). It probably uses the UserId as the key to identify the report. In the implementation of the repository, the implementation can do whatever makes sense -- a SQL join is a reasonable starting point.
The View/Report is basically a value type; it is immutable, and can provide data, but doesn't not have any methods, or any direct access to the aggregate roots. For example, the view might include the OrderId, but to actually get at the order aggregate root you would have to invoke a method on that repository.
A view that spans multiple aggregates is perfectly acceptable, because you can't actually modify anything using the view. Changes to the underlying state still go through the aggregate roots, which provide the consistency guarantees.
The view is a representation of a stale snapshot of your data. Consumers should not be expecting it to magically update -- if you want something more recent, go back to the repository for a new copy.

Entity Framework Linking tables

I’m using Entity Framework 5.0,
Scenario
"Organisation" has a list of "clients" and a list of "Periods" and a "CurrentPeriodID" At the start of each period some or all of the "Clients" are associated with that "Period", this I have done using a link table and this works OK so when I do "Organisation->Period->Clients" I get a list of "Clients" for the "Period".
Next I need to add some objects ("Activities") to the "Clients" for a "Period" so I get "Organisation->Period->Client->Activates" this won’t be the only one there will eventually be several other navigation properties that will need to be added to the "Clients" and the "Activities" and all of them have to be "Period" related, I also will have to be able to do (if possible) "Organisation->Period-Activities".
Question
What would be the best way of implementing the "Activities" for the "Organisation->Period-Client", I Don’t mind what way it is done Code First reverse Engineering etc. Also on the creation of the "Organisation" object could I load a current "Period" object using the "CurrentPeriodID" value which is stored in the "Organisation" object.
Thanks
To me this sounds like you need an additional entity that connects Period, Client and Activity, let's call it ClientActivityInPeriod. This entity - and the corresponding table - would have three foreign keys and three references (and no collections). I would make the primary key of that entity a composition of the three foreign keys because that combination must be unique, I guess. It would look like this (in Code-First style):
public class ClientActivityInPeriod
{
[Key, ForeignKey("Period"), Column(Order = 1)]
public int PeriodId { get; set; }
[Key, ForeignKey("Client"), Column(Order = 2)]
public int ClientId { get; set; }
[Key, ForeignKey("Activity"), Column(Order = 3)]
public int ActivityId { get; set; }
public Period Period { get; set; }
public Client Client { get; set; }
public Activity Activity { get; set; }
}
All three foreign keys are required (because the properties are not nullable).
Period, Client and Activity can have collections refering to this entity (but they don't need to), for example in Period:
public class Period
{
[Key]
public int PeriodId { get; set; }
public ICollection<ClientActivityInPeriod> ClientActivities { get; set; }
}
You can't have navigation properties like a collection of Clients in Period that would contain all clients that have any activities in the given period because it would require to have a foreign key from Client to Period or a many-to-many link table between Client and Period. Foreign key or link table would only be populated if the client has activities in that Period. Neither EF nor database is going to help you with such a business logic. You had to program this and ensure that the relationship is updated correctly if activities are added or removed from the period - which is error prone and a risk for your data consistency.
Instead you would fetch the clients that have activities in a given period 1 by a query, not by a navigation property, for example with:
var clientsWithActivitiesInPeriod1 = context.Periods
.Where(p => p.PeriodId == 1)
.SelectMany(p => p.ClientActivities.Select(ca => ca.Client))
.Distinct()
.ToList();

DDD: Modeling simple domain with two aggregate roots

Let's say I want to create action web site where members would be able to bid for items. To model this domain I have three classes: Member, Item and Bid.
My brainstorming would go something like this:
Item can contain multiple bids
Bid is associated with one Item and one Member
Member can contain multiple bids
Member and Item can exist without bid instance
Bid instance can't exist without both Member and Item
Considering all this it is obvious that since Member and Item objects are independent we can consider them aggregate roots. Bid will be part of one of these aggregate. That is clear but what is confusing to me right now is which aggregate root should I choose? Item or Member?
This is example from Pro ASP.NET MVC 3 Framework book by Apress, and the way they did is like following:
Which gives following code:
public class Member
{
public string LoginName { get; set; } // The unique key
public int ReputationPoints { get; set; }
}
public class Item
{
public int ItemID { get; private set; } // The unique key
public string Title { get; set; }
public string Description { get; set; }
public DateTime AuctionEndDate { get; set; }
public IList<Bid> Bids { get; set; }
}
public class Bid
{
public Member Member { get; set; }
public DateTime DatePlaced { get; set; }
public decimal BidAmount { get; set; }
}
Member and Item are aggregate roots here and Bid is contained within Item.
Now let's say that I have application use case: "Get all bids posted by specific member". Does that mean that I would have to first get all Items (eg. from data base via repository interface) and then enumerate all bids for each Item trying to find matching Member? Isn't that a bit inefficient? So a better way would be then to aggregate Bid objects inside of Member. But in that case consider the new use case: "Get all bids for specific item". Now again we need to go other way around to get all bids...
So taking into account that I need to implement both of these use cases in my application, what is the right and efficient way to model this domain then?
Your domain should really reflect only Command (CQRS) requirements (update/change data). I presume that you need Queries (read data, no update/change of data): "Get all bids for specific item" and "Get all bids posted by specific member". So, this "querying" has nothing to do with the domain, as the query implementation is independent on the command implementation (command is calling a domain method). This gives you a freedom to implement each query in an efficient way. My approach is to implement an efficient DB view getting you only data you want to display in UI. Then you create a new class called BidForItemDto (DTO = data transfer object) and you map data from DB view into a collection of BidForItemDto (you can do it manually via ADO.NET or use NHibernate (preferred, does everything for you)). The same for the second query, create a new class called BidPostedByMemberDto.
So, if it is queries you need, just forget about domain, realize that it's just data you want to display in UI, and query them efficiently from the DB. Only when you do some action in UI (click a button to place a bid for instance), it's executing a command "place a bid", which would at the end call domain method Item.PlaceBid(Member member, DateTime date, decimal amount). And btw, IMHO is it an Item which "has many bids", and the domain method "place bid" would surely need to access previous bids to implement the whole logic correctly. Placing bids collection into Member does not make much sense to me...
From the top of my head some examples of DB views and sql queries:
Get all bids for specific item:
create view BidForItemDto
as
select
i.ItemId,
b.BidId,
b.MemberId,
b.DatePlaced,
b.BidAmount
from Item i
join Bid b ON b.ItemId = i.ItemId
query:
SELECT *
from BidFormItemDto
where ItemId = <provide item id>
Get all bids posted by specific member:
create view BidPostedByMemberDto
as
select
m.MemberId,
b.BidId,
b.MemberId,
b.DatePlaced,
b.BidAmount
from Member m
join Bid b ON b.MemberId = i.MemberId
query:
SELECT *
from BidPostedByMemberDto
where MemberId = <provide member id>

nHibernate, I can tell it to Fetch<T>, but can I tell it to Stay<T>?

Just as the question states, I know that using NHinbernate I can tell a specific query to Fetch(Func<T,bool>), and even FetchMany(). But what if I want to do the other way around?
For instance, let us say that we have a class ..
class Employee {
public virtual string Name { get; set; }
public virtual Address Address { get; set; }
public virtual double Salary { get; set; }
}
If an Employee is looking at their own profile, I would want Address and Salary to be rendered. But what if a different employee is looking? It would seem more convenient to build one ASP.NET MVC View, but to specifically not return the data that needs to be hidden. So like ..
if( // myself // ) {
return employee = session.Query<Employee>()
.Fetch(context => context.Address)
.Take(1)
.SingleOrDefault();
}
else
return employee = session.Query<Employee>()
.Deny(context => context.Address)
.Deny(context => context.Salary)
.Take(1)
.SingleOrDefault();
Then my View might look like ..
#model Employee
<h2>#Model.Name</h2>
<h4>#Html.DisplayFor( model => model.Address )</h4>
<h4>#Model.Salary</h4>
I realize this isn't the best example in the universe, but is such a thing possible? I haven't found any methods that explicitly tell an object not to return, so far.
As always the answer to everything in ASP.NET MVC is : view models. So you fetch everything in your repository and return a domain Employee model containing all properties and then you would map this Employee model to an EmployeeViewModel. It is then this view model that will be passed to the view. And when the controller maps between the model and the view model depending on the user it might include or not some properties.

Resources