views:

1857

answers:

4

This is a practical Domain Driven Design question:

Conceptually, I think I get Aggregate roots until I go to define one.

I have an Employee entity, which has surfaced as an Aggregate root. In the Business, some employees can have work-related Violations logged against them:

Employee-----*Violations

Since not all Employees are subject to this, I would think that Violations would not be a part of the Employee Aggregate, correct?

So when I want to work with Employees and their related violations, is this two separate Repository interactions by some Service?

Lastly, when I add a Violation, is that method on the Employee Entity? Thanks for the help!

+12  A: 

After doing even MORE research, I think I have the answer to my question.

Paul Stovell had this slightly edited response to a similar question on the DDD messageboard. Substitute "Customer" for "Employee", and "Order" for "Violation" and you get the idea.

Just because Customer references Order doesn't necessarily mean Order falls within the Customer aggregate root. The customer's addresses might, but the orders can be independent (for example, you might have a service that processes all new orders no matter who the customer is. Having to go Customer->Orders makes no sense in this scenario).

From a domain point of view, you can even question the validity of those references (Customer has reference to a list of Orders). How often will you actually need all orders for a customer? In some systems it makes sense, but in others, one customer might make many orders. Chances are you want orders for a customer between a date range, or orders for a customer that aren't processed yet, or orders which have not been paid, and so on. The scenario in which you'll need all of them might be relatively uncommon. However, it's much more likely that when dealing with an Order, you will want the customer information. So in code, Order.Customer.Name is useful, but Customer.Orders[0].LineItem.SKU - probably not so useful. Of course, that totally depends on your business domain.

In other words, Updating Customer has nothing to do with updating Orders. And orders, or violations in my case, could conceivable be dealt with independently of Customers/Employees.

If Violations had detail lines, then Violation and Violation line would then be a part of the same aggregate because changing a violation line would likely affect a Violation.

EDIT** The wrinkle here in my Domain is that Violations have no behavior. They are basically records of an event that happened. Not sure yet about the implications that has.

jlembke
additionally: http://dddstepbystep.com/wikis/ddd/aggregate-root.aspx (fantastic definition)
cottsak
A: 

I was wondering what the conclusion would be?

'Violations' become a root entity. And 'violations' would be referenced by 'employee' root entity. ie violations repository <-> employee repository

But you are consfused about making violations a root entity becuase it has no behavior.

But is 'behaviour' a criteria to qualify as a root entity? I dont think so.

Sanjay
I think that yes, behavior is a characteristic of an AR. That behavior may not be known at first, maybe it will surface, so if it seems like it may be one, then treat it like one. In this case a Violation may indeed have behavior. But if an "entity" is just a bag of data, treating it like an AR would be anemic, it seems to me. In that case wouldn't it just be a characteristic, or property of some AR. Couldn't it then just be a value object?
jlembke
+2  A: 

Eric Evan states in his book Domain-Driven Design: Tackling the Complexity in the heart of Software:

An AGGREGATE is a cluster of associated objects that we treat as a unit for the purpose of data changes.

There are 2 important points here:

1- These objects should be treated as a "unit". 2- For the purpose of "data change".

I believe in your scenario, Employee and Violation are not necessarily a unit together, whereas in the example of Order and OrderItem, they are part of a single unit.

Another thing that is important when modeling the agggregate boundaries is whether you have any invariants in your aggregate. Invariants are business rules that should be valid within the "whole" aggregate. For example, as for the Order and OrderItem example, you might have an invariant that states the total cost of the order should be less than a predefined amount. In this case, anytime you want to add an OrderItem to the Order, this invariant should be enforced to make sure that your Order is valid. However, in your problem, I don't see any invariants between your entities: Employee and Violation.

So short answer:

I believe Employee and Violation each belong to 2 separate aggregates. Each of these entities are also their own aggregate roots. So you need 2 repositories: EmployeeRepository and ViolationRepository.

I also believe you should have an unidirectional association from Violation to Employee. This way, each Violation object knows who it belongs to. But if you want to get the list of all Violations for a particular Employee, then you can ask the ViolationRepository:

var list = repository.FindAllViolationsByEmployee(someEmployee);

Hope this helps, Mosh

Mosh
+1  A: 

I generally agree with Mosh on this one. However, keep in mind the notion of transactions in the business point of view. So I actually take "for the purpose of data changes" to mean "for the purpose of transaction(s)".

Repositories are views of the domain model. In a domain environment, these "views" really support or represent a business function or capability - a transaction. Case in point, the Employee may have one or more violations, and if so, are aspects of a transaction(s) in a point in time. Consider your use cases.

Scenario: "An employee commits an act that is a violation of the workplace." This is a type of business event (i.e. transaction, or part of a larger, perhaps distributed transaction) that occurred. The root affected domain object actually can be seen from more than one perspective, which is why it is confusing. But the thing to remember is behavior as it pertains to a business transaction, since you want your business processes to model the real-world as accurate as possible. In terms of relationships, just like in a relational database, your conceptual domain model should actually indicate this already (i.e. the associativity), which often can be read in either direction:

Employee <----commits a -------committed by ----> Violation

So for this use case, it would be fair that to say that it is a transaction dealing with violations, and that the root - or "primary" entity - is a Violation. That, then would be your aggregate root you would reference for that particular business activity or business process. But that is not to say that, for a different activity or process, that you cannot have an Employee aggregate root, such as the "new employee process". If you take care, there should be no negative impact of cyclic references, or being able to traverse your domain model multiple ways. I will warn, however, that governing of this should be thought about and handled by your controller piece of your business domain, or whatever equivalent you have.

Aside: Thinking in terms of patterns (i.e. MVC), the repository is a view, the domain objects are the model, and thus one should also employ some form of controller pattern. Typically, the controller declares the concrete implementation of and access to the repositories (collections of aggregate roots).

In the data access world...

Using LINQ-To-SQL as an example, the DataContext would be the controller exposing a view of Customer and Order entities. The view is a non-declarative, framework-oriented Table type (rough equivalent to Repository). Note that the view keeps a reference to its parent controller, and often goes through the controller to control how/when the view gets materialized. Thus, the controller is your provider, taking care of mapping, translation, object hydration, etc. The model is then your data POCOs. Pretty much a typical MVC pattern.

Using N/Hibernate as an example, the ISession would be the controller exposing a view of Customer and Order entities by way of the session.Enumerable(string query) or session.Get(object id) or session.CreateCriteria(typeof(Customer)).List()

In the business logic world...

Customer { /*...*/ }

Employee { /*...*/ }

Repository<T> : IRepository<T>
              , IEnumerable<T>
              //, IQueryable<T>, IQueryProvider //optional

{ /**/ }

BusinessController {
 Repository<Customer>  Customers { get{ /*...*/ }} //aggregate root
 Repository<Order> Orders { get{ /*...*/ }} // aggregate root
}

In a nutshell, let your business processes and transactions be the guide, and let your business infrastructure naturally evolve as processes/activities are implemented or refactored. Moreover, prefer composability over traditional black box design. When you get to service-oriented or cloud computing, you will be glad you did. :)

Timex