我正在使用cqrs和ddd来构建我的应用程序。
我有一个帐户实体,一个交易实体和一个transactionLine实体。事务包含多个transactionLines。每个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)
收到AddTransactionLine命令
检查新transactionLine的交易中是否存在具有相同帐户的transactionLine
如果是,请发出transactionAmountChangedEvt事件
否则,发出transactionAddedEvt事件
相应的事件处理程序处理正确的事件
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
收到AddTransactionLine命令
调度TransactionLineAdded事件
处理TransactionLineAdded
检查添加的交易的transactionLine是否指向与此帐户中现有的transactionLine相同的帐户
如果是这样,只需将新transactionLine的金额添加到现有的transactionLine
否则,请添加新的transactionLine
答案 0 :(得分:2)
命令和事件都不应包含域逻辑,只有域应包含域逻辑。在您的域中,聚合根表示事务边界(不是您的事务实体,而是逻辑的事务)。处理命令或事件中的逻辑会绕过这些边界并使您的系统变得非常脆弱。
该逻辑的正确位置是交易实体。
所以最好的方法是
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.
答案 1 :(得分:0)
将命令和事件视为'容器' dtos'您需要的数据,以便为您的AggregateRoots提供水分,或者发送给世界(事件)以供其他有界上下文使用它们。就是这样。与您的域严格相关的任何其他操作除了您的AggregateRoots,Entities和Value Objects之外没有任何地方。
您可以添加一些'验证'通过使用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!");
}
}
}