AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=1360&pId=-1
Decoupling Business Logic Layer from the User Interface Layer using C#
page
by Joydip Kanjilal
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 71174/ 145

Introduction

This article explains how we can effectively de-couple the business logic layer of an application from the UI layer. It illustrates both binary coupling and XML coupling, explaining which one of the two is better to de-couple the business logic layer of an application from the user interface layer. I will show you how we can design a framework that will illustrate how we can achieve loose coupling between these layers. Let us name this framework as Business Process Execution Engine or BPEE in short.

The following sections discuss this framework, its objectives and shows how the same can be implemented in C#.

Designing a Business Process Execution Engine

We will design a framework called Business Process Execution Engine (BPEE) that will illustrate how effectively we can decouple the User Interface Layer from the Business Logic Layer of our applications. The Business Process Execution Engine is a Generic Engine that abstracts the execution logic and encapsulates the functional logic in the Business Layer. It is a Generic Abstraction to deal with a Common Set of CRUD (Create, Update, Read & Delete) methods. It will be designed using the Factory Design Pattern, Singleton Pattern and will focus on a loose coupling between the Business Logic and the UI layers.

Features of BPEE

The BPEE is a generic engine for all verticals and a generic abstraction to deal with the CRUD methods. It can be extended further to provide transaction management, configurator, workflow, security, exception management, etc. The intent of this engine is designing a generic framework that can abstract the transaction processing and reduce the complexities in writing the business logic code. Porting the execution logic to this generic framework will enable the developers to write significantly less code in the business logic layer. Moreover, this framework promotes loosely coupling and illustrates how effectively you can decouple the User Interface Layer and the Business Logic Layer of your applications to provide more flexibility in your design.

The Architectural and Operational Objectives

This section discusses the architectural and the operational objectives of this framework.

Architectural Objectives

·         Isolate the common Execution Logic of an application from its Business Logic.

·         Facilitate architectural ease of use for different verticals/modules in the future.

·         Provision for converting the Business Logic Layer to Business Rules Engine in the future.

·         Ensure that the Business Logic Layer is an autonomous component. In the long run, this component can be replaced by XSLT or XML based files.

·         Enable other generic services like Security, Workflow, Messaging, etc. to make use of the this framework.

Operational Objectives

·         Ensure that consistent standards are maintained.

·         Minimize coding and reduce the development time.

Implementing the Framework

The classes and interfaces involved in this framework are:

·         IBusiness

·         IBusinessLogic

·         IBusinessEntity

·         BusinessLogic

·         BusinessService

·         BusinessEntity

·         BLFactory

·         BEFactory

IBusiness is a top level interface that contains the CRUD methods implemented by all the Entities and Business Logic classes. The IBusinessLogic is base interface from which the BusinessLogic base class is implemented. All business logic classes that you create should inherit the BusinessLogic base class and implement the IBusinessLogic interface of this framework. The IBusinessEntity is the base interface for the BusinessEntity base class. All your entities will derive from this BusinessEntity class and implement the IBusinessEntity interface.

The following code listings show the IBusiness, IBusinessLogic and the IBusinessEntity interfaces.

Listing 1

using System;
using System.Data;
using System.Collections;
 
namespace Definitions
{
    public interface IBusiness
    {
        bool Create(int hashCode);
        bool Delete(int hashCode);
        bool Update(int hashCode);
        int Read();
     }
}

Listing 2

using System;
using System.Reflection;
 
namespace Definitions
{
    public interface IBusinessLogic : IBusiness
    {
        int HashCode
        {
            get;
            set;
        }
 
        int Read(int hashCode);
    }
}

Listing 3

using System;
namespace Definitions
{
    public interface IBusinessEntity
    {
        int Key
        {
            get;
            set;
        }
    }
}

The BusinessLogic class is the business logic base class from which all the business logic classes should derive. Similarly, the BusinessEntity class is the base class for all BusinessEntity classes. The BusinessService class is based on the Façade design pattern and is the gateway to this framework from the user interface’s perspective. The Presentation layer of the application interacts with this class to pass the necessary information. The BusinessService class uses dependency injection technique to decouple the business logic layer from the user interface layer of the application.

The following code listings show the BusinessLogic, BusinessEntity and the BusinessService classes.

Listing 4

using System;
using System.Data;
using System.Web;
using Definitions;
 
namespace Framework
{
    public class BusinessLogic : IBusinessLogic, IBusiness
    {
        private IBusinessEntity iBusinessEntity = null;
        private int hashCode = -1;
 
        protected BusinessLogic()
        {
 
        }
 
        public IBusinessEntity BusinessEntity
        {
            get
            {
                return iBusinessEntity;
            }
 
            set
            {
                iBusinessEntity = value;
            }
        }
 
        #region IBusiness Members
 
        bool IBusiness.Create(int hashCode)
        {
            return true;
        }
 
