Reference between Expense(s) and Expense Report

215 views
Skip to first unread message

Kees Schouten

unread,
Jul 12, 2017, 3:55:53 PM7/12/17
to DDD/CQRS
From a use perspective I want to be able to add multiple Expenses to a Expense Report.
I understand that u should reference aggregates by Id.

While adding an Expense to an Expense report, I should be able to validate if the Expense is not already added to an other Expense Report. 
Should I create a reference on both sides?

Expense
{
  public ExpenseReportId ExpenseReportId {get;set;}
}

ExpenseReport
{
  public IEnumerable<ExpenseId> ExpenseIds {get;set;}
}

On the other hand, I'm thinking that the Expense should not know the concept of Reports, does it?


João Bragança

unread,
Jul 13, 2017, 7:15:36 AM7/13/17
to ddd...@googlegroups.com
Treat the Expense like an entity of the ExpenseReport aggregate.

--
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+unsubscribe@googlegroups.com.
Visit this group at https://groups.google.com/group/dddcqrs.
For more options, visit https://groups.google.com/d/optout.



--

Kees Schouten

unread,
Jul 14, 2017, 2:25:59 AM7/14/17
to DDD/CQRS, joao...@braganca.name
Ok I agree with that. 

But when a user adds an expense to a new expense report, how do I validate if the expense is not already added to an different report. 
Should I set an boolean property like "IsInReport": true on the expense model? Or are there better options?


Bill Davis

unread,
Jul 14, 2017, 10:28:14 AM7/14/17
to DDD/CQRS, joao...@braganca.name
It seems to me like you would have a "Duplicate Expense Check" view that contains the data you need to check for duplicate expenses. How do you know if an expense is a duplicate? The Date/Time, Vendor/Place and Amount? You would put those values and whatever references you need in that view (like the AggregateId of the ExpenseReport that contains the existing expenses)

Greg Young

unread,
Jul 14, 2017, 10:35:29 AM7/14/17
to ddd...@googlegroups.com
An entity can only belong in a single aggregate (by definition its id need only be unique within the aggregate boundary). As such you would be looking to see if a similar expense existed in another aggregate which would be a query.

--
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+unsubscribe@googlegroups.com.
Visit this group at https://groups.google.com/group/dddcqrs.
For more options, visit https://groups.google.com/d/optout.



--
Studying for the Turing test

Rupesh Kumar Tiwari

unread,
Jul 14, 2017, 12:18:28 PM7/14/17
to ddd...@googlegroups.com
I would keep Expense agnostic of ReportId. However, in order to maintain business rule I will create a Query to check if expense already included in some report then throw exception else Include in Report. 

AddExpenseToReport(expenseId, reportId) {
  var  expense = AllReportedExpenses.get(expenseId)
  if(expense != null) throw InvalidOperationException('Expense already included in report');
  var report = reportRepository.get(reportId);
  report.include(reportId);
}



Thanks,
Rupesh kumar Tiwari
 

Poule Dodue

unread,
Jul 14, 2017, 3:21:34 PM7/14/17
to DDD/CQRS
I would make Expense consumable or create a ConsumableExpense with its own lifecycle

Kees Schouten

unread,
Jul 15, 2017, 3:14:12 AM7/15/17
to DDD/CQRS
A query is certainly an option. Another option which comes to mind is:

When the Expense is added to the Report, An event is fired "ExpenseAddedToReport". 
A saga handles this event en changes the status of the Expense (using expenseId in event data)  to "Approved". Approved means in our domain that it's added to a report. If the Status was different from expected, the saga will throw an exception.

However I'm not not really that the Expense should know a status "Approved". Maybe it's not the concern of the expense if it's added to a report.


xprt64

unread,
Jul 15, 2017, 5:09:56 AM7/15/17
to ddd...@googlegroups.com
Sagas should not throw exception but send compensatory commands.

Greg Young

unread,
Jul 15, 2017, 7:12:58 AM7/15/17
to ddd...@googlegroups.com
I don't think Accepted is the right verb there maybe Attached?

--
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+unsubscribe@googlegroups.com.
Visit this group at https://groups.google.com/group/dddcqrs.
For more options, visit https://groups.google.com/d/optout.

Peter Hageus

unread,
Jul 15, 2017, 7:14:39 AM7/15/17
to ddd...@googlegroups.com
The only way I can see this being a problem is if Expense is an aggregate, and entered/registred in advance, and later assigned to a Report.

The ui/viewmodel for assigning expenses to reports can filter out already assigned. If eventual consistency is an issue (sounds unlikely, this seems lika a manual, non-conccurent process?), either have a process manager fix conflicts, or a view with double assigned expenses that a user can resolve.

If Expense is an entity, created as part of a Report aggregate, I don’t see how you can detect conflicts, unless the user enters something globally unique.

/Peter

--
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.

Samuel Francisco

unread,
Jul 31, 2017, 11:54:02 AM7/31/17
to DDD/CQRS
@Kees Schouten

What are the invariants protected by the Expense Report aggregate by means of the Expenses array??? Why do you need that array???

If Expenses exists outside the Expense Report the Expense should be itself an aggregate.

If there is none invariant between the Expenses inside a Expense Report there's no need of the array and its better that the Expense protects the business rule that it should not be attached tho more than one Expense Report by means of the ExpenseReportId property.
Reply all
Reply to author
Forward
0 new messages