Let me begin by first apologizing for the length of the post, but I really wanted to convey as much detail up-front so I don't take your time going back and forth in comments.
I am designing an application following a DDD approach and am wondering what guidance I can follow to determine whether an Aggregate Root should contain another AR or if they should be left as separate, "free-standing" ARs.
Take the case of a simple Time Clock application that allows Employees to clock themselves in or out for the day. The UI allows them to enter their employee ID and PIN which is then validated and the current state of the employee retrieved. If the employee is currently clocked-in, the UI displays a "Clock Out" button; and, conversely, if they are not clocked-in, the button reads "Clock In". The action taken by the button corresponds to the state of the employee as well.
The application is a web client that calls a back-end server exposed via a RESTful service interface. My first pass at creating intuitive, readable URLs resulted in the following two endpoints:
http://myhost/employees/{id}/clockin
http://myhost/employees/{id}/clockout
NOTE: These are used after the employee ID and PIN have been validated and a "token" representing the "user" is passed in a header. This is because there is a "manager-mode" that allows a manager or supervisor to clock-in or out another employee. But, for the sake of this discussion, I'm trying to keep it simple.
On the server, I have an ApplicationService that provides the API. My initial idea for the ClockIn method is something like:
public void ClockIn(String id)
{
var employee = EmployeeRepository.FindById(id);
if (employee == null) throw SomeException();
employee.ClockIn();
EmployeeRepository.Save();
}
This looks pretty straight-forward until we realize that the Employee's time card information is actually maintained as a list of transactions. That means each time I call ClockIn or ClockOut, I am not directly changing the state of the Employee but, instead, I am adding a new entry into the Employee's TimeSheet. The current state of the Employee (clocked in or not) is derived from the most recent entry in the TimeSheet.
So, if I go with the code shown above, my repository has to recognize that the persistable properties of the Employee have not changed but that a new entry was added into the Employee's TimeSheet and perform an insert into the data store.
On the other hand (and here's the ultimate question of the post), TimeSheet seems like it is an Aggregate Root as well as it has identity (the employee ID and period) and I could just as easily implement the same logic as TimeSheet.ClockIn(employeeId).
I find myself debating the merits of the two approaches and, as stated in the opening paragraph, wonder what criteria I should be evaluating to determine which approach is more suited for the problem.