Commandhandler multiple methods calls on a single aggregate

116 views
Skip to first unread message

Kees Schouten

unread,
Feb 7, 2018, 12:53:53 PM2/7/18
to DDD/CQRS
Is it ok to call multiple methods on a retrieved aggregate for a single command? For example:

We have a aggregate Expense and a SubmitExpensecommand. This command contains all the information of the users expense.

We don't have draft/concept expenses so it should be one command. This commands is quite big because it contains every detail, including line items.
I seems to makes sense to handle this command in the handler like this:

var expense = new Expense(command.amount, command.description, etc); //emits expenseCreatedEvent?
 
foreach(line in command.Lines)
{
 expense.addLineItem(new LineItem(line.amount, line.tax,etc.)); //emits expenseLineItemAddedEvent?
}  
 
expense.submit(); //emits event. expensesubmittedevent?

 

Mauro Servienti

unread,
Feb 7, 2018, 1:09:10 PM2/7/18
to ddd...@googlegroups.com
I would do:

var expense = new ...
var expenseLines = new List<Lines>();

foreach ( var line in command.Lines )
{
//convert to Line
expenseLines.Add( /* converted expense from command */ );
}
expense.Submit( expenseLines );

Or something like the above. So that there is one single interaction with the aggregate, in the end there is no point in submitting an expense with no lines.
--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+u...@googlegroups.com.
Visit this group at https://groups.google.com/group/dddcqrs.
For more options, visit https://groups.google.com/d/optout.
--
Mauro Servienti

Rupesh Tiwari

unread,
Feb 7, 2018, 4:37:30 PM2/7/18
to ddd...@googlegroups.com
I agree with Mauro too !

I would also move the factory function inside the aggregate in order to enforce the invariant by aggregate itself.  


var expense = repo.get(id);
expense.CreateLineItems(command.LineItems)
       .Submit();
      
repo.save(expense);


public class Expense {
  private int CustomerDiscount {get;set;}
  private int PromoDiscount {get;set;}
  
  public void CreateLineItems(List<ExpenseItem> items) {
    items.forEach(i=>CreateLineItem);
    return this;
  }
  
  public void Submit() {
    
    // put submit logic 
    //Event.Add(ExpeseSubmitted);
  }
  
  /*
  This arrangement makes it practical to enforce all invariants for objects
  in the AGGREGATE and for the AGGREGATE as a whole in any state change. 
  */
public void CreateLineItem(ExpenseItem item)
  {
    // aggregate can inforce invariants here 
    // asertionConcerns.AssertNull(item,'line item can not be null') //etc. 
    
    var item = new LineItem
    {
      Quantity = item.Quantity,
      ExpnseLineItemId = item.Id,
      UnitPrice = item.amount,
      UnitPriceDiscount = CustomerDiscount + PromoDiscount // give the aggregate opportunity to add current state  values
    };
    
    /* Here you can also add events into your event collection which can be published once expese is persisted in db later.
     Events.Add(ExpenseLineItemCreatedEvent)
     
    */
    
    LineItems.Add(item);
  }
}

mynkow

unread,
Feb 8, 2018, 2:39:50 PM2/8/18
to DDD/CQRS
In general it is safe to ivoke as many AR methods as you like if it make sense from business perspective. It is ok to create other ARs as well. The rule to update only one AR per command exists to minimise the pain when you try to update 2 and the persistence for the 2nd fails.

Mauro Servienti

unread,
Feb 8, 2018, 11:56:45 PM2/8/18
to ddd...@googlegroups.com
I tend to disagree with:

On Thu, 8 Feb 2018 at 20:39, mynkow <myn...@gmail.com> wrote:
In general it is safe to ivoke as many AR methods as you like if it make sense from business perspective. It is ok to create other ARs as well.

It tends to create unnecessary complexity that generally requires documentation to explain which methods needs to be called on an aggregate given an incoming command so to keep the aggregate in a consistent state. De fact violating the rule that an Aggregate is designed to guarantee consistency.

If given CommandA, in order to apply the requested change, MethodA, MethodB and MethodC must be called, in which way developers know:

- all the 3 methods must be called
- if the order in which the must be called is important 
- what happens if the skip one or more

.m
--
Mauro Servienti
Reply all
Reply to author
Forward
0 new messages