11

I did some research before posting this question. Among other questions or post, one of those is provided below. I could not get a clear mind how to determine..

Business Objects within a Data Access Layer

I have a Repository and the Business Layers call the repository to retrieve the data. For example, say I have the following classes for BLL and DAL:

class BllCustomer
{
    public int CustomerId {get; set;}
    public String Name {get; set;}
    public BllAddress Address {get; set;}
}

class BllAddress
{
     public int AddressId {get; set;}
     public String Street {get; set;}
     public String City {get; set;}
     public String ZipCode {get; set; }
}

class DalCustomer 
{
    public int CustomerId {get; set;}
    public String Name {get; set;}
    public int AddressID {get; set;}
}

class DalAddress
{
     public int AddressId {get; set;}
     public String Street {get; set;}
     public String City {get; set;}
     public String ZipCode {get; set; }
}

If the BLL wants to retrieve a Customer object, it would call GetCustomerById(customerId) in DAL.

The following are my concerns I could not get a clear mind:

  1. I cannot see how to determine what object the GetCustomerById in DAL should return? Should it return BllCustomer or DalCustomer?

  2. Where should be the retrieving (and/or converting to Business object) of the address associated with the customer?

If the DAL returns Dal objects then, the logic to retrieve and fill in the Address can only be in the BLL. If the DAL returns BLL objects, then the logic to retrieve and fill in the Address can be either in the BLL or DAL. Currently the DAL is returning the Business Objects and the logic to fill it in is in the DAL.

From what I read, I guess there is no right or wrong. From the link included above, there are people saying one way and the others are saying the other way. But how do I determine which would works best for my case?

Any help would be appreciated.

4
  • 2
    My first question would be: is this a legacy application? There are plenty of ORM Frameworks out there that make this kind of code obsolete and I would urge you to consider such a framework... – JDT Jan 30 '15 at 15:08
  • @JDT I'm not sure what you mean, I am using Entity Framework and having the exact same problem. As I understand it, you're not supposed to use your ORM as domain objects, so where is the translation made? – pseudocoder Nov 6 '15 at 15:11
  • Why would your ORM framework not return objects that are domain objects as well? – JDT Nov 6 '15 at 15:15
  • 4
    @JDT The ORM (EF in this case) returns entity classes that represent, typically, one database table per class. This is usually similar to, but not necessarily the same as, the domain classes. Maybe you are just saying that it is ok to use ORM classes as domain classes? I have read in a number of places that this is a no-no. – pseudocoder Nov 6 '15 at 15:30
5

I cannot see how to determine what object the GetCustomerById in DAL should return? Should it return BllCustomer or DalCustomer?

It should return a DalCustomer object, returning a BllCustomer object will break the single-responsibility principle. You can view the DalCustomer object as the interface or contract consumed by the business layer (or consumer). In effect if it returned a BllCustomer the DAL would have to cater for every business layer object that calls it or could potentially call it.

Where should be the retrieving (and/or converting to Business object) of the address associated with the customer?

The conversion should be done in a view model or manager. You need to have a intermediary to call your service or data access component. If you feel the need you can have a conversion in your BllCustomer object. But then when you swap your DAL from MSSQL to Oracle for example your returned object (or interface) must remain the same.

Preferably your business layer should also be independent of your Data Layer. The Business Layer is responsible for your business rules. It is here you will add your validations using a validation framework to enforce your business rules.

1
  • Note that the above answer should then also imply that the DalCustomer shouldn't then be the entity used by EF. DALs should not return their actual entities (which, to remain consistent with the given naming, would be called DatabaseCustomer). So it depends how you interpret the name "DAL Customer". Is it the internally used object to consume the database by the DAL itself, or the externally returned object that the DAL's consumers consume? This answer assumes the latter. – Flater Dec 27 '20 at 14:01
4

Your repository should return the BLL or domain object. chances are you don't need a DAL object at all.

public class Customer
{
    public string Name {get; private set;}
    public Customer(string name)
    {
        this.Name = name;
    }
}

