Click here to Skip to main content
Full site     10M members (32.3K online)    

Four ways of passing data between layers

Table of contents

What’s the goal?

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.

Layers and Tiers are different things

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.

Data incoming and data outgoing

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.

Method 1: Non-uniform way of passing

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.

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.

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).

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?

The final thing is the CustomerDal class which just returns the dataset using ADO.NET to pass the same to the middle layer.

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:

Problems and benefits with Non-uniform way

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”.

Circular dependency problem

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.

Method 2: Uniform way of passing data (Entity classes)

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.

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.

Problems and benefits with uniform way

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
    }
} 

Method 3: Data transfer objects (DTO)

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.

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(); 

Problems and benefits with DTO

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.

Method 4: Hybrid approach (Entity + DTO)

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.

When to use what?

Ready made solutions: Entity Framework and LINQ

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.

Source code

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.

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search 
Per page   
GeneralMy vote of 5
bmwz9
23hrs 29mins ago 
GeneralMy vote of 5
raj ch
10 May '13 - 21:44 
GeneralThanks
Abhimanyu Kumar Vatsa
10 May '13 - 18:59 
Generalmy vote of 5
blachsmith
10 May '13 - 16:36 
GeneralMy vote of 5
Member 9780226
9 May '13 - 21:51 
GeneralMy vote of 5
VitorHugoGarcia
9 May '13 - 6:37 
GeneralMy vote of 5
Eduardo Alba
8 May '13 - 15:20 
QuestionThank you! Well done!
Member 9924993
22 Apr '13 - 10:36 
GeneralMy vote of 5
Mihai MOGA
14 Dec '12 - 5:16 
GeneralMy 5
Shahriar Iqbal Chowdhury
14 Dec '12 - 2:18 
QuestionMy 5 !
WajihAhmed
7 Dec '12 - 21:10 
GeneralMy vote of 5
rfjaquez
1 Dec '12 - 10:30 
GeneralMy vote of 5
ravithejag
27 Nov '12 - 19:57 
GeneralMy vote of 5
Monjurul Habib
24 Nov '12 - 21:23 
Suggestion[My vote of 2] Started off well but then seriously went down hill
drm9988912
20 Nov '12 - 2:28 
GeneralRe: [My vote of 2] Started off well but then seriously went down hill
Shivprasad koirala
20 Nov '12 - 17:31 
GeneralMy vote of 5
VadimAlk
19 Nov '12 - 18:43 
GeneralMy vote of 5
Chamila Ranasinghe
15 Nov '12 - 17:22 
GeneralMy vote of 5
Himanshu Thawait
14 Nov '12 - 11:42 
GeneralVery Nice Explanation
Phanindra261
14 Nov '12 - 9:00 
GeneralRe: Very Nice Explanation
Shivprasad koirala
14 Nov '12 - 20:59 
GeneralRe: Very Nice Explanation
HariPrasad katakam
26 Nov '12 - 0:21 
QuestionEnterprise Library
Juzer
14 Nov '12 - 5:24 

Last Updated 8 May 2013 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2013