DDD and Homogeneous Many-to-Many Relationship - domain-driven-design

Imagine you build a social network. There are users who can add other users as friends. How do you model this in DDD? Naturally you cannot simply have a list of friends in the User class, because any friend loop would cause infinite recursion on getting such a user from the repository.
How would you change your model if you needed to track friend requests which can be pending, canceled, accepted or declined?

hmm... Actually it's quite easy to do what you've asked, and your situation is a standard one. You don't have to store actual User object in Friendslist of your User aggregate, just put there IDs of users who are friends for the User.
This is one of the rules of aggregate implementation proposed by Vaugh Vernon: link to other aggregate and entities by their ID. So no loops, just list of IDs.
In that situation then somebody become a friend to somebody you have to change two aggregates at one time. It can be undesirable behavior because change can't occur instantly in one transaction. But for this case you have Domain Events and friend requests can be easily modeled: your aggregates can communicate with each other with FriendshipRequested, FriendshipAccepted, FriendshipCancelled or FriendshipDeclined events and change their state correspondingly.
In this case you also receive logs and notifications for free.

That would very much depend on where you consistency boundaries need to be. Which would also therefore depend on what business rules you have.
While Meta-Knight has FriendRequest in the same Aggregate I would have it as its own and use events to communicate between the Aggregates therefore making a Person and there FriendRequests eventually consistent. This would allow you to do something like.
public class DomainRouter {
public void When(FriendRequestCreated event)
{
//Send command to each Person to record request
}
public void When(FriendRequestAccepted event)
{
//Send command to Person to record request accepted and add friend.
//Send comamnd to Person who accepted to add Friend
}
public void When(FriendRequestDeclined event)
{
//Send command to update Friend request on person.
//Send command to Person who declined to record they have declined?
}
}
The information on Person would therefore just be a record of state. The FriendRequest Aggregate would be where all the process actually happens.
What is important in DDD is to think about behavour. A FriendRequest can be Requested, Withdrawn, Accepted and Declined. What can a Person do? Does a Person need to be DDDd or could you make it nice and simply CRUD + store info into graph database etc.
Maybe you want to model it that in a way where you can go person.requestFriendAcceptance(Person id) in which case the router would probably just handle the FriendRequestCreated event by notifying the friend of a FriendRequest.

A User could have a list of Friends. A Friend could consist of a UserId, FriendType, GroupId, etc.
A User could also have a list of FriendRequests. A FriendRequest could have a UserId, RequestMessage, RequestStatus etc.
User, Friend and FriendRequest could all be part of the same Aggregate. But there could be some duplication by doing so. Another option would be to have one FriendRequest for both users involved, and each user would have a list of received and sent FriendRequest IDs.
This is just to give you some ideas because in DDD, your design will depend highly about how your app will work, and which features are needed. ;-)

Related

Finding aggregate id based on dependency