public class Repository
{
    public Customer GetCustomer(string id)
    {
        //get data from db
        return new Customer(datarow["name"]);
    }
}
2
  • Should BLL or separate class library expose interfaces instead of concrete classes like Customer? – Yola Feb 15 '20 at 10:50
  • 1
    No. Its fine to expose concrete classes. An interface for the repository would be useful – Ewan Feb 15 '20 at 11:13
3

Typically, the DAL has no knowledge of the BLL. Think of it this way, a different application with a different BLL could use the same DAL. A Payables application/module and a Receivables app for the same company would share data (clients, charges, payments, etc.). Trying to have one DLL have knowledge of more than one BLL would be very difficult and unnecessary. This would also allow you to change your data storage with no impact on the BLL (as long as you don't break the interfaces).

You now can pass a DAL object to the BLL or you could create a third set of objects: Entity. These would contain just the values to be passed around together. The DAL would reference the entity and interact with your storage/database and the BLL would handle all the logic and reference the DAL.

class EntCustomer
{
    public int CustomerId {get; set;}
    public String Name {get; set;}
    public BllAddress Address {get; set;}
}
class BllCustomer
{
   //reference EntCustomer, DalCustomer and handle business rules/logic
}

class DalCustomer 
{
   //reference EntCustomer and interact with data storage
}
3
  • Thank you for your comment. I agree with you.. I can already see that our DAL/(Repository) is already filled with logics like if the type is A, then go retrieve data from table B, but if the type is C,then go retrieve data from table C. But I am confused with your example using EntCustomer. In my case, DalCustomer is mirror of the tables in DB. Can you provide more examples, how the EntCustomer is used or why I should used it and benefits of it.I'm thinking to change DAL to return DalObjects to the BLL. Bll will hangle conversion to Business Objs,retrieve and fill in nested obj. – ShamirDaj Nov 2 '14 at 5:09
  • Can you provide more feedback? – ShamirDaj Nov 3 '14 at 4:38
  • I think that the purpose of the Ent-objects is to just transfer data between DAL and BLL. Your DAL classes can continue to mirror the db structure, but those classes would be internal to DAL. When the BLL asks for data from DAL, the DAL would fetch the required DAL-objects from the database (dalcustomer + daladdress) and construct an instance of EntCustomer from them and return it to BLL. – artokai Nov 3 '14 at 19:37
1

Your database, along with its entities, should be solely the concern of your data access layer. Your business layer declares requirements via interfaces, and your service layers (Data Access being one of them) will implement. What moves across the layers of your application are Business Objects and only business objects. Every layer will declare its own entities for its own convenience. That will be of no concern to any other layer. Application Layer may have ViewModels for its own use. DataAccessLayer migh use Entity FrameWork entities and contexts. These as well should be internal to the layer, and not visible to other layers.

Your Service Layers depends on (adds reference to), your business layers, and never the other way around. Your application layers will put things together, plugging in services into your business layer.

In your example, I would go something like this. (this is a very simplistic example just to demonstrate the correct order of dependencies).

namespace BusinessLayer
{
    public enum RecordStatus
    {
        InActive,
        Active,
        Suspended
    }
    public class BllCustomer
    {
        public int CustomerId { get; set; }
        public string Name { get; set; }
        public BllAddress Address { get; set; }
        public RecordStatus Status { get; set; }
    }

    public class BllAddress
    {
        public int AddressId { get; set; }
        public string Street { get; set; }
        public string City { get; set; }
        public string ZipCode { get; set; }
    }
    /// <summary>
    /// Your business layer declares requirements.
    /// </summary>
   public interface CustomerServices
    {
        BllCustomer GetById(int customerId);
    }
}
namespace DataAccessLayer
{
    //I am a simple wrapper. Will translate when necessary from Dataacess datatypes to BusinessLogic datatypes.
    //I am very useful when enums are involved.
   internal class DalCustomer
    {
        public BusinessLayer.BllCustomer Customer { get; init; }
        public int CustomerId { get => Customer.CustomerId; set => Customer.CustomerId = value; }
        public string Name { get => Customer.Name; set => Customer.Name = value; }

        /// <summary>
        /// Translate Sql column's A/I/S string to Enum type declared by our Business Layer.
        /// </summary>
        public string RecordStatus
        {
            get
            {
                switch (Customer.Status)
                {
                    case BusinessLayer.RecordStatus.Active:
                        return "A";
                    case BusinessLayer.RecordStatus.InActive:
                        return "I";
                    case BusinessLayer.RecordStatus.Suspended:
                        return "S";
                    default:
                        return null;
                }
            }
            set
            {
                if (Equals(value, "A"))
                    Customer.Status = BusinessLayer.RecordStatus.Active;
                else if (Equals(value, "S"))
                    Customer.Status = BusinessLayer.RecordStatus.Suspended;
                else
                    Customer.Status = BusinessLayer.RecordStatus.InActive;
            }
        }

    }
   public class CustomerRepository : BusinessLayer.CustomerServices
    {
        private string _connectionString = "from constructor...";
        public BusinessLayer.BllCustomer GetById(int customerId)
        {
            using (var cn = new SqlConnection(_connectionString))
                using(var cm = new SqlCommand(
                    "SELECT c.CustomerId,c.Name,c.Status,a.AddressId,a.Street,a.City,a.ZipCode" +
                    " FROM CUSTOMER c" +
                    " INNER JOIN Address a On c.AddressId = a.AddressId" +
                    " WHERE c.CustomerId = @Id",cn))
            {
                cn.Open();
                cm.Parameters.AddWithValue("@Id", customerId);
                using (var reader = cm.ExecuteReader())
                    if (reader.Read())
                    {
                        var address = new BusinessLayer.BllAddress();
                        //fill address fields ....
                        //....

                        //create BO and wrap it with Data Access entity.
                        var bo = new BusinessLayer.BllCustomer() { Address = address } ;
                        var entity = new DalCustomer() { Customer = bo };
                        //fill entity, will auto fill bo...
                        ///

                        //return Business Object.
                        return bo;
                    }
            }
            return null;
        }
    }
}
namespace ApplicationLayer
{
    public class CustomerViewModel
    {
        public BusinessLayer.CustomerServices CustomerServices { get; } = new DataAccessLayer.CustomerRepository();

        public BusinessLayer.BllCustomer Customer { get; set; }

    }
}
1

Function before nomenclature

There is a bit of name interpretation going on here. I suspect you may have gotten things twisted around and are actually missing an additional DTO class.

You have your classes a name, and are now asking us where you should use these classes, based on the name you arbitrarily gave them. That's doing things the wrong way around.

  • First you need to know where you're looking (which interaction between which components).
  • Then you decide to create a DTO to facilitate data transfer between them.
  • Then you name your class after the data it contains.

It makes no sense to name a class and decide its content without knowing where it is going to be used.


DTOs are inbetween layers

Every layer deals with two kinds of objects: the ones they expose, and the ones they consume from their own dependency. Using a common naming scheme:

 DATABASE
    |
    |  Entity
    V
   DAL
    |
    |  Data object
    V
   BLL
    |
    |  Domain/business object
    V
   WEB
    |
    |  (View)Model or response object
    V
WEB REQUEST

So, the DAL consumes entities (from the database) and exposes data objects (to its consumer, in this case the BLL).
In turn, the BLL consumes the data objects (from the DAL) and returns business objects (to its consumer, in this case the WEB project)

I infer that you are using the "DAL" classes as your actual entities. If you weren't using them as entities, I don't think you would've posted the question as the "DAL" classes would then obviously have no use except to act as the returned type from your DAL repositories - which would preclude the need for your question. Correct me if I'm wrong.

But if DalCustomer is your entity, then what is BllCustomer? There are two options here:

  • Either it's the data object, i.e. what you intend to return to the BLL, which means that the name isn't good. The object's name shouldn't account for a specific consumer, since any given layer may have several consumers.
  • Or it's the business object, i.e. what you intend to return from the BLL to the WEB project (from my example). In this case, you are actually missing a data object type which is returned from the DAL to the BLL.

Returning entities

From a comment you posted:

In my case, DalCustomer is mirror of the tables in DB. [..] I'm thinking to change DAL to return DalObjects to the BLL.

You're asking whether your DAL should return your entity classes.

The short answer is no. Returning your entities would mean that you fail to encapsulate your layer with its own dto objects, which effectively negates a large portion of the layer's purpose.

From the same comment:

BLL will handle conversion to Business Objs,retrieve and fill in nested obj

Just like how the BLL converts the data it consumes (from other project) into its own object (= the business object); the DAL should be doing the same thing. It should take the database entities and it should convert it into its own object (= the data object).

This whole "convert it into your own DTO class" effectively applies to each and every layer. It is not advised to skip it in a layer. Not that it can't be done, but the consequences are usually heavier than the cost of implementation, so it's better to just do it right the first time.


Dependency inversion

There is one caveat here: when you use dependency inversion, then your DAL does actually return a business object.

I can't delve into dependency inversion fully here (the internet is your friend to learn about that), but the short explanation is that with inverted dependencies, the DAL would consume the business layer, instead of the other way around, and therefore be able to use the business object as a return type.

I infer that your situation is not making use of inverted dependencies, but it seemed relevant to mention in case you read or find other examples where a business object is returned by a DAL.


Naming things based on their layer?

I strongly suggest you move away from naming things after the layer they're from, as I suspect it contributed to getting your wires crossed. Instead, name the class for what it is. It doesn't matter that e.g. the data object and the business object have the same class name, since they're in a different namespace anyway.

namespace MyProject.Data
{
    public class Customer
    {
        // ...
    }
}

namespace MyProject.Business
{
    public class Customer
    {
        // ...
    }
}

Namespaces exists specifically so we don't need to ensure that all our class names are globally unique, as that often becomes a challenge in coming up with either synonyms or superfluous pre/suffixes to keep the names unique.

There are some cases where this means you have to explicitly distinguish between two similarly named classes that are both used in the same file. You can either use their explicit namespaces (which is sufficient if there are only a handful of reference. Or, if it becomes too annoying to do so, you can still opt to locally give them a more distinct name there.

using DataCustomer = MyProject.Data.Customer;
using BusinessCustomer = MyProject.Business.Customer;

public class CustomerMapper
{
    public DataCustomer ToDataObject(BusinessCustomer c)
    {
        // ...
    }

    public BusinessCustomer ToDataObject(DataCustomer c)
    {
        // ...
    }
}

It's better to only use this unique name in files where it matters, instead of prefixing it into the actual class name and then have to deal with the ugly name everywhere.

1
  • Very well explained. Indeed layers should be responsible for any translation necessary, between their own DTO's and the types of their declared API's. "Then you name your class after the data it contains.", couldn't agree more. – Cogent Dec 28 '20 at 20:48
-1

DAL should be independent from BL and BL dependent on DAL. Your UI should only Access data via BL. Its good practice if you return DataTable or DataRow from DAL and then Convert DataTable/DataRow into BL object(s). When your UI needs to access the data it can access from BL. So UI will independent from column name and Database type(SQL Server, Oracle..). This way your UI will totally independent from DAL. Personally I prefer class name like "CustomerBL", Don't use the BL word in the begging of class name.

Below see Sample Code.

//Customer Class
class BllCustomer
{
    public int CustomerId { get; set; }
    public String Name { get; set; }
    public BllAddress Address { get; set; }

    public static BllCustomer GetByCustomerId(int id)
    {
        DataRow dr = DalCustomer.GetByCustomerId(id);
        if (dr == null)
            return null;
        BllCustomer oCust = new BllCustomer();
        oCust.CustomerId = int.Parse(dr["CustomerId"].ToString());
        //Do for other class members and load values

        return oCust;
    }
}


class DalCustomer
{

    public static DataRow GetByCustomerId(int id)
    {
        //Get Data row from Database and return Datarow
        DataRow CustomerRow = GETFROMDATABASE("SELECT * from CUSTOMER");
        return CustomerRow;
    }
}
1
  • 1
    Err... Doesn't that then mean that your BLL needs to have knowledge of the format/structure of your data tables?... – Paul Jul 21 '17 at 9:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.