Design patterns DDD无状态服务和构造函数注入

Design patterns DDD无状态服务和构造函数注入,design-patterns,dependency-injection,domain-driven-design,domainservices,onion-architecture,Design Patterns,Dependency Injection,Domain Driven Design,Domainservices,Onion Architecture,在域驱动的设计文献中,经常说域服务应该是无状态的 我相信这是因为服务调用应该代表单个工作单元。不应该存在多个服务方法将使用的任何服务状态 我在我的服务体系结构中打破了这个规则,这样我就可以在服务中注入所需的所有相关存储库。例如: public class UserService : IUserService { public IUnitOfWork UnitOfWork { get; set; } public IUserRepository UserRepository {

在域驱动的设计文献中,经常说域服务应该是无状态的

我相信这是因为服务调用应该代表单个工作单元。不应该存在多个服务方法将使用的任何服务状态

我在我的服务体系结构中打破了这个规则,这样我就可以在服务中注入所需的所有相关存储库。例如:

public class UserService : IUserService
{
    public IUnitOfWork UnitOfWork { get; set; }

    public IUserRepository UserRepository { get; set; }

    public ICustomerRepository CustomerRepository { get; set; }

    public UserService(IUnitOfWork unitOfWork, IUserRepository userRepository, ICustomerRepository customerRepository)
    {
        UnitOfWork = unitOfWork;
        UserRepository = userRepository;
        CustomerRepository = customerRepository;
    }

    public User RegisterNewUser(...)
    {
        // Perform relevant domain logic
    }

    // ...
}
为了在
UserService
上使用构造函数注入,我需要有状态(属性),以便服务方法能够访问相关的存储库等

尽管我希望将个人服务方法设计为独立的工作单元,但我不一定能阻止这种情况的发生

我如何构建域服务,使其成为无状态的?这是必要的吗

编辑:

埃里克·埃文斯在:

当域中的重要过程或转换不是 实体或价值对象的自然责任,添加操作 将模型作为独立接口声明为服务。定义 根据模型的语言进行接口,并确保 操作名是通用语言的一部分。提供服务 无状态


Vaughn Vernon在他的书中也推荐无状态服务。

接近目标的一种方法是将IOC容器注入服务类,然后重写属性get方法以解析必要类的实例。您的新类将如下所示:

public class UserService : IUserService
{
  private IUnitOfWork UnitOfWork 
  { 
    get{return container.Resolve<IUnitOfWork>()}
  }
  private IUnityContainer container {get;set;}

  public UserService(IUnityContainer container )
  {
    this.container = container;
  }

  public User RegisterNewUser(User user)
  {
     //Domain logic
  }

}
public类用户服务:IUserService
{
私人工作单位
{ 
获取{return container.Resolve()}
}
私有IUnityContainer容器{get;set;}
公共用户服务(IUnityContainer容器)
{
this.container=容器;
}
公共用户注册服务器(用户用户)
{
//域逻辑
}
}

您的服务类现在依赖于IOC容器,这不是一件好事,但如果您试图接近无状态服务,这就可以了。

在我看来,您似乎混淆了拥有属性和拥有状态

UserService
是一种服务。它只有只读(请删除setters)属性,这些属性是无状态服务,例如
IUserRepository
。这使得
UserService
本身成为无状态服务

是的,我们甚至可以在上面添加一个更高级别的服务,它有一个
IUserService
作为其组件之一。该服务也将是无状态的

为什么,你问

无国籍状态的目的(或至少是目的)是允许控制权倒置:我们放弃对我们到达外部的实例的控制。如果其他人正在控制我们得到的实例,那么这些实例最好是无状态的!如果我们得到的实例与另一个类得到的实例相同,并且我们都开始改变它的状态,该怎么办?结果将是不确定的


诚然,如果我们的服务根本没有属性(比如
UserRepository
),这个问题就可以避免。还请注意,当更高级别的服务(例如
UserService
)独占地具有此类服务的只读属性时,也同样可以避免这种情况,因为不需要分配任何内容。我们可以放心,注入的服务将始终正确运行,因为它的层次结构中没有可依赖的状态。

如果您希望服务成为无状态,应该通过方法参数传入依赖项,但这将是非常可怕的,并将这些依赖关系从实现细节提升到合同的一部分。是的,那将不有趣。这也意味着我必须在应用层和/或MVC控制器中注入相关的存储库。@Steven:检查我的编辑,谢谢。我相信Evens的意思与你想象的不同。我认为他比较了服务的状态和域对象的状态。域对象在其生存期内显然具有状态并更改其状态。服务不会——或者不应该——改变状态,我认为这就是他所说的“无状态”的意思。我认为不可能使服务完全无状态,因为它总是在某种状态上下文中运行;例如,UnitOfWork或DB事务。无论您是注入依赖项还是使用服务位置,服务都将始终受此影响。@Steven:谢谢您的澄清。谢谢您的回答。这似乎是最好的选择,但我绝对不想在我的域层中创建对IOC容器的依赖关系。甚至有必要拥有无状态服务吗?看看您的代码,我假设您正在创建一个web应用程序数据访问层,因此主要关注的是处理多线程。如果您将应用程序配置为每个用户都可以获得自己的IUserService实例,那么这并不重要。如果用户共享同一个服务,那么您可能会遇到并发问题(例如,当另一个线程在调用过程中处于中间位置时,会调用IUnitofWork.Save()。在这种情况下,我创建的解决方案可以帮助用户共享同一个IUserService实例,但可以通过IOC容器获得自己的DBContext。这是一个严重的坏主意。这就是。还要注意,这根本不能解决状态问题,因为从状态的角度来看,通过构造函数注入依赖项和从方法内部的容器解析依赖项之间没有什么区别。您的大多数服务都是无状态的,但在运行时取决于将保持所需状态的工作单元。使用相同的UoW,状态量保持不变。