C# 如何使聚合根更小 发生什么事?

C# 如何使聚合根更小 发生什么事?,c#,domain-driven-design,cqrs,event-sourcing,C#,Domain Driven Design,Cqrs,Event Sourcing,我正在开发内部微服务,它使用一次性密码处理操作确认。我决定使用事件源方法创建好的领域模型 我所拥有的 我有一个特定操作类型的确认会话。用户可以为确认尝试添加ask会话,并指定他希望接收质询的通道(一次性密码)。频道就像短信、电子邮件、推送等 下面是从命令处理程序中摘录的代码 var session = _sessionFactory.CreateSession(operationType, userId); var challengeCreationResult = await _challe

我正在开发内部微服务,它使用一次性密码处理操作确认。我决定使用事件源方法创建好的领域模型

我所拥有的 我有一个特定操作类型的确认会话。用户可以为确认尝试添加ask会话,并指定他希望接收质询的通道(一次性密码)。频道就像短信、电子邮件、推送等

下面是从命令处理程序中摘录的代码

var session = _sessionFactory.CreateSession(operationType, userId);

var challengeCreationResult = await _challengeProvider.CreateAsync(
    recipient, channelType, session, addOnFields);

if (!challengeCreationResult.IsSuccess)
{
    return OperationResult<AttemptCreationResult>.Error(
        challengeCreationResult.ErrorCode, challengeCreationResult.ErrorMessage);
}

session.AddAttempt(challengeCreationResult.Data);
addtrunt
ApplyResponseAsync
都会触发相应的事件,并将操作结果返回给用户

出了什么问题 在我尝试添加需要了解多个确认会话的业务规则之前,我对这个实现感到满意。这些是:

  • 如果用户在一小时内对单个操作类型给出五个错误答案,我们需要将其冻结一小时
  • 只有在上次确认尝试后30秒没有正确答案的情况下,用户才允许请求新的确认尝试
  • 问题:
    如何在保持聚合根/服务/事件处理程序的小型化和集中化的同时实现这些规则?

    我认为
    UserConfirmation
    看起来可以作为一个聚合,但不是
    UserConfirmationHistory
    的想法,因为不建议使用包含大量子级的聚合,这里就是这种情况

    这种情况下,业务规则自然不适合聚合,应该向上移动到域服务,域服务可以对应用程序服务提供的
    UserConfirmation
    实例列表进行操作

    我的意思是,我不喜欢在我的域类中有任何基础设施问题(无论是聚合还是服务),获取
    UserConfirmation
    条目列表将需要查询或存储库实现,这些都是基础设施问题,即使您使用接口对它们进行抽象


    我会怎么做
    • 应用程序服务:保存对
      IUserConfirmationRepository
      的引用,并获取
      UserConfirmation
      条目列表,将它们传递给
    • 域服务:它将接收
      UserConfirmation
      条目列表,并验证跨多个聚合的逻辑
    这样,您的业务规则仍然受到域类的保护

    但要谨慎行事:

    • 这样做,你可能只是打开了一个缺口。如果代码的用户能够创建并持久化
      UserConfirmation
      实体的实例,而无需通过此域服务,那么他们将能够绕过此不变量
    • 那么你应该想办法防止它。可以通过在
      UserConfirmation
      聚合中创建构造函数
      internal
      ,并使其成为创建此聚合的唯一可能路径是通过域服务

    另外,您提到,当您只需要进行简单的插入时,事情会变得更容易,但是:

    • DDD不是每个项目的方法。在使用DDD的项目早期存在很多摩擦,只有当您的领域非常复杂时才有理由这样做。然后你会在这条路上撕毁福利
    • 如果DDD真的是这个项目的正确方法,那么您将从以后这样做中获益
    var session = await _sessionStore.GetAsync(sessionId);
    
    var result = await session.ApplyResponseAsync(response, _responseValidator, actingUserId, operationType);