AvocadoSoftware.com

Software For Hardcore Developers
Welcome to AvocadoSoftware.com Sign in | Join | Help
in Search

Derick Baileys old blog archives - go to derickbailey.com for new contents

Order.CustomerId vs. Order.Customer.CustomerId

Ayende has A Post that makes a guess at the cause of an issue in the upcoming Microsoft Entity Framework. The primary content of the post is interesting and worth reading if you are keeping up with the EF, however, their are some subtleties present in the post that made me question how I currently create Entities in my object models.

The post directly guesses about how the EF has a hard time dealing with a line of code that is read from the object model - "Order.Customer.CustomerId" - the basic idea is that the EF does not have lazy loading, which forces the Order to always have it's Customer object loaded in memory. Conversely, this forces the Customer to always have all it's Orders loaded so you can examine an Order from the collection, such as: Customer.Orders[0].OrderId.  There is also some discussion on why the EF should be able to utilize an existing knowledge of the CustomerId from within the Order object. After all, an Order without a CustomerId is essentially an orphan record. This is where my thought process began to differ from Ayende's.

Having viewed an alternative perspective on the object model design, it made me begin to examine my own modelling assumptions. My first assumption was that an Order object would directly have a CustomerId property: Order.CustomerId. Looking at this issue from the perspective and assumptions that this post is making about the object model, though, I believe the Order object would not contain a CustomerId property but would instead have a Customer object directly linked from the Order object, allowing the Order.Customer.CustomerId code to function.

Constraining Order with a CustomerId

In the Domain Driven Design concept of Aggregates, we know that an aggregate will always be responsible for handing out a reference to the child object, protecting the model. We also know that an Aggregate is responsible for ensuring the integrity of the object tree - no broken business rules or constraints, no object in invalid states, etc. Therefore, when we have a constraint on a relationship between two objects, the Aggregate is going to be the enforcer of that constraint.

For example, assume that a business constraint exists on the relationship between a Customer and an Order.

An Order must have a valid CustomerId, to associate the Order with a Customer.

Given this constraint, it would be easy to implement an AddOrder() method that automatically assigns the CustomerId to the Order.

public class Customer
{
  public void AddOrder(Order order)
  {
    order.CustomerId = this.CustomerId;
    _orders.Add(order);
  }
}

This code will satisfy the given constraint - it will ensure that an Order always has a CustomerId when you add the Order to the Customer. However, this code also makes an indirect statement that you have a CustomerId property on the Order object. If we allow this statement to be true, then we have created a duplication of code in our object model. We now have a CustomerId property on the Customer object and a CustomerId property on the Order object. This is a clear violation of the Don't Repeat Yourself ("DRY") Principal and encroaches on the principal of Separation of Concerns, as well (the Order is now concerned with the specific data type and maintenance of a CustomerId).

Don't Repeat Yourself

The DRY Principle is quickly becoming more and more important in my development efforts. I have seen too many projects become bloated, convoluted, and difficult to maintain because a seemingly simple change in project specifications. If a change is required for a single column in a single table - how much work will it take to make this in the object model? For every line of code that is concerned with this data's type and content, you add time and effort for modifying the system and open more and more potential for missing a line of code that needed to be modified to account for the change. What happens if you have 2 or 3 places to change? How about 20 or 30 places to change? I would much rather have a single place of change.

Correcting Code by Restating the Constraint

Given the DRY Principal, how do we modify our code base to allow a single source of CustomerId data? First, we need to start by modifying the constraint - don't specify an implementation detail such as CustomerId in the constraint. Instead, only make the statement that an Order must belong to a Customer.

An Order must belong to a Customer.

This change in how the constraint is stated does a few things for us. First, it significantly shortens the statement while clearly keeping the intent of the statement in place. Secondly, it decouples the business need from the implementation by not speaking of a field or property. And lastly, it allows for the object model to be written differently, there-by allowing the DRY Principal to be adheared to.

We can now change the code to say Order.Customer.CustomerId, leaving a single point in code to represent the CustomerId and still fulfulling the business constraint.

public class Customer
{
  public void AddOrder(Order order)
  {
    order.Customer = this;
    _orders.Add(order);
  }
}

We can further ensure the enforcement of the constraint by modifying the constructor of the Order class and require a Customer to be passed to the Order.

public class Order
{
  public Order(Customer customer)
  {
    customer.AddOrder(this);
  }
}

The constraint, the DRY Principal, and Separation of Concerns have all been satisfied, now.

Conclusion

So, what is the end result? Who is the winner in Order.CustomerId vs. Order.Customer.CustomerId? For my money, the DDD methodology is the clear winner by simple benefit gained from the DRY Principal and Separation of Concerns. It certainly doesn't begin to answer the original questions in Ayende's post, but then I wasn't trying to answer those questions. I was trying to find the underlying assumptions that the post was making. Having done that and having decided that I like the Order.Customer.CustomerId approach, a whole new level of questions is opening up in my mind including the issue that Ayende's post brings up.

Oh, the joy of learning... answer one question only to find it reveals 10 more questions. :)

Published Thursday, June 07, 2007 3:24 PM by dredge
Filed Under: , ,
New Comments to this post are disabled

This Blog

Post Calendar

<June 2007>
SuMoTuWeThFrSa
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

Advertisement

News

this is my old blog archives - go to http://derickbailey.com for updates

Syndication

Advertisement

Powered by Community Server, by Telligent Systems