Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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.

share|improve this question

migrated from stackoverflow.com Apr 20 '12 at 15:54

This question came from our site for professional and enthusiast programmers.

    
I've edited/updated the post so the question is clearer and uses a better scenario (hopefully). – SonOfPirate Apr 17 '12 at 13:15

I would be inclined to implement a service for time tracking:

public interface ITimeSheetTrackingService
{
   void TrackClockIn(Employee employee, Timesheet timesheet);

   void TrackClockOut(Employee employee, Timesheet timesheet);

}

I don't think that it's time sheet's responsibility to clock in or clock out, neither it's employee's responsibility.

share|improve this answer

Aggregate roots should not contain each other (though they may contain IDs to others).

First, is TimeSheet really an aggregate root? The fact that it has identity makes it an entity, not necessarily an AR. Check out one of the rules:

Root Entities have global identity.  Entities inside the boundary have local 
identity, unique only within the Aggregate.

You define the TimeSheet's identity as employee ID & time period, which suggests that TimeSheet is a part of Employee with time period being the local identity. Since this is a Time Clock application, is the main purpose of Employee to be a container for TimeSheets?

Assuming you do have to ARs, ClockIn and ClockOut seem more like TimeSheet operations than Employee ones. Your service layer method could look something like this:

public void ClockIn(String employeeId)
{
    var timeSheet = TimeSheetRepository.FindByEmployeeAndTime(employeeId, DateTime.Now);    
    timeSheet.ClockIn();    
    TimeSheetRepository.Save();
}

If you really need to track clocked-in state in both Employee and TimeSheet, then have a look at Domain Events (I don't believe they are in Evans' book, but there are numerous articles online). It would look something like: Employee.ClockIn() raises EmployeeClockedIn event, which an event handler picks up and in turn calls TimeSheet.LogEmployeeClockIn().

share|improve this answer
    
I see your points. However, there are certain rules that constrain if and when an Employee can be clocked in. These rules are mostly driven by the current state of the Employee such as whether they are terminated, already clocked-in, scheduled to work that day or even the current shift, etc. Should the TimeSheet possess this knowledge? – SonOfPirate May 10 '12 at 16:09

Evans said

Cluster the entities and value objects into aggregates and define boundaries around each. Choose one entity to be the root of each aggregate, and allow external objects to hold references to the root only (references to internal members passed out for use within a singled operation only). Define properties and invariants for the aggregate as a whole and give enforcement responsibility to the root or some designated framework mechanism.

Having said that, though; what you've described so far suggests using DDD would be an over-complication. You don't have any domain logic; you just turn a bit on or off. Using DDD this this is making things way more complicated than they are. Anything that needs to know if someone is clocked in or clocked out is simply a report--domain logic is not required and all the extra DDD abstractions to manage complexity that isn't there is just adding accidental complexity.

share|improve this answer
    
Not sure if you read the whole post because there most certainly is domain logic behind clocking in an employee (albeit just an example of the concepts which are really the points of the post). As I stated, I am not simply flipping a bit but recording a transaction. Furthermore, I am also not talking about queries but focused on the write/command side of the domain. – SonOfPirate May 10 '12 at 16:03
    
Recording changes in state isn't really domain logic. – Peter Ritchie May 10 '12 at 16:12
2  
That's all domain logic is. A domain models behavior. Behavior is all about changing the state of the domain based on a set of predefined rules and outcomes. Don't confuse domain logic with persistance. – SonOfPirate May 10 '12 at 17:12
2  
Clocking in an employee changes the state of the domain. This behavior is constrained by various rules and is performed by adding an entry into the timesheet associated with that employee. That is domain logic. – SonOfPirate May 10 '12 at 17:13
2  
"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." What else would you expect to happen or need to happen to consider it domain logic? – SonOfPirate May 10 '12 at 19:56

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.

An aggregate root should never contain another aggregate root.

In your description, you have an Employee entity, and likewise a Timesheet entity. These two entities are distinct, but could include references to each other (ex: this is Bob's timesheet).

That much is basic modeling.

The aggregate root question is slightly different. If these two entities are transactionally distinct from each other, then they can be correctly modeled as two distinct aggregates. By transactionally distinct, I mean that there is no business invariant that requires knowing the current state of Employee and the current state of TimeSheet simultaneously.

Based on what you describe, Timesheet.clockIn and Timesheet.clockOut never need to check any data in Employee to determine if the command is permitted. So for this much of the problem, two different AR seems reasonable.

Another way of considering the AR boundaries would be to ask what kinds of edits are permitted to happen simultaneously. Is the manager allowed to clock the employee out while at the same time HR is fiddling with the employee's profile?

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)

Identity only means it is an entity -- it's the business invariant that drives whether it should be considered a separate aggregate root.

However, there are certain rules that constrain if and when an Employee can be clocked in. These rules are mostly driven by the current state of the Employee such as whether they are terminated, already clocked-in, scheduled to work that day or even the current shift, etc. Should the TimeSheet possess this knowledge?

Maybe -- the aggregate should. That doesn't necessarily mean that the Timesheet should.

Which is to say, if the rules for modifying a Timesheet depend on the current state of the Employee entity, then Employee and Timesheet definitely need to be part of the same aggregate, and the aggregate as a whole is responsible for ensuring that the rules are followed.

An aggregate has one root entity; identifying it is part of the puzzle. If an Employee has more than one Timesheet, and they are both part of the same aggregate, then Timesheet is definitely not the root. Which means that the application cannot directly modify or dispatch commands to the timesheet - they need to be dispatched to the root object (presumably the Employee), which can delegate some of the responsibility.

Another check would be to consider how Timesheets get created. If they are created implicitly when an employee clocks in, then that's another hint that they are a subordinate entity in the aggregate.

As an aside, it is unlikely that your aggregates should have their own sense of time. Instead, time should be passed to them

employee.clockIn(when);
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.