'Domain Driven Design - How do I model a Company and Employee use case?
I've been digging into DDD for the first time the last few weeks. I'm developing an HR application and am really struggling with the idea of Aggregate Roots.
So the current entities would be:
- Company
- Employee
Now given the lifecycle of an Employee, it makes sense at first to include it as a child entity on Company. An Employee can be added to a Company (new hire or existing) and then that Employee could resign or be fired - at which point that Employee record would likely be updated with some kind of status, so it can be differentiated from active Employees.
In regards to domain logic, every Employee at a Company must have a unique email. So if a Company always contained a list of all its Employees, I imagined that could be modeled as:
company.AddEmployee(employee) - This method would contain logic that makes sure the email is unique. In addition, it would update the size property of the Company, based on how many Employees they have. (<100 is small, <500 is medium, etc)
Now the biggest issue I have seen people discuss is concerning large Aggregates. I think this use case would fall under that concern, as a Company could have 10k+ Employees in this HR application. If I'm adding a single Employee at a time, it seems really wasteful to gather all 10k+, even if it's just their emails.
Am I doing the right thing here by making the Company the Aggregate Root, or is there a better way?
Solution 1:[1]
I assume the app you are developing handles many organisations, otherwise there would not be a need for a separate "Company" aggregate.
An aggregate should preserve its transaction boundary. There is always a tradeoff when you establish this boundary. E.g. you can make the whole system an aggregate. But then you will only be able to process all the requests strictly sequentially with a lot of locking and waiting.
Or you can make a Company an aggregate to handle Employees. Then all the Employees of a given company will be processed sequentially. That may not work well at the scale of big company.
Or you can make a Company an aggregate to handle organisation-wide stuff. And make Employee an aggregate to handle personal stuff. In this case you will need some job to check after the fact if the e-mail is uniquie, or if some other properties are Ok (making a request to some external security system, for example), and updating this Employee to "verified" or "active" status.
The last approach will work well in a highly concurrent setting.
So, as a system designer you should pick one of the approaches and accept the trade-offs.
Solution 2:[2]
You can keep Company as the aggregate root, but I'd use domain events to guard against duplicate emails.
My approach would be along the following lines:
class Company
{
List<Employee> Employees = new List<Employee>();
public int EmployeeCount { get; private set; }
public void AddEmployee(Employee employee)
{
Employees.Add(employee);
EmployeeCount++;
AddDomainEvent(new EmployeeCreated(employee));
}
}
Retrieve Company without existing employees. Just retrieve the EmployeeCount property.
Call the AddEmployee method:
- Add the employee to collection
- Increment EmployeeCount
- Add a domain event for the new employee.
Before committing the Unit of Work, process all domain events stored on your entities in the unit of work. In this case that will be the one EmployeeCreated event.
- That event will include the proposed email of the new employee.
- Your domain event handler can make a dB query that returns a scalar True/False value if the proposed email address has been used.
- If it has been used, throw exception.
If no errors thrown, then commit the unit of work.
To prevent concurrency issues, add a concurrency token on Company, so that we don't have two simultaneous calls to AddEmployee resulting in two new employees in the dB but only a single increment of the Employee count.
Solution 3:[3]
"Company" aggregate root makes sense if its only responsibility is to handle "Human Resources" related concerns.
Regarding the "Email Uniqueness". This is an application layer concern. If you run the business on pen and paper, the business side wouldn't care if two employees have the same email.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | iTollu |
| Solution 2 | Neil W |
| Solution 3 | Yorro |
