【问题标题】:In which layer should Specification Pattern objects be "new'ed up"?规范模式对象应该在哪一层“新建”?
【发布时间】:2012-01-04 06:22:35
【问题描述】:

所以,我在这里查看了一些关于规范模式的帖子,但还没有找到答案。

我的问题是,在 n 层架构中,规范究竟应该在哪里“更新”?

  1. 我可以将它们放在我的服务层(又名,应用程序层,它有时被称为......基本上,一个 .aspx 代码隐藏会与之对话的东西),但我觉得这样做,我让业务规则泄露出域。如果以其他方式(服务层除外)访问域对象,则域对象无法执行自己的业务规则。

  2. 我可以通过构造函数注入将规范注入我的模型类。但同样,这感觉是“错误的”。我觉得应该注入模型类的唯一东西是“服务”,如缓存、日志记录、脏标志跟踪等......如果你能避免它,使用 Aspects 而不是乱扔模型的构造函数具有大量服务接口的类。

  3. 我可以通过方法注入(有时称为“双重调度”???)注入规范,并明确地让该方法封装注入的规范以强制执行其业务规则。

  4. 创建一个“域服务”类,该类将通过构造函数注入获取规范,然后让服务层使用域服务来协调域对象。这对我来说似乎没问题,因为规范强制执行的规则仍然在“域”中,并且域服务类的命名非常类似于它正在协调的域对象。这里的事情是我觉得我正在编写很多类和代码,只是为了“正确”实现规范模式。

此外,相关规范需要一个存储库来确定它是否“满意”。

这可能会导致性能问题,尤其是。如果我使用构造函数注入 b/c 消费代码可能会调用一个可能包装规范的属性,而这反过来又会调用数据库。

那么有什么想法/想法/文章链接吗?

新建和使用规范的最佳地点在哪里?

【问题讨论】:

    标签: domain-driven-design specification-pattern n-layer application-layer


    【解决方案1】:

    规范是对业务规则的实施检查。它必须存在于域层句号中。

    很难具体说明如何执行此操作,因为每个代码库都不同,但我认为任何业务逻辑都需要在域层中,而不是其他任何地方。此业务逻辑需要完全可测试,并且与 UI、数据库、外部服务和其他非域依赖项松散耦合。所以我肯定会排除上面的1、2和3。

    4 是一个选项,至少规范将存在于您的域层中。然而,规范的更新实际上再次取决于实施。我们通常使用依赖注入,因此几乎所有对象的更新都是通过 IOC 容器和相应的引导代码执行的(即我们通常流畅地连接应用程序)。但是,我们永远不会直接将业务逻辑直接链接到例如UI 模型类等。我们通常在诸如 UI 和域之类的事物之间有轮廓/边界。我们通常定义领域服务契约,然后可以被 UI 等外部层使用。

    最后,我的回答是假设您正在使用的系统至少在某种程度上是复杂的。如果它是一个非常简单的系统,那么域驱动设计作为一个概念可能太过分了。但是,在我看来,无论代码库如何,都应该尊重一些概念,例如可测试性、可读性、SoC 等。

    【讨论】:

      【解决方案2】:

      简答:

      您主要在服务层中使用规范,所以在那里。

      长答案: 首先,这里有两个问题:

      你的规范应该放在哪里,它们应该在哪里更新?

      就像您的存储库接口一样,您的规范应该存在于域层中,因为它们毕竟是特定于域的。有一个 question on SO 在存储库接口上讨论了这个问题。

      他们应该在哪里更新呢?好吧,我在我的存储库中使用LinqSpecs,并且我的存储库中大多数时候都使用了三种方法:

      public interface ILinqSpecsRepository<T>
      {
          IEnumerable<T> FindAll(Specification<T> specification);
          IEnumerable<T> FindAll<TRelated>(Specification<T> specification, Expression<Func<T, TRelated>> fetchExpression);
          T FindOne(Specification<T> specification);
      }
      

      我的其余查询是在我的服务层中构建的。这可以防止存储库因 GetUserByEmail、GetUserById、GetUserByStatus 等方法而变得臃肿。 在我的服务中,我更新了我的规范并将它们传递给我的存储库的 FindAll 或 FindOne 方法。例如:

      public User GetUserByEmail(string email)
      {
          var withEmail = new UserByEmail(email); // the specification
          return userRepository.FindOne(withEmail);
      }
      

      这里是规范:

      public class UserByEmail : Specification<User>
      {
          private readonly string email;
      
          public UserByEmail(string email)
          {
              this.email = email;
          }
      
          #region Overrides of Specification<User>
      
          public override Expression<Func<User, bool>> IsSatisfiedBy()
          {
              return x => x.Email == email;
          }
      
          #endregion
      }
      

      因此,为了回答您的问题,服务层中的规范是新的(在我的书中)。

      我觉得唯一应该注入模型类的东西 是“服务”

      IMO 你不应该向域实体注入任何东西。

      此外,相关规范需要一个存储库 为了确定它是否“满意”。

      那是code smell。我会在那里查看您的代码。规范绝对不需要存储库。

      【讨论】:

      • 不太确定将存储库注入规范是否是代码异味。检查此 StackOverflow 讨论:link。规范和领域服务之间的相似之处可能非常相似,特别是如果您尝试强制执行/证明的规则恰好依赖于首先通过存储库获取信息。
      • 您将规范传递给存储库,例如通过 FindOne(spec) 或 FindAll(spec)。如果将存储库注入到规范中,则有创建循环引用的危险,这可能会导致问题。
      • 仅供参考,我会在需要时将规范注入到服务中。
      猜你喜欢
      • 2016-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-07
      • 2019-11-02
      • 2011-01-30
      相关资源
      最近更新 更多