【问题标题】:Repository pattern and/or/vs business logic layer存储库模式和/或/vs 业务逻辑层
【发布时间】:2011-03-29 21:23:12
【问题描述】:

我有一个问题想听听你的意见。

我正在尝试使用存储库模式。我有一个将数据加载到 POCO 的存储库对象。我还创建了一个业务逻辑层,它添加了一些功能,但基本上包装了 POCO。所以最后我有一个 BLL,它使用存储库加载 DAO。

我对这个解决方案不太满意。我有三层,但我觉得 BLL 没有提供足够的功能来保持它。另一方面,我不想将我的逻辑放在存储库层或数据访问层?

所以我的问题是我应该把应用程序的逻辑放在哪里?您使用哪种解决方案(DAO + repo 或 DAO + BLL + rep 或任何其他)?

【问题讨论】:

    标签: c# design-patterns


    【解决方案1】:

    在设计您的域时,有两种基本的方式来考虑业务规则。

    1.) 域实体是基本 POCO/DTOs。然后你将它们交给域服务。这些服务可以像另一个类一样简单,或者它们真的可以是位于另一台服务器上的实际服务。

    var user = repository.Find(x => x.UserName == userName);
    if (userLogonService.IsValidUser(user, password)) {
       userLogonService.UpdateUserAsLoggedOn(user);
    }
    repository.SaveChanges();
    

    2.) 领域实体包含自己的操作逻辑。这更接近于许多 MVC 模式将遵循的。 既然你问了,这是我更喜欢的模型。

    var user = repository.Find(x => x.UserName == userName);
    if (user.CheckPassword(password)) {
        user.LogOnNow();
    }
    repository.SaveChanges();
    

    两者都是完全有效的模式。 #1 有一个独立的业务运营层,但受到Anemic Domain Model 的影响。如果您的域开始变得复杂,或者如果模型可以做很多事情,#2 可能会导致大型域实体。

    编辑 #1:对 John Kraft 的回应

    Oven.Bake(myPizza) 与 myPizza.Bake()

    我基本同意。您是否只有一个烤箱服务,或者您是否有数十个可用烤箱存储在烤箱存储库中,烤箱只是另一个域实体?在#2 中,烤箱是域的一部分。我倾向于进行领域建模的方式,大多数名词都是领域实体,除非你 100% 确定确实存在其中之一。

    但是披萨在烘烤时确实会发生一些事情。

    interface ICanBeBaked {
        int BakeMinutes { get; }
        int BakeTemp { get; }
        void Bake();
    }
    class Pizza : ICanBeBaked {
        int BakeMinutes { get { return 15; } }
        int BakeTemp { get { return 425; } }
        void Bake() {
            // melt cheese!
            this.isBaked = true;
        }
    }
    class Oven {
        void Bake(ICanBeBaked thingToBake) {
            // set the temp, reserve this oven for the duration, etc.
            thingToBake.Bake();
        }
    }
    

    【讨论】:

    • #2 是我想避免的,但我觉得将大量逻辑放入 BLL 并使用业务对象包装 POCO/DAO 并不是最好的解决方案。我不知道为什么我没有考虑服务层。我想在领域变大的情况下,没有简单而好的方法来分离所有逻辑,所以这必须在设计阶段考虑。
    • 我更喜欢选项 #1 到 #2。我将这些等同于一个例子:#1 => Oven.Bake(myPizza) #2 => myPizza.Bake() #2 我觉得不对,因为披萨在现实世界中什么都做不了。
    • 我看到的方法 #2 的一个缺点是,现在域对象不再只是一个值对象 (POCO),它还包含逻辑。因此,跨服务边界移动变得困难,这意味着创建单独的 DTO。就我个人而言,我更喜欢通过域服务将域对象的逻辑外部化。这样就可以独立于持久性(SRP 原则)来测试逻辑。
    • 返回并阅读 Evans 的领域驱动设计。它非常面向名词/动词。你的实体应该做事。 DTO 和实体之间存在差异。 DTO!= POCO。 POCO 意味着它不依赖于框架的任何部分(不调用数据库、服务器、队列、文件等。逻辑是自封装的。)在 DDD 中,值​​对象不是实体。在 DDD 中,您的实体具有逻辑。
    • 在 #1 中,我们应该从 UI 中引用 Repository 和 Services 层。这是正确的吗?然后它可能会使开发人员混淆他们应该在哪里使用 Repository 或 Service 方法。关于他们应该将查询放在哪里(在服务或存储库中?)也存在同样的问题。请在这方面帮助我。
    【解决方案2】:

    我的“DAL”(更多的是一个本土的 ORM,这是另一个主题)本身实际上是几层;一种提供存储库和一些活动记录模式支持的抽象,下面是实际的数据访问代码。

    此时我们有一个最小的业务层,但真正的原因是它很薄,因为网页代码隐藏中嵌入了太多(遗留)业务逻辑。随着它的重构,我预计业务层会不断发展壮大。

    这是相当标准的分层。你不要说为什么你对当前的堆栈不满意,但请记住,这样做的核心原因是职责分离。您可能还想看看领域驱动设计的概念;它为围绕业务政策和实践组织代码提供了很多思考,而不是具体的软件问题。这是您工具箱中非常有用的分析工具。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-05
      • 2014-01-10
      • 1970-01-01
      • 2014-09-04
      • 2011-01-26
      相关资源
      最近更新 更多