        bool IBusiness.Update(int hashCode)
        {
            return true;
        }
 
        bool IBusiness.Delete(int hashCode)
        {
            return true;
        }
 
        int IBusiness.Read()
        {
            return -1;
        }
        
        #endregion
 
        #region IBusinessLogic Members
 
      
        int IBusinessLogic.Read(int hashCode)
        {
            return -1;
        }
 
        public int HashCode
        {
            get
            {
                return hashCode;
            }
            set
            {
                hashCode = value;
            }
        }
 
        #endregion
    }
}

Listing 5

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Definitions;
 
namespace Framework
{
    public abstract class BusinessEntity : IBusinessEntity
    {
        private int key;
 
        public int Key
        {
            get
            {
                return this.key;
            }
            set
            {
                this.key = value;
            }
        }
    }
}

Listing 6

using System;
using Definitions;
 
namespace Framework
{
    public enum OperationType
    {
        Create, Update, Read, Delete
    }
 
   public class BusinessService
    {
       private IBusinessLogic iBusinessLogic = null;
       private IBusinessEntity iBusinessEntity = null;
 
       public IBusinessLogic BusinessLogic
       {
           get
           {
               return iBusinessLogic;
           }
 
           set
           {
               iBusinessLogic = value;
           }
       }
 
       public IBusinessEntity BusinessEntity
       {
           get
           {
               return iBusinessEntity;
           }
 
           set
           {
               iBusinessEntity = value;
           }
       }
 
       public int Execute(OperationType operationType)
       {
           int hashCode = -1;
           //Code to register the business entity 
           //instance in the DTORegister. On doing so,
           //the hash code of the instance is returned 
           //which is used to pass data across
           //the layers of the application.
           switch (operationType)
           {
               case OperationType.Create:
                   iBusinessLogic.Create(hashCode);
                   break;
 
               case OperationType.Update:
                   iBusinessLogic.Create(hashCode);
                   break;
               case OperationType.Delete:
                   iBusinessLogic.Delete(hashCode);
                   break;
               case OperationType.Read:
                   hashCode = iBusinessLogic.Read(hashCode);
                   break;              
           }
 
           return hashCode;
       }
    }
}

You need to insert the code to register the business entity instance in the DTORegister, a hash table containing DTO instances. You can find my article on Data Transfer Object here. I have commented the portion of the code in the Execute() method where you need to insert it. You should also un-register the DTO instance once the CRUD operation is over.

We have two factory classes called BusinessLogicObjectFactory and BusinessEntityObjectFactory. These classes are based on the Factory design pattern and are responsible for returning the required business logic and business entity instances. The following code listings illustrate the two factory classes, BLFactory and BEFactory.

Listing 7

using System;
using System.Text;
using System.Collections.Generic;
using Definitions;
using Framework;
using BusinessObjects;
 
namespace ObjectFactory
{
    public enum BLObjectType
    {
        Employee, Department
    }
 
    public class BLFactory
    {
        public static IBusinessLogic GetBLObject(BLObjectType bLObjectType)
        {
            IBusinessLogic iBusinessLogic = null;
 
            switch (bLObjectType)
            {
                case BLObjectType.Employee:
                    iBusinessLogic = new EmployeeBO();
                    break;
                case BLObjectType.Department:
                    iBusinessLogic = new DepartmentBO();
                    break;
                default:
                    return null;
            }
 
            return iBusinessLogic;
        }
 
        public static IBusinessLogic GetBLObject(string bLObjectType)
        {
            IBusinessLogic iBusinessLogic = null;
 
            switch (bLObjectType)
            {
                case "EmployeeBO":
                    iBusinessLogic = new EmployeeBO();
                    break;
                case "DepartmentBO":
                    iBusinessLogic = new DepartmentBO();
                    break;
                default:
                    return null;
            }
 
            return iBusinessLogic;
        }
    }
}
 

Listing 8

using System;
using System.Text;
using System.Collections.Generic;
using Definitions;
using Framework;
using DataObjects;
 
namespace ObjectFactory
{
    public enum BEObjectType
    {
        Employee, Department
    }
 
    public class BEFactory
    {
        public static IBusinessEntity GetBEObject(BEObjectType bEObjectType)
        {
            IBusinessEntity iBusinessEntity = null;
 
            switch (bEObjectType)
            {
                case BEObjectType.Employee:
                    iBusinessEntity = new EmployeeDO();
                    break;
                case BLObjectType.Department:
                    iBusinessLogic = new DepartmentDO();
                    break;
                default:
                    return null;
            }
 
            return iBusinessEntity;
        }
 
        public static IBusinessEntity GetBEObject(string bEObjectType)
        {
            IBusinessEntity iBusinessEntity = null;
 
            switch (bEObjectType)
            {
                case "EmployeeDO":
                    iBusinessEntity = new EmployeeDO();
                    break;
                case "DepartmentDO":
                    iBusinessLogic = new DepartmentDO();
                    break;
                default:
                    return null;
            }
 
            return iBusinessEntity;
        }
    }
}

