Creating a N/3-Tier/Layered Application

For updated version of the article please visit www.agnosticdevs.com

     

    “In most of the Business Solutions, we tend to implement good-reusable code. There are a lot of different ways to achieve the same.

    So, we are here to discuss one of those ways. This Article is about Layered/Tiered architecture using C#.NET. This sample implements a Business need to Register Employee with HR Details, using SQL Database as backend.

     

    What is N/3-tier architecture?

    N/3-Tier Architecture is an Approach to separate the Business logic (e.g. set validations and rules), Data Access and User Interface. It helps to make the logics and validations simpler, separate from Database logics and still keeping the Data access code reusable.

    In a scenario, where you have to change the whole UI of an Application and the Business Rules, Database structure etc. is still kept same. The simple approach may be like writing the whole application again and setup validation, rules and db access again.

    But, still this can be avoided, if we use N\3-Tier architecture.

    A few more benefits are listed below:

    • Business logic is designed separately that’s why it is fully reusable. The new application may simply utilize the same class library reference to setup business rules.

    • In case, there’s some requirement to change some database logic, the only layer this affects is Data Access. Business logic or UI is least affected by the same.

    • The instantiation of Object is totally dependent on the Layers and Access Components, so no Memory Resources are wasted.

    • All three layers can be designed by a different set of specialized Developers, and this makes it easy to develop application faster and easily as the development of all three layers can be carried out in parallel mode.

    I believe there are a lot more other benefits, that you may explore yourself in detail.”

As per the above theory, we have three layers:

 

  1. Presentation Layer or User Interface (UI)

  2. Object Mapping Tier (OM)

  3. Business Logic Layer (BLL)

  4. Data Access Layer (DAL)

 

The above 3 layers will be coded into separate projects and included into a single VS Solution.

 

How it works:

 

These three projects are to be created under same solution, in a manner to achieve the 3 Tier or Layered Model for our application. The Type of projects and naming convention as per our example can possibly be as follows:

 

  1. Business Logic Layer

 

Type                                                 :       Class Library

Name                                                        :       BLL

Parent                                              :       _3TierArchitecture (VS Solution)

Other Project Outputs to be referred      :       DAL, OM

 

  1. Data Access Layer

 

Type                                                 :       Class Library

Name                                                        :       DAL

Parent                                               :       _3TierArchitecture (VS Solution)

Other Project Outputs to be referred      :       OM

 

  1. Presentation Layer or User Interface:

 

Type                                                 :       Web Application

Name                                                        :       EmployeeManagement

Parent                                               :       _3TierArchitecture (VS Solution)

Other Project Outputs to be referred      :       BLL

 

The above convention/sequence should be maintained to ensure no references are missed.

 

Here, the prime motive behind creating a separate layer for OM is to ensure the DAL is a standalone layer and to be able to type cast object of BLL to a DAL understandable form. As we are not adding the reference of BLL into DAL there must be something to carry the data to BLL and vice versa.

 

The actual sequence of data flow:

 

Input to DB:

 

We create objects of Business Objects (defined in BLL) in Presentation Layer, BLL performs validations, processes, calculations, and other steps evolved logics, then finally BLL Object is converted to OM object and is sent to DAL to be stored in Database.

 

 

 

 

Output to UI or Presentation Layer:

 

We call a BLL function with some parameter; BLL performs validations on input parameters and Calls DAL function with same parameter, DAL Queries database with the parameters provided returns the Data to BLL in OM Format or DataTable, BLL casts the object of returned information on the basis of OM ReturnType requested. Then finally return the BLL object to Presentation Layer.

 

For the above sequence, we need to refer Class Library (DLL) of DAL and OM into BLL, Class Library of OM into DAL and Class Library of BLL into EmployeeManagement Project (UI)

 

After creating the above structure your solution should look similar to this screen:

clip_image002

 

Step-1: Business Object Mapping and Business Object Modeling (OM and BLL)

 

Now, we need to draw the structure for our entity employee in database to identify the Business Objects of our application.

 

Let’s say, we have a requirement to register employee with its personal and HR details. For the same we have these two tables in our database:

 

clip_image004

 

 

 

 

In the above figure we have two tables called tblEmployee and tblEmployeeHRDetails. On the basis of the same we can say:

 

We have two business entities

 

    1. Employee – personal information

    2. Employee -  HR Information

    3. Referrer Person

 

We have following Business Rules:

 

  1. Employee – HR information is dependent on the existence of Employee – Personal information.

  2. Referrer is dependent on Employee HR Information.

  3. Each Employee may have 2 referrers.

 

 

 

clip_image006

 

So the above can be interpreted as the following class structures:

clip_image008clip_image010

 

 

 

 

 

 

 

