除了实例化DAL的BLL之外,什么选项允许在n层解决方案中进行单元测试而不将DAL暴露给UI或BLL到DAL?

时间:2015-11-27 23:51:00

标签: c# data-access-layer n-tier-architecture bll

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

  • 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: 另一种方法是忘记界面的想法,只需在BLL中进行以下具体的类和方法调用,UI可以使用:

// 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中。我不想这样做会让UI访问DAL方法。

选项C: 我简单地看了一下Unity容器,但是该工具建议在入口点预先注册所有接口和具体类,这可能是UI,这反过来最终使得BLL和DAL的UI可见性看起来更糟糕。我看到一个使用MEF和Unity的参考来解决看到所有层的入口点的问题,但也看到如果你这样做,你不能真正单元测试这样的配置。与选项B相比,似乎有很多工作和复杂性。

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

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

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

2 个答案:

答案 0 :(得分:2)

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

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

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

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

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

您应该能够彻底地测试业务逻辑。如果BLL依赖于DAL,则无法执行此操作。

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

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

这种方法众所周知为Hexagonal architectureOnion architectureClean architecture。它也在域驱动设计书籍中引用。

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

答案 1 :(得分:1)

应在DAL中定义

IDataServicesbut then the BLL sees the DAL and has a reference to it这是自然而然的方式。项目可以引用它下面的图层。如果您不允许向下引用,则根本没有引用。

请注意,Reflection引用仍然是一个引用,因为您无法在不更改上面的图层的情况下更改下面的图层。依赖关系不是仅编译时的概念。选项B不会增加任何好处。它删除了编译时依赖项,但没有删除运行时依赖项。

从A到B删除依赖关系的一点是你可以在不改变A的情况下改变B.这就是整点。运行时依赖性仍然很重要。

关于C:您可以让UI要求BLL注册其依赖项。然后BLL可以要求DAL注册。这样,UI就可以屏蔽DAL。

D:我不知道这会做什么。

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