DDD:如何使用中介者模式来推送聚合

问题描述 投票:0回答:1

我不太明白这个[实现领域驱动设计Vaughn,Vernon]实践背后的原因:

使用调解器发布聚合内部状态

解决模型与其客户端之间的紧密耦合问题。

我不太明白这种做法背后试图避免什么:

  • 不在应用层发布聚合?
domain-driven-design
1个回答
3
投票

我不太明白这种做法背后试图避免什么:

  • 不在应用层发布聚合?

早在 Web 开发的早期,大多数人都实现了这样的基本应用程序:

public class UserController
{
  private readonly MyDbContext context;

  public IActionResult GetAll()
  {
    return Ok(context.Users.ToList());
  }
}

这显然很糟糕,因为您可能会暴露业务层逻辑所需的敏感信息,例如密码。人们已经了解到他们需要将“表示模型”(DTO)与数据库模型分开。

随着 DDD 的发展,人们开始编写带有存储库的领域模型来操作它们。所以他们开始实现这样的应用程序(YMMV):

public class UserRepository
{
  private readonly MyDbContext context;

  public IEnumerable<UserDomain> GetAll()
  {
    return context.Users.ProjectTo<UserDomain>().ToList();
  }
}
public class UserController
{
  private readonly UserRepository userRepository;

  public IActionResult GetAll()
  {
    return Ok(userRepository.GetAll().ProjectTo<UserDto>().ToList());
  }
}

这是也是错误的,因为:

  1. DTO 的映射取决于域模型。领域层的任何变化都会影响表示层映射。但领域层的目的是在应用程序状态发生变化时验证业务规则。当您查询您的州时,您不需要业务验证。
  2. 表示模型是两个映射的结果(持久化到领域+领域到表示)。没有映射库能够跨多个转换转换模型。这意味着您需要将整个域模型加载到内存中,以便进行到表示模型的映射。这是一个非常昂贵的操作,尤其是当您想要对结果进行排序、过滤或分页时。
  3. 领域模型必须包含查询所需的所有数据。但领域层的目的是验证业务规则。它应该只包含验证所需的数据。为什么要在域模型中包含对象 4 或 5 导航属性,其目的是在状态更改时验证业务规则,只是因为一个大型的非状态更改查询需要该信息?

那么如何实现一个高效的应用呢?您有两个选择:

  1. 您可以简单地将 ORM 注入控制器中并使用它来查询数据库。然而,这不是很好,因为您的控制器(在应用程序/表示层中)会因数据库特定代码而变得混乱。

  2. 您可以创建一个中介对象,其接口将在应用程序层,而实现将在基础设施层。它的工作方式就像一个存储库,其接口位于领域层,实现位于基础设施层。它将直接返回从持久性存储映射的 DTO,而不是返回域对象。现在,您可以通过调用域服务进行状态更改操作以及调用中介进行查询来实现控制器:

public class UserController
{
  private readonly UserService userService;
  private readonly UserMediator userMediator;

  public IActionResult GetAll()
  {
    return Ok(userMediator.GetAll());
  }

  public IActionResult Update(int id, UserDto data)
  {
    try
    {
      return Ok(userService.Update(id, data));
    }
    catch (BusinessException ex) when (ex.Error == UserErrors.NotFound)
    {
      return NotFound();
    }
  }
}
public class UserService
{
  private readonly MyDbContext context;

  public IEnumerable<UserDto> Update(int id, UserDto data)
  {
    var entity = context.Users.Find(id) ??
      throw new BusinessException<UserErrors>(UserErrors.NotFound);
    // redacted update of the entity
    // business rules go here
    context.SaveChanges();
  }
}
public class UserMediator
{
  private readonly MyDbContext context;

  public IEnumerable<UserDto> GetAll()
  {
    return context.Users.ProjectTo<UserDto>().ToList();
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.