As per the above diagram, we have:

 

  1. 3 Object Mapping Classes

    1. BaseEmployee

    2. BaseEmployeeHRDetails

    3. EmployeeReferrer

  2. 3 Business Objects

    1. Employee

    2. EmployeeHRDetails

    3. EmployeeReferrer

  3. 3 Rules

    1. Employee to HR-Details

    2. EmployeeHRDetails.Referrer1 to EmployeeReferrer

    3. EmployeeHRDetails.Referrer2 to EmployeeReferrer

 

There are a lot more business object and rules that can be identified here but still we are trying the sample as simple as possible. So once you are through with this one, try to identify them all at your end.

 

Note: A few examples can be:

  1. Employee.PermanentAddressInfo and  Employee.CurrentAddressInfo

  2. EmployeeHRDetails.EmployeeStatusID

  3. EmployeeHRDetails.EmployeeStatusID

  4. EmployeeHRDetails.DepartmentID

 

and many more….

 

The sample OM BaseEmployee should look like:

 

public class BaseEmployee

    {

        #region Properties

 

        private Int64 _ID;

        /// <summary>

        /// ID of Employee Record, database column "ID"

        /// </summary>

        public Int64 ID

        {

            get

            {

                return _ID;

            }

            set

            {

                _ID = value;

            }

        }

 

        private string _FirstName;

        /// <summary>

        /// First Name of employee, Database Column "FirstName"

        /// </summary>

        public string FirstName

        {

            get

            {

                return _FirstName;

            }

            set

            {

                _FirstName = value;

            }

        }

        // Other properties

    }

 

The corresponding BLL Class for the above should look like:

 

public class Employee : BaseEmployee

    {

        private static DALEmployee _ObjDal;

 

        #region Properties

        /// <summary>

        /// ID of Employee Record, database column "ID"

        /// </summary>

        public new Int64 ID

        {

            get

            {

                return base.ID;

            }

            set

            {

                base.ID = value;

            }

        }

 

        /// <summary>

        /// First Name of employee

        /// </summary>

        public new string FirstName

        {

            get

            {

                return base.FirstName;

            }

            set

            {

                if (value == "")

                    throw new Exception("First name can not be blank");

                else if (value.Split("0123456789".ToCharArray()).Length > 1)

                    throw new Exception("First name can not have numeric characters");

                /// in the same way as above, you can implement your validations on data prior to

                /// assigning the same to your Properties.

                base.FirstName = value;

            }

        }

    }

