1

我有一个分层的解决方案如下:

  • UI(用户界面)
  • BLL(业务逻辑层)
  • DAL(数据访问层)
  • SharedEntities(仅具有实体 POCO 的 VS 项目)

我希望 BLL 有一个名为 GetProductList() 的服务,该服务在我的 DAL 层中实现。我想过在 BLL 和 DAL 实现中定义一个接口,如下所示:

选项 A:

// Interface defined in BLL 
public interface IDataServices
{
  List<Product> GetProductList();
}

// Interface implemented in DAL
public class DataServices : IDataServices
{
    Public List<Product> GetProductList()
    {
      return //do some database work here and return List<Product>;
    }
}

如果我想在 DAL 中实现这一点,那么我必须让 DAL 项目引用 BLL 项目才能看到 IDataServices 的接口定义。或者,我可以在 DAL 中复制接口定义,但最终我会使用重复的代码来维护(BLL 和 DAL 中的接口定义相同)。

选项 B:我可以做到这一点的另一种方法是忘记接口的想法,只需在 UI 可以使用的 BLL 中进行以下具体的类和方法调用:

// Concrete class defined in the BLL
public class DataServices
{
    Public List<Product> GetProductList()
    {
         DAL aDAL = new DAL();
         Return (aDAL.GetProductList());
    }
}

这很容易,但是 BLL 看到了 DAL 并引用了它,但这真的是一件坏事吗?只要 BLL 不使用任何数据库对象(即数据源、连接字符串等)来满足请求并且 DAL 符合匹配我在 BLL DataServices 类中定义的服务名称,这还不够吗?我听到的所有关于交换另一个数据库引擎的讨论仍然可以通过确保下一个 DAL 提供 BLL 在 DataServices 类中标识的相同服务来完成,例如 GetProductList()。在这个设置中,UI 仍然不知道任何关于 DAL 的信息,而 DAL 也不知道任何关于 BLL 的信息。如果我考虑使用依赖注入来避免在 BLL 中实例化 DAL 的想法,那将意味着在 UI 中实例化它以传递给 BLL。

选项 C:我简要查看了 Unity Container,但该工具建议在入口点预先注册所有接口和具体类,这本来是 UI,这反过来又使 UI 对 BLL 和 DAL 可见似乎更糟。我看到了使用 MEF 和 Unity 来解决入口点看到所有层的问题的参考,但也看到如果你这样做,你就不能真正对这样的配置进行单元测试。与选项 B 相比,似乎需要大量工作和复杂性。

选项 D:我想到的另一个选项是在 BLL 和 DAL 之间创建一个外观层(另一个 VS 项目)。这似乎没有多大意义,除非我最终得到了很多与 BLL 无关的 DAL 方法,因此它们必须被隐藏;允许 DAL Facade 仅显示 BLL 需要的内容。如果我要换入另一个数据库,我仍然必须根据 BLL 的需要创建外观所需的方法,正如我在选项 B 中提到的那样。

因此,基于这一切,我正在考虑选择选项 B,但我想在这里获得一些社区意见。我还能做什么满足以下条件:

  • 1) 不让 UI 看到 DAL
  • 2) 不让 DAL 看到 BLL
  • 3) 该解决方案仍然允许对所有层进行单元测试
4

2 回答 2

2

您的选项 A(BLL 中的接口,DAL 中的实现)+ 和 IoC 容器是最好的方法。

在设计复杂的软件解决方案时,您必须将其分成更小的部分。

其中一些部分对解决方案至关重要。它们将是您开发软件的原因,而不仅仅是购买现有解决方案。你必须专注于他们。这些部分将很复杂且难以实施。你对此无能为力。它们必须尽可能地实施。

也会有简单的部分。易于实现的功能,甚至可以购买。尝试尽可能少地制作这些零件。它们是必要的,但它们不是你被雇用的目的。

首先关注困难的部分。那里的业务逻辑会很复杂。业务逻辑将不依赖于您选择的存储解决方案 - 所以不要让它依赖于您系统中的 DAL 项目。在业务逻辑层中为实体的存储库定义接口(如果遵循 DDD,则为聚合)。

您应该能够彻底测试业务逻辑。如果 BLL 依赖于 DAL,则不能这样做。

UI 应该与业务逻辑完全分离。UI 只是用户可以执行的一组用例。因此,它应该向用户呈现有限的信息,并且只接受来自用户的有限信息。

要将 UI 与 BLL 分开,请使用单独的 ViewModel 和 Command 类来显示和接受来自用户的信息。在每个用例中使用额外的应用层来编排 BLL。

这种方法被称为六边形架构洋葱架构清洁架构。它也在领域驱动设计书籍中被引用。

当然,您需要一个将所有这些依赖项放在一起的地方。这个地方是一个组合根,它应该尽可能靠近应用程序入口点。如果您不想引用 UI 项目中的所有图层,请将合成根移动到另一个项目。

于 2015-11-28T00:45:14.870 回答
1

IDataServices应在 DAL 中定义。but then the BLL sees the DAL and has a reference to it这是自然的方式。一个项目可以引用它下面的层。如果您不允许向下引用,则根本就没有引用。

请注意,反射参考仍然是参考,因为您无法在不更改上面的图层的情况下更改下面的图层。依赖项不是仅编译时的概念。选项 B 没有任何好处。它删除了编译时依赖,但不删除运行时依赖。

删除从 A 到 B 的依赖关系的要点是,您可以在不更改 A 的情况下更改 B。这就是重点。运行时依赖仍然很重要。

关于 C:您可以让 UI 要求 BLL 注册其依赖项。然后 BLL 可以要求 DAL 注册。这样,UI 就不会受到 DAL 的影响。

D:我不知道这会带来什么。

通过定义 DAL 可以轻松满足您的约束IDataServices

于 2015-11-27T23:58:25.283 回答