i have a quite messy question since i'm not sure if this is more of global design issue.
I have service which defines some aggregates and those aggregates publish events. Let's pick one aggregate from the service and call it A.
Now, im defining another service which has some aggregate that is supposed to work with the A, lets call the second aggregate B.
When A publishes some event i want to send command to the B (ideally through saga) but im having a hard time calculating appropriate id of B (the A does not know that B exists, therefore the events it publishes does not have any hint on how to calculate the id)
I can think of few possible scenarios:
First one would be statically calculating the ID of B based on the id of A, for example in axon i could do something like some-id-${suffix} so when i receive event from A with some-id i can immediately know that it should be dispatched to some-id-B
Second one would be using the read side? (im not sure how its properly called) and query the thing and try to find B id based on the A id but that seems a little bit of overkill.
Is there anything i could read that would navigate me through possible scenarios and give me a hint how to handle them? Thanks!
From what I understand, you have a relationship from aggregate B to aggregate A. These kind of relationships are normal and they occur all the time.
Note: Since this question is very general and without a context, I may be missing something. If there is a more special case than the one described notify my about it.
This is a great read for aggregate design
Note: Check this video from Martin Fowler before reading the rest of this answer, I strongly suggest it as it explains concepts related to events and commands in great detail.
Note: Because the term entity is also very important, from here on I will not use aggregate anymore, so assume that each entity (Player, User, Game) are root of their own aggregate and is a consistency boundary, so eventual consistency in this case by domain events will be used. I will also ignore CQRS at the moment to avoid having to talk about read side and write side. We will discuss and implement a Model
Let take the example with the game. You have a Player that should represent a User in a Game. The Player entity should reference the Game and User in some way. It can be via a direct reference or by ID. In the case of distributed system it will be by ID. For our example Lets use UUIDs (for example 8d670541-aa51-470b-9e72-1d9227669d0c) for ID's so we can generate them randomly without having to define a schema, auto generate sequencial number (like in SQL databases) or a special algorithm.
Let's say that a User has UserStatistics. So when that when a Player makes scores (for example by killing other players in a shooting game) the UserStatistics entity should be created if not exists and updated. The UserStatistics should reference User by ID also, so we have a dependency from UserStatistics to User.
UserStatistics will look like this:
UserStatistics {
UUID UserID,
uint KillsCount,
uint GamesPlayedCount
}
Because the Player cannot exists without a User, the User should be created first. Because a Player is part of a Game than means that the Game should be created first. Let define some terminology in our Ubiquitous Language. A User 'joins' a Game by becoming a Player in it. Let say that Game will be created by someone else, not Users to avoid having to discuss the situation when the User creates a game and should join it also at the same time etc. Should this happen in the same transaction etc... The Game will be something like a MMO where it is created by someone and regular users can join.
When a User joins a Game, then the Player entity will be created with a userID and gameID. Creating a Player without an userID and gameID is not valid.
Let discuss the problem with Commands and Events. Commands can be Triggered by Events. Let use the Observer Pattern. One Entity will have to observe another Entity for events. In our example this means that the dependency is from UserStatistics (the observer) to User and Player (the subject/message producer). The fact that a specific Command on a UserStatistics will be executed as a reaction to an Event raised from Player and User should not affect the Player or Player in any way. By using an Event to deliberately trigger a special Command in a passive aggressive style is not a very good strategy. Commands can be triggered by a Event, but not only one specific Command can be Triggered. Many different Commands can be triggered and only the dependent Entities, Services or Systems should care about what happens. The Player and User just provide Events.
When a User joins a Game and the Player is created it will reference both entities by ID, so it will look something like this:
Player {
UUID GameID,
UUID UserID
}
Also UserJoinedGameEvent event will be raised from User entity (it cam be raised from Game but we will choose User). It looks like this:
UserJoinedGameEvent {
UUID GameID,
UUID UserID,
UUID PlayerID
}
The UserStatisticsService can subscribe for events and update statistics.
When a User joins a Game, a process of gathering statistics will start and we will update (or create if it doesn't exists) his UserStatistics with how many games the he has played. At the same time when a Player makes a kill we will have to update the statistics again.
StartGatheringUserStatisticsCommand will be triggered from UserJoinedGameEvent event.
Let's add an event PlayerMadeKillEvent that looks like this:
PlayerMadeKillEvent {
UUID UserID,
UUID PlayerID,
UUID GameID
}
UserStatisticsService will subscribe for PlayerMadeKillEvents and update UserStatistics by using PlayerMadeKillEvent.UserID to find the statistics for the specific User.
When a User quits a Game, a UserQuitsGameEvent can be raised and statistics gathering can stop.
In this example we didn't have a specific schema to generate special ID's, we can reference other aggregates that will be created first and then use their ID.

DDD - Modelling User with contact information that must be unique across the system

I need some clarification on modelling a user for Identity and access domain. The user domain model has a contact information entity (entity because it is mutable), the customer can register with a phone number, but can choose to change it when needed.
The phone number once used by a customer can never be used by any other user. So the model I believe must allow querying the phonenumber table(since it is many to one with the customer, as the old numbers are deactivated and archived).
If creating a domainservice is ok, what should be the Repository as there is no aggregate identified.
With these cases I have a customer(user) aggregate, but to allow querying all the users to see if the phone number provided by the customer is already been used by anyone else, what should be the aggregate, or can I write a DomainService that just can query the database directly to the phonenumber table to check for the uniqueness, am I violating any DDD principle doing so, what are the cleaner alternatives.
An alternative would be to create an Aggregate that makes it explicit in which scope you intend the unique constraint to hold.
As a (contrived) example, a phone number might be unique across a country but not internationally. Thus :
// An Aggregate Root
public class Country {
// Store a lookup structure (userId, phoneNumber) here
public void addUser(userId, phoneNumber) {
// check phone uniqueness here
}
public void changeUserPhone(userId, phoneNumber) {
// check phone uniqueness here
}
}
Since you're using CQRS, phone numbers being in a separate Aggregate doesn't matter because on the Query side, Read Models will reassemble the User and their phoneNumber together.
This also plays well with the "don't create Aggregate Roots" approach since you have a starting point from where to create your User (User is probably an AR) and not just create it out of thin air.
You could have your repository check if the phone number exists, if it does then throw a rule exception else save the change. The key here is to inject an instance of the repository through the application layer and run rule inside the domain layer.

DDD and getting additional information in a domain class

I think I've read 16,154 questions, blog posts, tweets, etc about DDD and best practices. Apologies for yet another question of that type. Let's say I have three tables in my database, User, Department, and UserDepartment. All very simple. I need to build a hierarchy showing what departments a user has access to. The issue is that I also need to show the parent departments of those that they have access to.
Is it best to have a GetDepartments() method on my user class? Right now I have a user service with GetDepartments(string userName), but I don't feel like that is the optimal solution. If user.GetDepartments() is preferred then how do I get access the repository to get the parent departments for those that the user has access to?
Don't think it matters, but I'm using the Entity Framework.
public class User
{
[Key]
public int UserId { get; private set; }
[Display(Name = "User Name")]
public string UserName { get; private set; }
[Display(Name = "Email")]
public string Email { get; private set; }
[Display(Name = "UserDepartments")]
public virtual ICollection<UserDepartment> UserDepartments { get; private set; }
public List<Department> GetDepartments()
{
// Should this be here? and if so, what's the preferred method for accessing the repository?
}
}
DDD is more about the behavior, which also mean it is TDA (tell, don't ask) oriented.
Normally you structure your aggregates in a way that you tell them what to do, not ask for information.
Even more, if some extra information is required by the aggregate in order to perform its behavior, it is typically not their job to figure out where to get this information from.
Now, when you are saying that your User aggregate has GetDepartments method, it raises a bell. Does the aggregate need this information in order to perform any kind of behavior? I don't think so, it is just you wanting some data to display.
So what I see here is that you are trying to structure your aggregates against your data tables, not against the behavior.
This is actually #2 error when applying DDD (#1 is not thinking about bounded contexts).
Again, aggregates represent business logic and behavior of your system. Which means that you don't have to read from aggregates. Your read side can be done much easier - just make a damn query to the DB.
But once you need to ask your system to do something - now you do it through aggregates: AppService would load one from the repository and call its behavior method.
That's why normally you don't have properties in your aggregates, just methods that represent behavior.
Also, you don't want your aggregates to be mapped to the data tables anyhow, it is not their job, but the job of repositories. Actually, you don't want your domain to have dependencies on anything, especially infrastructure.
So if you want to go for DDD direction then consider the following:
Structure your aggregates to encapsulate behaviors, not represent data tables
Don't make your domain dependant on infrastructure, etc.
Make repositories to be responsible to load/save aggregates. Aggregates themselves should know nothing about persistence, data structure, etc.
You don't have to read data through aggregates.
Think of #4 as your system has two sides: the "read" side when you just read the data and show them in the UI, and the "command" side when you perform actions.
The first one (read) is very simple: stupid queries to read the data in a way you want it. It doesn't affect anything because it is just reading, no side effects here.
The second one is when you make changes and that is going through your domain.
Again, remember the first rule of DDD: if you don't have business logic and behavior to model then don't do DDD.

DDD: Large Aggregate Root - Person

I am building a system to manage person information. I have an ever growing aggregate root called Person. It now has hundreds of related objects, name, addresses, skills, absences, etc. My concern is that the Person AR is both breaking SRP and will create performance problems as more and more things (esp collections) get added to it.
I cannot see how with DDD to break this down into smaller objects. Taking the example of Absences. The Person has a collection of absence records (startdate, enddate, reason). These are currently managed through the Person (BookAbsence, ChangeAbsence, CancelAbsence). When adding absences I need to validate against all other absences, so I need an object which has access to the other absences in order to do this validation.
Am I missing something here? Is there another AR I have not identified? In the past I would have done this via an "AbsenceManager" service, but would like to do it using DDD.
I am fairly new to DDD, so maybe I am missing something.
Many Thanks....
The Absence chould be modeled as an aggregate. An AbsenceFactory is reposible for validating against other Absence s when you want to add a new Absence.
Code example:
public class AbsenceFactory {
private AbsenceRepository absenceRepository;
public Absence newAbsenceOf(Person person) {
List<Absence> current =
absenceRepository.findAll(person.getIdentifier());
//validate and return
}
}
You can find this pattern in the blue book (section 6.2 Factory if I'm not mistaken)
In other "modify" cases, you could introduce a Specification
public class SomeAbsenceSpecification {
private AbsenceRepository absenceRepository;
public SomeAbsenceSpecification(AbsenceRepository absenceRepository) {
this.absenceRepository=absenceRepository;
}
public boolean isSatisfiedBy(Absence absence) {
List<Absence> current =
absenceRepository.findAll(absence.getPersonIdentifier());
//validate and return
}
}
You can find this pattern in the blue book(section 9.2.3 Specification)
This is indeed what makes aggregate design so tricky. Ownership does not necessarily mean aggregation. One needs to understand the domain to be able to give a proper answer so we'll go with the good ol' Order example. A Customer would not have a collection of Order objects. The simplest rule is to think about deleting an AR. Those objects that could make sense in the absence of the AR probably do not belong on the AR. A Customer may very well have a collection of ActiveOrder objects, though. Of course there would be an invariant stating that a customer cannot be deleted if it has active orders.
Another thing to look out for is a bloated bounded context. It is conceivable that you could have one or more bounded contexts that have not been identified leading to a situation where you have an AR doing too much.
So in your case you may very well still be interested in the Absence should the Customer be deleted. In the case of an OrderLine it has no meaning without its Order. So no lifecycle of its own.
Hope that helps ever so slightly.
I am building a system to manage person information.
Are you sure that a simple CRUD application that edit/query RDBMS's tables via SQL, wouldn't be a cheaper approach?
If you can express the most of the business rules in term of data relations and table operations, you shouln't use DDD at all.
I have an ever growing aggregate root called Person.
If you actually have complex business rules, an ever growing aggregate is often a syntom of undefined (or wrongly defined) context boundaries.

should a domain model keep itself consistent using events?

I am working on an application where we try to use a Domain Model. The idea is to keep the business logic inside the objects in the Domain Model. Now a lot is done by objects subscribing to related objects to react to changes in them. This is done through PropertyChanged and CollectionChanged. This work OK except in the following:
Complex actions : Where a lot of changes should be handled as a group (and not individual property/collection changes). Should I / how can I 'build' transactions?
Persistency : I use NHibernate for persistency and this also uses the public property setters of classes. When NHibernate hits the property a lot of bussiness logic is done (which seems unnecessary). Should I use custom setters for NHibernate?
Overal it seems that pushing all logic in the domain model makes the domain model rather complex. Any ideas???
Here's a 'sample' problem (sorry for the crappy tooling i use):
You can see the Project my container and objects below it are reacting to each other by subscribing. Now changes to Network are done via NetworkEditor but this editor has no knowledge of NetworkData. This data might even be defined in a another assembly sometimes. The flow goes from user->NetworkEditor->Network->NetworkData and the all other object interested. This does not seem to scale.
I fear that combination of DDD and PropertyChanged/CollactionChanged events might now be the best idea. The problem is, that if you base your logic around these events it is extremely hard to manage the complexity as one PropertyChanged leads to another and another and soon enough you loose control.
Another reason why ProportyChanged events and DDD doesn't exactly fit is that in DDD every business operation should be as explicit as possible. Keep in mind that DDD is supposed to bring technical stuff into the world of business, not the other way around. And basing on the PropertyChanged/CollectionChanged doesn't seem very explicit.
In DDD the main goal is to keep consistency inside aggregate, in other words, you need to model the aggregate in such way, that whatever operation you invoke the aggregate is valid and consistent (if the operation succeeds of course).
If you build your model right that there's no need to worry about 'building' transaction - an operation on aggregate should be a transaction itself.
I don't know how your model looks like, but you might consider moving the responsibilities one level 'up' in the aggregate tree, quite possibly adding additional logical entities in the process, instead of relying on the PropertyChanged events.
Example:
Lets assume you have a collection of payments with statuses and whenever a payment changes, you want to recalculate the total balance of customer orders. Instead of subscribing changes to the payments collection and calling a method on customer when collection changes, you might do something like this:
public class CustomerOrder
{
public List<Payment> Payments { get; }
public Balance BalanceForOrder { get; }
public void SetPaymentAsReceived(Guid paymentId)
{
Payments.First(p => p.PaymentId == paymentId).Status = PaymentStatus.Received;
RecalculateBalance();
}
}
You might have noticed, that we recalculate the balance of single order and not the balance of entire customer - and in most cases that's ok as customer belongs to another aggregate and its balance can be simply queried when needed. That is exactly the part that shows this 'consistency only within aggregate' thingy - we don't care about any other aggregate at this point, we only deal with single order. If that's not ok for requirements, then the domain is modeled incorrectly.
My point is, that in DDD there's no single good model for every scenario - you have to understand how the business works to be successful.
If you take a look at the example above, you'll see that there is no need to 'build' the transaction - entire transaction is located in SetPaymentAsReceived method. In most cases, one user action should lead to one particular method on an entity withing aggregate - this method explicitly relates to business operation (of course this method may call other methods).
As for events in DDD, there is a concept of Domain Events, however these are not directly related with PropertyChanged/CollectionChanged technical events. Domain Events indicate the business operations (transactions) that have been completed by an aggregate.
Overal it seems that pushing all logic in the domain model makes the
domain model rather complex
Of course it does as it is supposed to be used for scenarios with complex business logic. However if the domain is modeled correctly then it is easy to manage and control this complexity and that's one of the advantages of DDD.
Added after providing example:
Ok, and what about creating an aggregate root called Project - when you build aggregate root from Repository, you can fill it with NetworkData and the operation might look like this:
public class Project
{
protected List<Network> networks;
protected List<NetworkData> networkDatas;
public void Mutate(string someKindOfNetworkId, object someParam)
{
var network = networks.First(n => n.Id == someKindOfNetworkId);
var someResult = network.DoSomething(someParam);
networkDatas.Where(d => d.NetworkId == someKindOfNetworkId)
.ToList()
.ForEach(d => d.DoSomething(someResult, someParam));
}
}
NetworkEditor would not operate on Network directly, rather through Project using NetworkId.

Resources