8

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

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

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

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

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

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

除此之外,有问题的规范需要一个存储库才能确定它是否“满意”。

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

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

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

4

2 回答 2

8

简短的回答:

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

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

您的规格应该放在哪里,它们应该在哪里更新?

就像您的存储库接口一样,您的规范应该存在于域层中,因为它们毕竟是特定于域的。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 你不应该向域实体注入任何东西。

除此之外,有问题的规范需要一个存储库才能确定它是否“满意”。

那是代码气味。我会在那里查看您的代码。规范绝对不应该需要存储库。

于 2011-11-23T15:09:29.677 回答
8

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

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

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

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

于 2011-11-23T11:00:06.897 回答