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.
Related
i'm stuck with this problem while designing aggregates in a DDD project.
Please consider the following scenario:
public abstract class BaseAppType{
public abstract int GetUserOwnerId();
public List<AppTypeHost> Hosts {get;set;} = new List<AppTypeHost>()
}
public class PersonalAppType:BaseAppType //this is an aggregate root
{
public int override GetUserOwnerId(){ return Hosts.Select(h=>h.UserId).Single(); }
}
public class TeamAppType:BaseAppType //this is another aggregate root
{
publi int TeamOwnerId {get;set;}
public int override GetUserOwnerId(){ //this is much harder becase i don't have the info in the object }
}
public class Team {
public List<TeamMember> TeamMembers = new List<TeamMember>();
}
public class TeamMember {
public int TeamId {get;set;}
public int UserId {get;set;}
public TeamMemberRole Role {get;set;} //this might be either Owner or Member
}
So basically i've two types of appointments that share common info, functionality and shape via a root class.
Now i've to implement GetUserOwnerId in the two derived class, which are two distinct aggregates root.
In the PersonalAppType it is kind of easy because the information of the userOwner is within one of the entity of the aggregate so i simply query the object in memory and return it.
In the TeamAppType it is more diffuclt because the information is in another aggregate root ( basically for my business rules, the owner of the TeamAppType is the Owner of the Team AggregateRoot).
Since Team is another AggregateRoot i could not load it into the TeamAppType aggregate and i pretty stuck...
I've tried:
the route of injecting a service in the TeamAppType
so that i can call it within the GetUserOwnerId but i don't like it because it feel "wrong" to inject a service within a domain constructor and it is kind of hard because when i retrieve the aggregate root from ef core, it doesn't inject the service ( because it uses the default construcor with 0 params )
I've also tried the route of doing it in a domain service, something like this:
public class AppTypeOwnerResolverService{
public int GetUserOwnerId (BaseAppType appType)
{
switch (appType.GetType())
{
case "PersonalAppType":
//retrieve owener of PersonalAppType
break
case "TeamAppType":
//retrieve owener of TeamAppType
break
}
}
}
but it feels off because it looks like the GetUserOwnerId should stay within the inherited class and this reduces the benefits of polymorfism.
Do you have any suggestion on how to approach this problem?
Thanks to everyone for the help.
Another option would be to have a Team aggregate emitting domain events, (i.e. TeamOwnerAssigned) and having a domain event handler that modifies the TeamAppType aggregate based on this event.
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.
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
I'm thinking a bit about "best practice" regarding testability and the best way to define a particular action.
In the SportsStore application (from Pro ASP.NET MVC 4), for the AdminController, we have the following two methods in the AdminController.cs file:
IProductRepository
namespace SportsStore.Domain.Abstract {
public interface IProductRepository {
IQueryable<Product> Products { get; }
void SaveProduct(Product product); //Defined in EFProductRepository
void DeleteProduct(Product product); //Defined in EFProductRepository
}
}
AdminController:
private IProductRepository repository;
public ViewResult Edit(int productId) {
Product product = repository.Products.FirstOrDefault(p => p.ProductID == productId);
...
}
[HttpPost]
public ActionResult Delete(int productId) {
Product prod = repository.Products.FirstOrDefault(p => p.ProductID == productId);
...
}
As I noticed, we are basically doing the same bit of logic, that being, finding the productID. If productId changes at all, to something else, we need to change this in two spots. This can be tested, easily, though, since the controller itself is making the Linq call.
I was thinking that I could put this into the equivalent of the EFProducts (so the database implementation of the IProducts interface), but this creates a tie to a database state of some kind. I'd like to avoid this in my unit tests as it increases testing complexity a fair amount.
Is there a better place to put this FindOrDefault logic, rather than in the controller, yet keep a good amount of testability?
Edit1: Adding the definition for the repository, which points to an interface
The responses under my question discuss the possibilities. Both the book and #maess agree in keeping the logic for this particular part in the controller. The comments under my question are worth looking at, as both #SOfanatic and #Maess provided fantastic input.
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.