【问题标题】:Domain logic in command handler or event handler?命令处理程序或事件处理程序中的域逻辑?
【发布时间】:2015-10-27 16:58:56
【问题描述】:

我正在使用 cqrs 和 ddd 来构建我的应用程序。

我有一个 account 实体、一个 transaction 实体和一个 transactionLine 实体。一个事务包含多个事务行。每个 transactionLine 都有一个金额并指向一个帐户。

如果用户在已经有一个 transactionLine 指向与新 transactionLine 相同的帐户的交易中添加一个 transactionLine,我想简单地将新的 transactionLine 金额添加到现有的一个,防止交易有两个指向同一个帐户的 transactionLines。

例如:

Before command :
    transaction
        transactionLine1(amount=100, account=2)
        transactionLine2(amount=50, account=1)

Command :
    addNewTransaction(amount=25, account=1)

Desired result :
    transaction
        transactionLine1(amount=100, account=2)
        transactionLine2(amount=75, account=1) // Add amount (50+25) instead of two different transactionLines

instead of

transaction
    transactionLine1(amount=100, account=2)
    transactionLine2(amount=50, account=1)
    transactionLine3(amount=25, account=1) // Error, two different transactionLines point to the same account

但我想知道最好在命令或事件处理程序中处理这个。

如果这种情况由命令处理程序处理

Before command :
    transaction
        transactionLine1(amount=100, account=2)
        transactionLine2(amount=50, account=1)

Command :
    addNewTransaction(amount=25, account=1)  // Detects the case

Dispatches event
    transactionLineAmountChanged(transactionLine=2, amount=75)
  1. 收到AddTransactionLine命令

  2. 检查新transactionLine与同一账户的交易中是否存在transactionLine

  3. 如果是,则发出 transactionAmountChangedEvt 事件

  4. 否则,发出一个 transactionAddedEvt 事件

  5. 相应的事件处理程序处理正确的事件

如果这种情况由事件处理程序处理

Before command :
    transaction
        transactionLine1(amount=100, account=2)
        transactionLine2(amount=50, account=1)

Command :
    addNewTransaction(amount=25, account=1)

Dispatches event
    transactionLineAdded(transactionLine=3, amount=25)

Handler  // Detects the case
    transactionLine2.amount = 75 
  1. 收到AddTransactionLine命令

  2. TransactionLineAdded 事件被调度

  3. TransactionLineAdded 被处理

  4. 检查添加的交易的transactionLine是否与该账户中现有的transactionLine指向同一个账户

  5. 如果是这样,只需将新transactionLine的金额添加到现有transactionLine

  6. 否则,添加新的 transactionLine

【问题讨论】:

    标签: domain-driven-design cqrs


    【解决方案1】:

    命令和事件都不应该包含域逻辑,只有域应该包含域逻辑。在您的域中,聚合根代表事务边界(不是您的事务实体,而是逻辑的事务)。处理命令或事件中的逻辑会绕过这些边界并使您的系统非常脆弱。

    该逻辑的正确位置是事务实体。

    所以最好的方法是

    AddTransactionCommand finds the correct transaction entity and calls
    Transaction.AddLine(...), which does the logic and publishes events of what happened
    TransactionLineAddedEvent or TransactionLineChangedEvent depending on what happened.
    

    【讨论】:

      【解决方案2】:

      将命令和事件视为“容器”、“dtos”数据,您将需要这些数据来补充您的 AggregateRoot 或发送到世界(事件)以供其他限界上下文使用它们。而已。除了您的 AggregateRoots、实体和值对象之外,任何其他与您的域严格相关的操作都没有位置。

      您可以通过使用 DataAnnotations 或您自己的 validate 方法实现来为您的命令添加一些“验证”。

      public interface ICommand
      {
          void Validate();
      }
      
      public class ChangeCustomerName : ICommand
      {
          public string Name {get;set;}
      
          public void Validate()
          {
              if(Name == "No one")
              {
                  throw new InvalidOperationException("Sorry Aria Stark... we need a name here!");
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-06-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-25
        • 1970-01-01
        相关资源
        最近更新 更多