The following code listings show the EmployeeBO and the EmployeeDO classes. While the former is the custom business logic class, the later is the custom business entity class. As mentioned earlier, both these classes are instantiated using the respective factory classes shown earlier.

Listing 9

using System;
using System.Data;
using System.Data.Common;
using System.Collections.Generic;
using System.Text;
using Definitions;
using Framework;
 
namespace BusinessObjects
{
    public class EmployeeBO : BusinessLogic,IBusinessLogic
    {
        bool IBusiness.Create(int hashCode)
        {
            return true;
        }
 
        bool IBusiness.Update(int hashCode)
        {
            return true;
        }
 
        int IBusinessLogic.Read(int hashCode)
        {
            return -1;
        }
 
        int IBusiness.Read()
        {
            return -1;
        }
 
        bool IBusiness.Delete(int hashCode)
        {
            return true;
        }
    }
}

Listing 10

using System;
using Definitions;
using Framework;
 
namespace DataObjects
{
    [Serializable]
    public class EmployeeDO : BusinessEntity
    {
        private string _name;
        private string _address;
 
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
 
        public string Address
        {
            get { return _address; }
            set { _address = value; }
        }
    }
}
Decoupling the User Interface Layer from the Business Logic Layer

Now, once a Business Logic and a Business Entity instance is created via the BusinessLogicObjectFactory and the BusinessEntityObjectFactory classes respectively, the exposed public properties of the BusinessService class, namely BusinessLogic and BusinessEntity, are used to set the business logic and the business entity instances created using the factory classes mentioned above. This is done in the presentation layer or the user interface layer of the application.

The following code snippet shows how you can use the BusinessService class to decouple the business logic from the user interface layer of your application using dependency injection technique.

Listing 11

IBusinessLogic iBusinessLogic = BLFactory.GetBLObject(BLObjectType.Employee);
IBusinessEntity iBusinessEntity = BEFactory.GetBEObject(BEObjectType.Employee);
//Code to populate the business entity instance with data
BusinessService businessService = new BusinessService();
businessService.BusinessLogic = iBusinessLogic;
businessService.BusinessEntity = iBusinessEntity;
businessService.Execute(OperationType.Create);

Note how the factory classes have been used to create the business logic and the business entity instances respectively. The static methods GetBLObject and GetBEObject accept references of the BLObjectType and BEObjectType enumerators. These methods then check the corresponding values and return business logic or business entity instances appropriately. Refer to the code shown in the listing 7 and 8 above. You can create instances of the DepartmentBO or the DepartmentDO by simply changing the binding as shown below.

Listing 12

IBusinessLogic iBusinessLogic = BLFactory.GetBLObject(BLObjectType.Department);
IBusinessEntity iBusinessEntity = BEFactory.GetBEObject(BEObjectType.Department);
//Code to populate the business entity instance with data
BusinessService businessService = new BusinessService();
businessService.BusinessLogic = iBusinessLogic;
businessService.BusinessEntity = iBusinessEntity;
businessService.Execute(OperationType.Create);

We can make the decoupling even more efficient (more loosely coupled) using a XML file where we can store the binding information. The binding information here implies the information that relates a user interface to its business logic and business entity classes.

Then we can use the factory classes to create the business logic or business entity instances as illustrated below.

Listing 13

IBusinessLogic iBusinessLogic = BLFactory.GetBLObject
    (ConfigurationManager.GetBLObjectName(“U001”));
IBusinessEntity iBusinessEntity = BEFactory.GetBEObject
    (ConfigurationManager.GetBEObjectName(“U001”));
//Code to populate the business entity instance with data
BusinessService businessService = new BusinessService();
businessService.BusinessLogic = iBusinessLogic;
businessService.BusinessEntity = iBusinessEntity;
businessService.Execute(OperationType.Create);

In the above code example, notice that we have used a class called ConfigurationManager to read the binding information from a XML file. The methods GetBLObjectName and GetBEObjectName return the names of the business logic and the business entity classes respectively. These methods accept the screen ID as parameters and use this ID to search and retrieve the binding information from the XML file that contains it.

We are done! I leave it to the readers to design the XML schema that relates a user interface to its corresponding business logic or business entity class name. Then you have to implement a class that reads this binding information; you can name it as ConfigurationManager.

Downloads
Suggested Readings
Conclusion

In this article I have implemented a Business Process Execution Framework that has illustrated how effectively we can decouple the User Interface and the Business Logic Layers of our applications. There is a lot more to it; it is simply a beginning. You need to incorporate a lot of things to make this framework more robust, scalable and flexible. I hope that the readers will take this forward and send me their comments and suggestions. Happy reading!



©Copyright 1998-2018 ASPAlliance.com  |  Page Processed at 2018-12-12 7:34:59 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search