2

I'll preface this question by saying I am relatively new to DDD so I may be making some fundamental mistakes here!

I am working on a project which involves the concepts of Transactions (in the financial sense like Credit Card Transaction, or Single/Multiple CardTransaction.). A Transaction can be on Single card or Multiple Cards. The transaction amount may differ for each card. Also Cardholder or CardCustomer can be same or different.

A Transaction perform some specific business logic execution based on TransactionTypes (like Activate, Redeem ....).

A Transaction will perform following operations - Pre Transaction Validation on Card - Update Balance - Update Card Status - Update Customer info - Log Transaction

Now i am confused to decide what could be my aggregateroot here.

Approach 1

Use transaction an an entity holding cardid, amount, customerid, transactiontype, transactionlogid as a ValueObject and Card, CardAmountSummary, CardCustomer, and TransactionLog as an entity Create Seperate AggreateRoot for SinglegiftCardTransaction, MultipleGiftCardTransaction, BookletTransaction, WalletTransaction each will act as a wrapper for Transaction/s. Transaction boundary may differ based on bulk or single transaction.

Approach 2

Use Transaction as a AggregateRoot holding a list of Cards, Each Card will have CustomerId, Amount, CardAmountSummaryId, transactionlogid, transactiontypeid as a Value object.

What would be best fit for AggreateRoot and Entities here ???

1

Conceptually, the issue you are describing is finding a balance between choosing the which objects should "own" the data, and which objects should "use" that data. While DDD (and OOP in general) is a paradigm defined by bringing data and behavior together, the specific kind of behavior you are describing simply cannot be contained within the same object as the own that "owns" the data. With the above in mind, the answer to your question is... neither. Coordination of business rules between entities is best left for a domain service.

Consider the example of withdrawing money (withdraw) from your BankAccount using an ATM. The issue here is there are two transactions that must complete in order for money to be dispensed, each with their own business rules that need to be enforced. The ATM.dispense method is subject to the rule that the ATM must (physically) have enough currency to dispense the amount desired. And your BankAccount.debit method requires that you have the available funds in your account. The only way to transactionally mandate that both rules are enforced without creating a direct dependency between the ATM and BankAccount entities is to create an ATMService object that can coordinate/mediate the business process between the two entities that actually "own" the data. The body of the withdraw method might look like:

// each of these could throw an exception 
atm.dispense( amount );
account.debit( amount );

Importantly, the ATM.withdraw method is carried out as part of a single transaction. So if there is an error in any part of the process, nothing is committed (all or nothing). Transaction id and the like could be added and used for event correlation if needed.

The above example is similar to your first option, but differs in that the ATMService does not "own" any data at all (it is not, itself, an aggregate). The data, and intermediary transactions are all owned and carried out by the appropriate entities.

For long-running or async processes, you could introduce a process manager to help mediate a work-flow as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.