In the above code, notice the two things:

 

  1. The class is inherited from BaseEmployee (OM) to make compatible with the same while passing the object of BLL to DAL, refer to DAL block for Code sample.

  2. Use of new keyword in property declarations for this class. This is concept of shadowing(VB) or hiding(C#). Using this feature of C# we are hiding the OM.BaseEmployee.ID and other properties, creating a mapping for BLL Properties. So that a specific set of validations can be performed before initializing the OM Layer Properties. Validation Example can be found in above code as BLL.Employee.FirstName Property.

 

The next step is to define method to make calls to DAL and perform DB interactions wherever required.

 

Write to Database:

 

/// <summary>

        /// Update the Employee Information to database.

        /// </summary>

        /// <returns></returns>

        public Boolean Update()

        {

            BaseEmployee ObjEmployee = this;

            return _ObjDal.Update(ref ObjEmployee);

        }

 

In the same, BLL.Employee Class we have defined the above function.

Here, the _ObjDal is a private static object of DAL.DALEmployee Class, so as to keep the DAL Object single in all instances.

 

As you may notice the object of current Class (BLL.Employee denoted as this) is first converted to OM.BaseEmployee and then passed to DAL.DALEmployee’s Update method. That is how BLL communicates with DAL without creating any cyclic reference to DAL and BLL (BLL-DAL-BLL).

 

Note: Implementation of Update method can be found DAL block.

 

Read from Database:

 

public Employee(Int64 EmployeeID)

{

       this.ID = EmployeeID;

       BaseEmployee ObjEmployee = this;

       _ObjDal.GetEmployeeByID(ref ObjEmployee);

}

 

In a common scenario where you may require to fetch the object from database by ID (Primary key Column), you may overload the constructor like above.

 

Here, we have first converted the object of current class (BLL.Employee) to OM.BaseEmployee and then passed the reference of the same to DAL.DALEmployee.GetEmployeeByID. In the implementation part of the same method database record is loaded in DataTable/DataSet or DataReader objects and values are assigned to object reference of OM.BaseEmployee, which ultimately affects the current class automatically.

 

Step-2: Data Access Layer: (DAL)

 

Now, here we need to identify the methods required for various Business Objects. As per our requirements, we have the following:

 

  1. Employee

    1. Register or Add

    2. GetEmployeeByID

    3. Update

    4. GetEmployeeByDepartment

 

  1. EmployeeHRDetails

    1. Update

  2. EmployeeReferrer

    1. Update

 

Sample Update Method:

 

public Boolean Update(ref BaseEmployee ObjEmployee)

        {

            SqlConnection SqlCn = new SqlConnection(_ConnectionString);

            SqlTransaction SqlTran = null;

           

            try

            {

                SqlParameter[] Params;

 

                SqlParameter ParamID = new SqlParameter("@EmployeeID", SqlDbType.BigInt);

                ParamID.Value = ObjEmployee.ID;

 

                SqlParameter ParamFirstName = new SqlParameter("@FirstName", SqlDbType.NVarChar, 200);

                ParamFirstName.Value = ObjEmployee.FirstName;

 

                SqlParameter ParamMiddleName = new SqlParameter("@MiddleName", SqlDbType.NVarChar, 200);

                ParamMiddleName.Value = ObjEmployee.MiddleName;

Params = new SqlParameter[] { ParamID, ParamRefByName1, ParamRefByAddress1, ParamRefByPhoneNo1, ParamRefByEmail1, ParamRefByName2, ParamRefByAddress2, ParamRefByPhoneNo2, ParamRefByEmail2, ParamDeptID, ParamEmpTypeID, ParamDOJ };

 

                //Uncomment the following lines in order to implement actual execution, this is just a sample.

                SqlHelper.ExecuteNonQuery(SqlTran, _UpdateHrDetails, Params);

                SqlTran.Commit();

                SqlCn.Close();

                SqlCn.Dispose();

                return true;

            }

            catch (Exception Ex)

            {

                // Process your transaction Rollbacks here (if any) then Pass exception to BLL

                if (SqlTran != null)

                    SqlTran.Rollback();

                throw Ex;

            }

}

Please refer the full method in Code samples.

 

Sample GetEmployeeByID Method

 

public void GetEmployeeByID(ref BaseEmployee ObjEmployee)

        {

            try

            {

                DataTable DtEmployee = new DataTable();

 

                SqlParameter ParamID = new SqlParameter("@ID", SqlDbType.BigInt);

                ParamID.Value = ObjEmployee.ID;

 

                SqlParameter[] Params = new SqlParameter[] { ParamID };

 

                DtEmployee.Load(SqlHelper.ExecuteReader(_ConnectionString, CommandType.StoredProcedure, _GetEmployeeByID, Params));

                if (DtEmployee.Rows.Count > 0)

                {

                    ObjEmployee.AlternateEmail = DtEmployee.Rows[0]["AlternateEmail"].ToString();

                    ObjEmployee.BloodGroup = DtEmployee.Rows[0]["BloodGroup"].ToString();

                    ObjEmployee.CurrentAddress = DtEmployee.Rows[0]["CurrentAddress"].ToString();

                    ObjEmployee.CurrentLandLine = DtEmployee.Rows[0]["CurrentLandline"].ToString();

                    ObjEmployee.CurrentMobile = DtEmployee.Rows[0]["CurrentMobile"].ToString();

                    ObjEmployee.DateOfBirth = Convert.ToDateTime(DtEmployee.Rows[0]["DateOfBirth"].ToString());

                    ObjEmployee.DrivingLicenseNumber = DtEmployee.Rows[0]["DrivingLicenseNumber"].ToString();

                    ObjEmployee.FirstName = DtEmployee.Rows[0]["FirstName"].ToString();

                    ObjEmployee.IsMarried = Convert.ToBoolean(DtEmployee.Rows[0]["IsMarried"]);

                    ObjEmployee.LastName = DtEmployee.Rows[0]["LastName"].ToString();

                    ObjEmployee.MiddleName = DtEmployee.Rows[0]["MiddleName"].ToString();

                    ObjEmployee.PassportNumber = DtEmployee.Rows[0]["PassportNumber"].ToString();

                    ObjEmployee.PermanentAddress = DtEmployee.Rows[0]["PermanentAddress"].ToString();

                    ObjEmployee.PermanentLandline = DtEmployee.Rows[0]["PermanentLandline"].ToString();

                    ObjEmployee.PermanentMobile = DtEmployee.Rows[0]["PermanentMobile"].ToString();

                    ObjEmployee.SpouseFatherName = DtEmployee.Rows[0]["SpouseFatherName"].ToString();

                }

 

                DtEmployee.Dispose();

            }

            catch (Exception Ex)

            {

                // Process your transaction Rollbacks here (if any) then Pass exception to BLL

                throw Ex;

            }

        }

 

 

Note: There can be many more implementations and requirements but we here want to keep it simple. Any more complex logic can also be implemented using the same pattern. Try it yourself.

 

CREDITS: 

“Alok Arora

Development Team Leader - XentaQSys Technologies

Technical Consultant – Learning Geeks

Join my ASP.NET Knowledge base group by sending "ADD ME!" on gordo_matthews0207@yahoo.ca

Thanks and Regards

10 Comments

Comments have been disabled for this content.