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.
Related
In my attempt to start learning about DDD, I began with a simple domain model that I will gradually build up over time. My domain in this instance is the usual order > order item in order to keep things simple and be able to add on later with invoices, etc. Here's what I have so far:
public class Order
{
private readonly IList<OrderItem> _orderItems;
public Guid Id { get; private set; }
public bool Completed { get; private set; }
public DateTime Created { get; private set; }
public IEnumerable<OrderItem> OrderItems
{
get { return _orderItems; }
}
public Order()
{
Id = new Guid();
Created = DateTime.UtcNow;
_orderItems = new List<OrderItem>();
}
public void AddOrderItem(int quantity, int unitCost)
{
var orderItem = new OrderItem(quantity, unitCost);
_orderItems.Add(orderItem);
}
public void CompleteOrder()
{
Completed = true;
}
}
public class OrderItem
{
public int Quantity { get; private set; }
public int UnitCost { get; private set; }
public OrderItem(int quantity, int unitCost)
{
Quantity = quantity;
UnitCost = unitCost;
}
}
I will eventually turn Quantity and UnitCost into value objects, but that isn't the important part here. As DDD preaches, we always want to protect our invariants, but I'm having a little trouble with one piece of that. From an Order, you can add a new OrderItem by calling the AddOrderItem() method and passing your quantity and unit cost.
My question now becomes what is to stop another coder from creating a new OrderItem with var orderItem = new OrderItem(1, 2)? The OrderItem constructor should probably have an Order order parameter since an OrderItem cannot exist without an Order, but again now that other coder could just call new OrderItem(new Order(), 1, 2)?
Am I missing something? Or is it just accepted that the team working on the model needs to understand the fundamentals of DDD?
Update
Thanks #theDmi, #guillaume31, #Matt as you all have provided some good points. I think it is pretty clear at this point that the repository's interface should be enough to make it clear that you can't do anything with an OrderItem created by itself. Setting the ctor for OrderItem to internal also helps to enforce this restriction as well, but it may not be needed. I plan to see what happens with or without the internal ctor. Ultimately, the reason I accepted #guillaume31's answer is the comment about the bidirectional relationships. That makes a lot of sense and I have encountered this issue in the past with EF for example, so I like the idea of keeping it unilateral as well.
"An OrderItem cannot exist without an Order" is not really an invariant. Well at least it's not an invariant in the Order aggregate. By definition, invariants only look at things that are inside one aggregate (or span across multiple ones), not things that wander around outside an aggregate.
The OrderItem constructor should probably have an Order order
parameter since an OrderItem cannot exist without an Order
I wouldn't model it that way, because
Bidirectional relationships between entities are not recommended. It can lead to synchronization problems (A points to B but B points to something else), it's better to have unidirectional relations if you can.
By doing that, your ultimate goal is to put a constraint on what's happening outside an Aggregate, which is not really the point of DDD, and, as other answers have shown, dispensable. All changes in a DDD system go through an Aggregate and a Repository.
When working with DDD, all attempts to change the state of the system run through a repository, because you need to retrieve the aggregate you want to work on first. So even if someone creates objects that make no sense outside of a certain entity, they will not be able to do anything useful with it.
Regarding this problem, DDD has even advantages over CRUD-based systems: It leads to a high discoverability. First, the repository interface tells you what you can load. Then you get an aggregate, which in turn offers operations that modify the aggregate in a meaningful way.
Using DDD, I have 4 aggregate roots where, using the appointment analogy, a clinic can have a number of patients, each patient can have a number of appointments, each appointment can have a number of prescriptions.
Now, to avoid creating a very big bloated Aggregate, Clinic in this case, I have created 4 separate aggregate roots.
public class Clinic
{
public Guid Id { get; private set; }
}
public class Patient
{
public Guid Id { get; private set; }
public Guid ClinicId { get; private set; }
public Patient(Guid clinicId)
{
ClinicId = clinicId;
}
}
public class Appointment
{
public Guid Id { get; private set; }
public Guid PatientId { get; private set; }
public Appointment(Guid patientId)
{
PatientId = patientId;
}
}
Now, the question is how should I manage the scenario where a patient is deleted, in which case all appointments referencing this patient should be deleted too.
I guess this is where a domain expert is going to come in handy. From a technical point of view it probably is going to depend on the architecture you decide on.
100% consistency
Here you could use an application service to first delete the patient and then all the appointments linked to that patient.
Eventual consistency
Using messaging you could publish PatientDeletedEvent that would be responded to by some endpoint that would delete the appointments.
However
You probably do not want to be deleting patients in the first place. Even so, your questions about the appointments for, say, setting a patient Inactive may still results in you wanting to delete future appointments.
This is where you would need a domain expert to guide you in creating the correct model and behaviour.
I'm reading Vernon's article Effective Aggregate Design. And I have a question about why modifies only one aggregate instance per transaction?
Let's take an example, consider a Warehouse invertory management story.
Inventory represents an item with quantity in a warehouse. 5 Implementing Domain Driven Design books in Shanghai warehouse for instance.
Entry represents a log about an in/out operation on an Inventory. Entering 2 Implementing Domain Driven Design books in Shanghai warehouse for instance.
An Inventory's quantity need to be changed if an Entry is submitted.
It easily comes to my mind, this is an invarient could be implemented by transactional consistency.
Solution A: Using one Aggregate and cluster Entry into Inventory.
public class Inventory implements Aggregate<Inventory> {
private InventoryIdentity id;
private Sku sku;
private int quantity;
private List<Entry> entries;
public void add(Entry entry) {
this.quantity += entry.getQuantity();
this.entries.add(entry);
}
}
public class Entry implements LocalEntity<Entry> {
private int quantity;
// some other attributes such as whenSubmitted
}
public class TransactionalInventoryAdminService impelments InventoryAdminService, ApplicationService {
#Override
#Transactional
public void handle(InventoryIdentity inventoryId, int entryQuantity, ...other entry attributes)
Inventory inventory = inventoryRepository.findBy(inventoryId);
Entry entry = inventory.newEntry(entryQuantity, ..);
inventory.add(entry);
inventoryRepository.store(inventory);
}
}
Solution B: Using seperate Aggregate for Inventory and Entry.
public class Inventory implements Aggregate<Inventory> {
private InventoryIdentity id;
private Sku sku;
private int quantity;
public void add(int quantity) {
this.quantity += quantity;
}
}
public class Entry implements LocalEntity<Entry> {
private Inventory inventory;
private int quantity;
private boolean handled = false;
// some other attributes such as whenSubmitted
public void handle() {
if (handled) {
throw .....
} else {
this.inverntory.add(quantity);
this.handled = true;
}
}
}
public class TransactionalInventoryAdminService impelments InventoryAdminService, ApplicationService {
#Override
#Transactional
public void handle(InventoryIdentity inventoryId, int entryQuantity, ...other entry attributes)
Inventory inventory = inventoryRepository.findBy(inventoryId);
Entry entry = inventory.newEntry(entryQuantity, ..);
entry.handle();
inventoryRepository.store(inventory);
entryRepository.store(entry);
}
}
Both A and B are feasible, but solution B is kind of inelegant for leaving inadvertent oppertunity to invoke Inventory.add(quantity) without Entry involved. Is this what the rule (Modifies only one aggregate instance per transaction) tries to point out for me? I'm confused mostly why we should modify only one aggregate in a transaction, what goes wrong if we don't.
Update1 start
Is it intend to alleviate concurrency problems (with another rule of "make smaller aggregates")? For example, Entry is an Aggregate with relatively low contention and Inventory is one with relatively high contetion (assuming that multiple user could manipulate one Inventory), it causes unnecessary concurrency failure if I modify them both in a transaction .
Update1 end
Some further problems need to be addressed if I adopt solution A:
1.What if there are many Entry s for an Inventory and I need a paged query UI? How to implement a paged query using Collections? One way is to load all Entry s and picks what the page need, the other way is InventoryRepository.findEntriesBy(invoiceId, paging), but this seems to break the rule of get an local entity only by get it's aggreate then navigate the object graph.
2.What if there are too many Entry s for an Inventory and I have to load all of them when add an new Entry?
I know these questions stem from lacking full understanding. So any idea is welcome, thanks in advance.
Rule of thumb is to keep your aggregates small, since you want to avoid transactional failures due to concurrency. And why would we make the memory footprint big if it shouldn't be?
So, solution A is not optimal. Big aggregates often introduce problems that easily can be avoided.
It's true that another rule of thumb is to only change one aggregate in one transaction. If you make Entry it's own aggregate, you can make the inventory's quantity eventual consistent, meaning the Entry aggregate could raise an event to which the inventory is subscribed. This way you're only changing one aggregate per transaction.
public class Entry {
public Entry(InventoryId inventoryId, int quantity) {
DomainEvents.Raise(new EntryAdded(inventoryId, quantity))
}
}
If you don't feel comfortable with eventual consistency, you can still keep the aggregates separate, but modify them both in one transaction for now - until you're feeling the pain, using an encapsulating domain service. Another option is to keep the domain events in process, so that they're also committed in a single transaction.
public class InventoryService {
public void AddEntryToInventory(Entry entry) {
// Modify Inventory quantity
// Add Entry
}
}
One of the reason you should avoid modifying multiple aggregates in single transaction is that each aggregate may be stored in different db storage and may require some specific transaction handling or impose all the difficulty of managing distributed transactions (two-phase commit etc).
The better approach is eventual consistency along with events and saga pattern.
See also: https://softwareengineering.stackexchange.com/questions/356106/ddd-why-is-it-a-bad-practice-to-update-multiple-aggregate-roots-per-transaction
We have an application which stores its data in two different databases. At some point in the future we may only be storing our data in one database, so we want it to be as painful as possible to make this kind of change. For this reason, we wrap our DbContexts in a single MyDataContext which gets injected into our UnitOfWork and Repository classes.
class MyDataContext : IDataContext {
internal Database1Context Database1;
internal Database2Context Database2;
}
class UnitOfWork : IUnitOfWork {
MyDataContext myDataContext;
public UnitOfWork(MyDataContext myDataContext) {
this.myDataContext = myDataContext;
}
public Save() {
//todo: add transaction/commit/rollback logic
this.myDataContext.Database1.SaveChanges();
this.myDataContext.Database2.SaveChanges();
}
}
class Database1Context : DbContext {
public DbSet<Customer> Customers { get; set; }
}
class Database2Context : DbContext {
public DbSet<Customers> CustomerProfile { get; set; }
}
class CustomerRepository : ICustomerRepository {
MyDataContext myDataContext;
public CustomerRepository(MyDataContext myDataContext) {
this.myDataContext = myDataContext;
}
public GetCustomerById(int id) {
return this.myDataContext.Database1.Customers.Single(...);
}
}
My first question is, am I doing it right? I've been doing a lot of reading, but admittedly DDD is a little bit overwhelming at this point.
My second question is which layer of the application do the IUnitOfWork and IDataContext interfaces reside in? I know that the interfaces for repositories live in the Core/Domain layer/assembly of the application, but not sure about these two. Should these two even have interfaces?
My first question is, am I doing it right?
You can do that, but first reconsider why you're storing data in different places in the first place. Are distinct aggregates at play? Furthermore, if you wish to commit changes to two different databases within a transaction, you will need to use 2-phase commit which is best to avoid. If you have different aggregates, perhaps you can save them separately?
My second question is which layer of the application do the
IUnitOfWork and IDataContext interfaces reside in?
These can be placed in the application layer.
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.