3-layer architecture - passing data between layers

mj82 picture mj82 · Jun 29, 2012 · Viewed 9.8k times · Source

Trying to implement 3-layer (not: tier, I just want to separate my project logically, on one machine) architecture I've found so many different approaches that I'm confused, what's the best way (if there's any) to make that in WinForms app.

Now I have no doubts only about 3 layers that should be present in the project:

  • UI (Presentation Layer)
  • BLL (Business Logic Layer)
  • DAL (Data Acces Layer)

In UI I put all the WinForms. There must be also some logic to fill the object with data from controls and pass it to BLL layer.

In DAL I want to put classes and methods for data manipulations using ADO.NET, like:

public class OrderDAL
{
    public OrderDAL()
    {
    }

    public int Add(Order order)
    {
        //...add order to database
    }

    public int Update(Order order)
    {
        //...update order in database
    }

    //...etc.
}

The problem is with BLL and the question - should I use Data Transfer Objects to pass data between layers, or should I pass the whole Class?

If I choose to use DTO, then I've to create additional common class, Order, that reference to UI, BLL and DAL:

public class Order
{
    public int Id { get; set; }
    public DateTime Date { get; set; }
    public string Number { get; set; }
    public string CustomerName { get; set; }

    public Order ()
    {
    }
}

and put the logic separated into BLL:

public class OrderBLL
{
    public OrderBLL()
    {
    }

    public int Add(Order order)
    {
        OrderDAL orderDAL = new OrderDAL();
        return orderDAL.Add(order);
    }

    public int Update(Order order)
    {
        OrderDAL orderDAL = new OrderDAL();
        return orderDAL.Update(order);
    }

    //...etc.
}

This approach, under different names, is used among others: here or here.
On the other hand, some "wise guys" and their followers (like here) call it Anemic Domain Model and complain it's a bad design and anti-pattern that should not be used.

The pros:

  • DTO can easily by design to represent Database table,
  • it's light and clear, contains only fields needed for database,
  • DAL doesn't have to reference BLL,

The cons:

  • anti-pattern (sounds scary ;P),
  • violation of OOP (separated properties from methods),
  • because logic is in different class, it may be more difficult to maintain when something changes.

So, the opposite approach is to pass the whole object between layers, like here: no DTO, just BLL looking like that:

public class Order
{
    public int Id { get; set; }
    public DateTime Date { get; set; }
    public string Number { get; set; }
    public string CustomerName { get; set; }

    public Order()
    {
    }

    public int Add()
    {
        OrderDAL orderDAL = new OrderDAL();
        return orderDAL.Add(this);
    }

    public int Update(Order order)
    {
        OrderDAL orderDAL = new OrderDAL();
        return orderDAL.Update(order);
    }
}

The pros:

  • it's a nicely encapsulated object, following OOP rules (I suppose ;)).
  • both logic and properties are in one place, easier to maintain and debug.

The cons:

  • to use the object, DAL has to reference BLL (that's not how the 3-tier layer should do, isn't it?).
  • class may contain some fields that are not used in Database, as well as some fields from Database (like Id) do not represent "real life" object.

So, it looks like whatever I choose, I'll violate some rules. What's better way then, which should I choose? Maybe there is other approach I haven't found?

Answer

duffymo picture duffymo · Jun 29, 2012

I don't like DTOs, because they mean creating a dual hierarchy with little or no value.

I also don't like the idea of making model objects responsible for their own persistence. I prefer a separate persistence layer. Why? Model objects don't always need to be persisted to be useful. Business logic and functionality are orthogonal to persistence.

If you have two layers it's possible to keep a one way dependency graph: persistence knows about model, but model does not know about persistence. You end up with a cyclic dependency if model objects are responsible for persistence. You can never test or use model objects without persistence.

My advice? Don't do DTOs. Break out a separate persistence layer.