O/R Mappers, the Law of Demeter, and Lazy Loading
Basically, my argument was that writing
orderLine.Product.Price coupled your code with the object
model structure, so I suggested it was better to write
orderLine.ProductPrice.
This idea is not new, and is called the
Law of Demeter.
One of the reasons I disliked O/R mappers was because, in my
opinion, they forced you to write code such as
orderLine.Product.Price. However, that's not necessarily
true. A good O/R mapper should let you map the
orderLine.ProductPrice to any field in the database that can
be retrieved knowing the orderLine primary key.
For example, if the Price is not stored in the Product table
but in a table that relates Customer and Product (to have a
different price for each Customer), I can retrieve the
product price from the OrderLine by using
orderLine.Order.Customer.Id + orderLine.Product.Id to read
CustomerProduct.Price.
Note that if I write orderLine.Product.Price and then the
product price depends on the customer, there's no way to
hide that. I cannot make Product.Price return the customer's
price because I cannot obtain the 'Customer.Id' from the
Product.
I don't know which O/R mappers support creating this mapping
directly. But I do know Hibernate (a well-known Java O/R
mapper) does not.
Of course, with any O/R mapper you can manually write the
orderLine.ProductPrice to do something like the
following:
public decimal ProductPrice
{
get
{
return
CustomerProductFactory.Get(this.Order.Customer.Id,
this.Product.Id).Price;
}
}
This way, you are not solving the problem at the mapping
level but in the code.
Why do I think it's important to solve this at the mapping
level? Because it's a good way to know when you need to
eager load or to lazy load.
You are not going to add 'shortcut' properties in your
classes for every reachable field. For example, if I'm not
using the Product.Category.Name in the Order, then I won't
have a CategoryName field in it.
If I add the shortcut, it’s because the field is relevant to
the Order. So I can assume that if it's relevant to the
order, it should be eager loaded.
In sum, if you want to apply the Law of Demeter to your
domain model and your persistence layer, you should never
write object.object.property, and you should use the
shortcuts to know what to eager load.
If you know any O/R mapper that supports this at the mapping
level, please let me know.
This is what we’ve always done with
DeKlarit, but we are
not an O/R mapper. In this case, we load DataSets with all
the fields that are relevant to an Order, regardless of
where they are stored.