Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

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
add comment

3 Answers

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
add comment

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
1  
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
1  
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
    
The fact that you're "persisting" data as transactions isn't domain logic. You haven't provided any detail for Employee.ClockIn(), if it's just state=State.ClockedIn; then it's just a property setter--which is a very anaemic domain model. –  Peter Ritchie May 10 '12 at 19:02
show 1 more comment

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
add comment

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.