Layers, messages and domain models
In some previous posts I was trying to explain why I think is better to use a message-based approach for developing n-layer applications instead of using a domain model. In some posts I've focused on the DataSet, but its not really about the DataSet but about the way of exchanging data between layers (notice Im saying layers, not tiers). This time I'll try to explain myself better ;).
When I'm saying message what I mean is to gather the data you need for a specific task in a specific data structure. So, if I'm dealing with an Order and the order has master and detail, I want a data structure with 2 levels, even if an Order involves more than 2 classes or 2 tables. If you think a message is something different than this, please pretend you agree with me, it is not really important ;) in this context.
Lets think about an ASP.NET application that needs to display an Order that is stored in a relational database, with the following tables:
Order [Id (key), CustomerId]
OrderDetails [Id (key), OrderId, ProductId, Quantity]
Customer [Id (key), Name]
Product [Id (key), Price]
If I map this to an object model, and I want to use the resulting classes in my ASP.NET application, I'll probably write something like this (in pseudo c#):
Order.Id
Order.Customer.Id
foreach (OrderDetail detail in Order.Details)
{
detail.Product.Id
detail.Product.Price
detail.Quantity
}
Now, suppose I need to change my underlying schema. Instead of having one price for each product, I'll create Customers Categories, and I'll have a Product price for each Category. The tables will look like:
Order [Id (key), CustomerId]
OrderDetails [Id(key), OrderId, ProductId, Quantity]
Customer [Id(key), Name, CategoryId]
Category [Id(key), Name]
Product [Id (key)]
ProductCategory [ProductId(key), CategoryId(key), Price]
The usual way to hide this change in a multilayer application is to change the way I map my tables to my classes, so the signature of the classes will be the same and I won't have to change my ASP.NET code. In this case, the problem is that I cannot (or perhaps I can but I dont realize how ;). I'll need to change my code to something like:
Order.Id
Order.Customer.Id
foreach (OrderDetail detail in Order.Details)
{
detail.Product.Id
detail.GetProductPrice(Order.Customer.Category.Id)
detail.Quantity
}
This is because there is no way to obtain the price from the Product object, as it needs the Category, and the Category depends on the Invoices Customer.
If instead of using a domain model, I define a message with:
OrderId
CustomerId
OrderDetail[] lines
OrderDetail:
ProductId
ProductPrice
Quantity
Ill write:
OrderId
CustomerId
foreach (OrderDetail detail in Order.Details)
{
detail.ProductId
detail.ProductPrice
detail.Quantity
}
And this code will be valid with the first database schema and with the second one. I just need to change the SQL statement that loads the structure.
Why does this happen? Because if I use my domain model from a different layer, Im exposing it. We build applications in layers because we want to isolate one layer from the changes in the other layers, and exposing a domain model does not let me do it. Its usually known as a bad practice to expose the database schema to the UI layer of an application. Exposing the object model is better, but it's still bad idea.
On the other hand, think on how complex is for the ASP.NET developer to deal with each model. If he uses the object model, he needs to know how to traverse it to find the data he needs. Also, he does not really know how many roundtrips it takes to access the Order, and how many columns will be retrieved (i.e., all the fields from the accessed tables will be probably retrieved even if not needed).
With a data structure designed to work with the Orders, we are giving the external layer programmer a much better interface with our middle layer.
UPDATE: this post has been translated to French!