This is the third and final part of my short series on using custom entities in asp.net applications. In this part, we will look at some coding technique commonly used to program around custom entities and data transfer objects. For the sake of brevity, I will not go into the coding details rather focus on the architectural issues. So let’s get started.
Using Custom Entities and DTOs
Many of the sample applications show how to use Custom Entities (and DTOs). In addition to the standard UI, BLL and DAL Layer, the usual design is to have a fourth layer (usually known as Model Layer) consisting of Data Transfer Objects. The DTOs in the model layer is used for transfer of data betweent the three layers. Usually when a user makes a request, it flows from the UI to the BLL and finally to the DAL. In response, the DAL populates one or more DTOs and sends it back to the BLL. The BLL then sends the data back to the UI. Pay close attention to this last statement which is highlighted. To me, this is where we can employ different coding techniques and this decision is left to the developer. Let me explain these techniques in the following sections with examples.
Using DTOs - Only
In this technique, only DTOs are used between all the layers for transfer of data. The BLL returns the same DTOs received from the DAL to the UI. The UI binds to one or more instances of DTOs. Listing 1 shows this technique:
Listing 1
// Model Layer
using System;
public class EmployeeDTO
{
// properties
public int EmployeeID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Designation { get; set; }
public int Age { get; set; }
// constructor
public EmployeeDTO () {}
// constructor
public EmployeeDTO (int employeeId, string name, string address, string designation, int age)
{
this.EmployeeID = employeeId;
this.Name = name;
this.Address = address;
this.Designation = designation;
this.Age = age;
}
}
// Business Logic Layer
using System;
public class Employee
{
public Employee () {}
public IListGetAllEmployees ()
{
return EmployeeDAL.GetAllEmployees ();
}
public EmployeeDTO GetEmployeeById (int employeeId)
{
return EmployeeDAL.GetEmployeeById (employeeId);
}
public int Save (EmployeeDTO employee)
{
return EmployeeDAL.Save (employee);
}
// Validation…
// Business Rules…
}
The Emloyee entity in the BLL has a corresponding data transfer object (EmployeeDTO) in the Model Layer. The Employee entity has different CRUD methods. These methods accept and return one or more instances of type EmployeeDTO. This technique is shown in figure 1.
DTO DTO
UI <----------> BLL <-----------> DAL
Fig 1
There is nothing wrong in using DTOs throughout the application. But to me, this has a few shortcomings. First of all, this technique breaks the basic principle of OO programming, Encapsulation. Encapsulation states that both the state and behavior are held within the object. The object itself is responsible for managing its state and behavior. The state or behavior cannot be separated from the object. In the above code, the Employee class (residing in the BLL) does not define any state. It instead relies on the EmployeeDTO (Model Layer) to handle its state. This shows that the object is not in charge of its state and hence clearly breaks the principle of encapsulation.
Second, custom entities (defined in the BLL) have association with each other. These associations are defined by the help of their properties (primary keys). If they lack any property (as in the above code) then how do we define an association? I don’t see a point in defining associations in the Model Layer between data transfer objects (which lack any behavior as well).
Data Transfer Objects lack behavior yet they are not without advantage. Since they only consist of properties, they are light weight objects used to send data across subsystems and hence help improve the throughput of the system.
Using DTOs in parallel to Custom Entities
In this case, DTOs are limited between the BLL and DAL for transfer of data. However; the UI binds to custom entities instead of DTOs. This is possible since the custom entities define both the state and behavior. Before the UI binds to a custom entity, the custom entity copies over the properties of the DTO. This way, the data is available when the UI binds to a custom entity. Let us see this approach in listing 2:
Listing 2
// Model Layer
using System;
public class EmployeeDTO
{
// properties
public int EmployeeID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Designation { get; set; }
public int Age { get; set; }
// constructor
public EmployeeDTO () {}
// constructor
public EmployeeDTO (int employeeId, string name, string address, string designation, int age)
{
this.EmployeeID = employeeId;
this.Name = name;
this.Address = address;
this.Designation = designation;
this.Age = age;
}
}
// Business Logic Layer
using System;
public class Employee
{
// properties
public int EmployeeID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Designation { get; set; }
public int Age { get; set; }
// constructor
public Employee() {}
// constructor
public Employee (int employeeId, string name, string address, string designation, int age)
{
this.EmployeeID = employeeId;
this.Name = name;
this.Address = address;
this.Designation = designation;
this.Age = age;
}
// ------------------- CRUD Methods -------------------------------
public IListGetAllEmployees ()
{
return ConvertListOfEmployees (EmployeeDAL.GetAllEmployees ()); // convert to list of BO before sending to UI
}
public EmployeeDTO GetEmployeeById (int employeeId)
{
return MapToBO (EmployeeDAL.GetEmployeeById (employeeId)); // convert to BO before sending to UI
}
public int Save (Employee employee)
{
return EmployeeDAL.Save (MapToDTO (employee)); // convert to DTO before sending to UI
}
// ------------------- Converters ---------------------------------
// Convert list of EmployeeDTO to list of Employee
private void ConvertListOfEmployee (IListemployeeList)
{
IListEmployeeCollection;
foreach (EmployeeDTO employee in employeeList)
{
EmployeeCollection.Add (MapToBO (employee));
}
return EmployeeCollection;
}
// Convert an EmployeeDTO to Employee
private Employee MapToBO (EmployeeDTO employee)
{
return new Employee (employee.EmployeeID, Name, Age, Address, Designation);
}
// Convert an Employee to EmployeeDTO
private Employee MapToDTO (Employee employee)
{
return new EmployeeDTO (employee.EmployeeID, Name, Age, Address, Designation);
}
}
As you can see clearly, the above Employee Entity follows the principle of encapsulation. Both the state and behavior are encapsulated within the entity. However, in addition to the CRUD methods, the entity has a set of converters which are used to convert a DTO to a CE and vice versa. In my view, this is the only shortcoming of using this approach which may incur some overhead. But this overhead is minimal and can be ignored easily.
CE DTO
UI <----------> BLL <-----------> DAL
Fig 2
Why not use Custom Entities directly
Curios readers must be thinking “Why not just use custom entities throughout the layers” i.e. In addition to the UI, custom entities can be referenced by DAL as well. This way we don’t need to have an extra layer of data transfer objects. The answer to this is that referencing custom throughout will create a circular reference between the BLL and DAL. A reference to the DAL from the BLL is unidirectional. But if DAL also references the BLL, it results in a bidirectional reference which leads to a circular reference.
Summary
In this part, we saw how to use custom entities in conjunction with data transfer objects. We looked at two different techniques. The first technique uses DTOs for transfer of data through all the layers. This technique works well but does not follow the principle of encapsulation. Also, this leads to the confusion as what is the best place to define the association. Should we define it at the BLL layer or the Model layer?
The second technique uses DTOs only between the BLL and DAL. The UI communicates with the BLL through the custom entities. This approach removes both the problems the first approach, however; it incurs a slight overhead of converting custom entities to DTOs and vice versa.
With this, we come to the end of this series. But this is just not it. I plan to write more on custom entities as this has become the standard coding practice. Please do provide your feedback through your comments. Stay tuned for more...