3层架构 - 在层之间传递数据

6
尝试实现三层架构(不是:层,我只想在一台机器上逻辑分离我的项目)时,我发现有很多不同的方法,让我感到困惑,如果有的话,最好的方法是什么,可以在WinForms应用程序中实现。现在我只对项目中应该存在的3个层没有疑问:
UI(表示层)
BLL(业务逻辑层)
DAL(数据访问层)
在UI中,我放置了所有的WinForms。还必须有一些逻辑来使用控件中的数据填充对象并将其传递到BLL层。
在DAL中,我想放置使用ADO.NET进行数据操作的类和方法,例如:
public class OrderDAL
{
    public OrderDAL()
    {
    }

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

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

    //...etc.
}

问题出在 BLL 上,关键是:我应该使用 数据传输对象(DTO) 在层之间传递数据,还是应该传递整个类?
如果我选择使用 DTO,那么我需要创建一个额外的公共类 Order,它会被 UI、BLL 和 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 ()
    {
    }
}

并将逻辑分离到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.
}

这种方法在不同的名称下被使用,例如:herehere
另一方面,一些“聪明人”及其追随者(如here)称其为贫血领域模型,并抱怨它是一个糟糕的设计和反模式,不应该被使用。 优点:
  • DTO可以轻松地被设计成代表数据库表,
  • 它很轻量级和清晰,只包含数据库所需的字段,
  • DAL不必引用BLL,
缺点:
  • 反模式(听起来很可怕;P),
  • 违反面向对象编程原则(将属性与方法分开),
  • 因为逻辑在不同的类中,当某些内容改变时,可能更难维护。

因此,相反的做法是在层之间传递整个对象,就像这里一样:没有DTO,只有以下BLL:

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);
    }
}

优点:

  • 这是一个很好的封装对象,遵循面向对象编程规则(我想是这样的吧;)).
  • 逻辑和属性都在同一位置,更易于维护和调试。

缺点:

  • 要使用对象,数据访问层必须引用业务逻辑层(这不应该是三层架构的方式,不是吗?)。
  • 类可能包含一些在数据库中未使用的字段,也可能包含一些来自数据库的字段(如Id)不代表“真实”对象。

所以,看起来无论我选择什么,都会违反某些规则。那么,有没有更好的方法,我应该选择哪种?也许还有其他方法我还没发现?


我建议您查看http://en.wikipedia.org/wiki/Domain-driven_design。 - HatSoft
1个回答

8
我不喜欢DTOs,因为它们意味着创建一个几乎没有价值的双重层次结构。
我也不喜欢让模型对象负责自己的持久化。我更喜欢一个单独的持久化层。为什么?模型对象并不总是需要被持久化以便有用。业务逻辑和功能与持久性无关。
如果你有两个层次,就可以保持一个单向依赖图:持久性知道模型,但模型不知道持久性。如果模型对象负责持久性,你会得到一个循环依赖。你永远不能测试或使用模型对象而不需要持久性。
我的建议?不要使用DTOs。拆分一个单独的持久化层。

那么我不明白 - 你更喜欢 OP 显示的第一种还是第二种选项? - alex
十年前,我推荐使用单独的数据访问层来返回模型对象,而不是 DTO。我不明白为什么你现在才问这个问题。 - duffymo
但我认为OP的问题是关于应用程序内部层之间的,而不是服务之间的(这里涉及到JSON序列化)。在这种情况下,您建议不要使用DTO对象,而是传递模型对象(具有数据和行为)。但这与实体对象是分开的,您可能会将其存储在数据库中? - alex
不同意。我在谈论应用程序中的层。如果您的React UI调用我的REST服务获取数据,我会将其反序列化为JSON对象并传递给您。这就是现在的做法。J2EE标准和DTO设计模式都假定从前端到后端都是Java。UI是JSP(编译为生成HTML的servlet),而后端是servlet和EJB。 - duffymo
你错过了一个关键点:POJO和EJB之间的区别。如果这对你来说是一个新概念,请去做一些研究。 - duffymo
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接