Layered architecture is the most common way of creating software projects because they bring in SOC (Separation of Concern), decoupling, easy understanding of code, etc. In software industry people are pretty clear about the common layers and their responsibility (UI for look and feel, middle layer for business logic, and data access layer for data).
But the biggest confusion or I will say where developers have not come to common standards is the way of passing data between these layers. This article will try to flesh out the various ways of passing data between layers.
I repeat again layers and not tiers. In case you are confused about layers and tiers, the below diagram explains it visually. Layers are more of a logical separation for your code while tiers mean that those layers are deployed in different physical machines. So for now we will keep the scope of this article limited and restricted, or else we need to get into web service, remoting, etc., which will lead to more confusion.
We will be analyzing the passing of data between layers from two perspectives: how the data will come towards the data access layer, and how the data will pass to UI. As you read the article you would understand better why I want two perspectives.
This is the first way (probably the most common way) of passing data from one layer to another, the non-uniform way. So let’s start from UI to DAL.
Data from the UI to middle tier is passed using getter and setter, below is a sample code for the same.
Customer objCust = new Customer(); objCust.CustomerCode = "c001"; objCust.CustomerName = "Shivprasad";
Further down from the middle tier to the data access layer, data is passed by using comma separation, arrays or some other non-uniform way which makes a developer personally comfortable. In the below code snippet look at the Add method and how it’s making a call to the data access layer with input parameters.
Add
public class Customer { private string _CustomerName = ""; private string _CustomerCode = ""; public string CustomerCode { get { return _CustomerCode; } set { _CustomerCode = value; } } public string CustomerName { get { return _CustomerName; } set { _CustomerName = value; } } public void Add() { CustomerDal obj = new CustomerDal(); obj.Add(_CustomerName,_CustomerCode); } }
Below is the data access layer code with two inputs: CustomerName and CustomerCode.
CustomerName
CustomerCode
public class CustomerDal { public bool Add(string CustomerName,string CustomerCode) { // Insert data in to DB } }
So summarizing for non-uniform way data is passed in the following ways:
Now let’s analyze how data will be passed from DAL to UI in the non-uniform method. So let’s start from the UI first.
From UI, data will be pulled in using the middle tier object as shown below.
Customer obj = new Customer(); List<Customer> oCustomers = obj.getCustomers();
From middle tier the data will be pulled from the data access layer using dataset/datatable/XML etc. The most important point to note is the middle tier loops through the dataset and converts it into a strong type object, in this case it’s Customer. You can see the getCustomers function (code is shown below), how it loops through the dataset and fills in a strongly typed list of customers. This is necessary because the UI is referencing strongly typed classes (watch the UI code snippet just above).
Customer
getCustomers
public class Customer { private string _CustomerName = ""; private string _CustomerCode = ""; public string CustomerCode { get { return _CustomerCode; } set { _CustomerCode = value; } } public string CustomerName { get { return _CustomerName; } set { _CustomerName = value; } } public List<Customer> getCustomers() { CustomerDal obj = new CustomerDal(); DataSet ds = obj.getCustomers(); List<Customer> oCustomers = new List<Customer>(); foreach (DataRow orow in ds.Tables[0].Rows) { // Fill the list } return oCustomers; }
This conversion also ensures that we maintain the golden rule of tiered layer: “UI should avoid directly referencing data access components like ADO.NET, OLEDB etc. With this if we change our data access methodology later, it’s not replicated on the UI.” You can watch the project of my console UI, it has no references to System.Data, very clean, isn’t it?
System.Data
The final thing is the CustomerDal class which just returns the dataset using ADO.NET to pass the same to the middle layer.
CustomerDal
public class CustomerDal { public DataSet getCustomers() { // fetch customer records return new DataSet(); } }
Summarizing in a non-uniform way the data is moved to UI in the following ways:
Some of the benefits of non-uniform way:
But it has some serious disadvantages.
That’s where we can use the uniform way of passing data, i.e., “Entities”.
In case you are wondering why we cannot use the middle tier classes across layers, do not forget, the middle tier is already using DAL. If DAL tries to use the middle tier it will lead to circular dependency.
The other way of passing data is using the uniform approach using entity classes. Entity classes are nothing but simple classes with SET and GET. It does not have logic. For instance you can see CustomerEntity class with customer name and customer code. Now this class is in a separate project called as Entity so that it can be referenced by all layers.
CustomerEntity
public class CustomerEntity { protected string _CustomerName = ""; protected string _CustomerCode = ""; public string CustomerCode { get { return _CustomerCode; } set { _CustomerCode = value; } } public string CustomerName { get { return _CustomerName; } set { _CustomerName = value; } } }
So let’s start with CustomerDal first. The DAL returns back a CustomerEntity collection and also takes in a CustomerEntity object to add to the database. Please note the data access layer is responsible to convert the output to the CustomerEntity type object.
public class CustomerDal { public List<CustomerEntity> getCustomers() { // fetch customer records return new List<CustomerEntity>(); } public bool Add(CustomerEntity obj) { // Insert in to DB return true; } }
The middle tier inherits from the CustomerEntity class and adds behavior to the entity class. This class will have all the business rules for the entity. Also note that data is passed to the data access layer in an Entity class format (see the Add method) and also the data is returned back in entity format (see the getCustomers method).
public class Customer : CustomerEntity { public List<CustomerEntity> getCustomers() { CustomerDal obj = new CustomerDal(); return obj.getCustomers(); } public void Add() { CustomerDal obj = new CustomerDal(); obj.Add(this); } }
Note: Watch the type cast made in “Add” method. I personally love the elegancy and simplicity the way data is passed to the DAL.
Finally the UI is also no different even he talks with the same protocol of entity class structure.
Summarizing, in the uniform method we pass data from one layer to other layer using “Entity” class structure. So either the data is moving in or out it uses entity structure.
First let’s talk about good things.
The only disadvantage which I see is the double loop problem. When we talk about entity classes they normally represent real world object structure.
Now the database is technically optimized, so the structure of the database world is different from that of the real world. For instance it’s very much possible that you have customer name and address are stored in the same table (denormalized tables) so that you can fetch faster. But as soon as these objects come in the real world it's possible we would like to have one to many relationships. Do have a look at the below image.
So in a simple world we would need to write some kind of logic for this transformation.
Below is a simple code extract from a middle tier where you can see there is a double loop, one loop which fills the customer collection and the other which adds the address object as an aggregated object to the customer object.
foreach (DataRow o1 in oCustomers.Tables[0].Rows) { obj.Add(new CustomerEntyityAddress()); // Fills customer foreach (DataRow o in oAddress.Tables[0].Rows) { obj[0].Add(new AddressEntity()); // Fills address } }
So to overcome the same we can create classes which are more of technical classes only to pass data efficiently across layers.
And because these are technical classes to just pass data around, we do not need to worry if they represent real world objects or not. In other words, we can flatten the class structure to avoid the double loop problem which we talked about previously.
You can see the below code snippet which shows how the CustomerDTO class inherits from CustomerEntity and aggregates the Address class to create a de-normalized class which is optimized for quick loading and viewing.
CustomerDTO
Address
public class CustomerDTO : CustomerEntity { public AddressEntity _Address = new AddressEntity(); }
You can see in the below code snippet how just one loop fills the de-normalized class.
foreach (DataRow o1 in oCustomers.Tables[0].Rows) { CustomerDTO o = new CustomerDTO(); o.CustomerCode = o1[0].ToString(); o.CustomerName = o1[1].ToString(); o._Address.Address1 = o1[2].ToString(); o._Address.Address2 = o1[3].ToString(); obj.Add(o); }
And as usual, the UI can always call the DTO to get data into the UI.
// From Data out Customer obj = new Customer(); List<CustomerDTO> oCustomers = obj.getCustomers();
The biggest advantage is quick and fast loading due to denormalized structure.
The biggest disadvantage is as these are technical classes which are optimized from a performance perspective, they can violate all rules of OOP and real world representation. So it is best to have a hybrid approach as discussed in the next section.
On one side Entity classes represent real world objects which follow OOP principles and on the other side DTO represents flattened classes to gain performance. So the best approach would be to take goodies from both worlds.
When data manipulation happens in the system (it can be insert, update, or delete form) we would like to ensure that we have data integrity and objects exposed on the user interface should mimic the real world. So for those scenarios a full blown normalized class structure (Entity) is the way to go.
But if you are looking to pull huge data to be displayed, like some lengthy report, then a denormalized structure / DTO is more suitable. I still remember in of our old projects these classes where termed as “Reporting classes”. The class structure for these classes never depicted real world objects.
In good old days we used to write these classes manually. I loved how carefully and tenderly I used to create those classes. World has moved ahead and we have awesome frameworks like EF (Entity Framework) and LINQ which can do magic in minutes. This article will not go beyond its scope to teach Entity Framework or LINQ.
But yes, if you understand the flavors of these classes, you can easily create better class structures by using EF or LINQ.
You can download the source code from here.
If you ever get time, do visit me my site for .NET interview questions and C# interview questions.