0

I'm working on a payment system where payments can be split among multiple payees. Additionally, we have refunds, which also can be split among these payees. I have some business rules that need to be enforced:

  1. The sum of payment splits should not exceed the total payment amount.
  2. The sum of refund splits should not exceed the net payment amount (i.e., the total payment amount minus the total refunded amount).
  3. A specific refund split for a payee should not exceed the amount that was originally paid to that payee.

I've tried modeling Payment and Refund as separate aggregate roots, with associated entities for their splits (PaymentSplit and RefundSplit respectively). However, I'm facing challenges ensuring the invariants, especially since some rules require cross-referencing between aggregates.

How can I design my aggregates to enforce these rules effectively without breaking DDD principles?

  • 1
    Having this level of coupling between refund and payment suggests to me that they should be responsibilities of the same aggregate (especially in view that a refund is of a particular previously received payment). – Levi Ramsey Aug 18 '23 at 15:12
  • @LeviRamsey, hey, you are right about that. I have realized that payment changes with each refund created and payment has to be aggregate root here. But I also have introduced aggregate root “payment distribution”. Essentially would be responsible to distribute payment among merchants. But having hard time with designing refund of distributed payment (can be partial). Do you have any suggestions? – Kainar Masujima Aug 19 '23 at 05:04

1 Answers1

0

The payment receipt should be modelled as ValueObject referencing the PayeeId and the amount.

Those receipts will be part of your root aggregate so you don't break the Transactional scope, and all your invariants rules will be part of the same transaction.

Sylvain Lecoy
  • 947
  • 6
  • 15