Mattias Santoro
Mattias Santoro

Reputation: 184

DDD: How to prevent Purchase to be persisted without related withdrawal

Let's say that an Employee can purchase products using some sort of "welfare credits" given by his Employer.

I have a CreditsAccount entity that holds the amount of credits and a reference to the employee, and I want to model the Purchase as an entity that holds the reference to the CreditsAccount and the product is purchasing.

If I have a PurchaseRepository.save() method, how can I prevent that a Purchase is saved without doing any withdraw from the CreditsAccount?

Solution 1

The Purchase is a subentity of the aggregate CreditsAccount, I don't have any PurchaseRepository but instead a method on the account like CreditsAccount.AddPurchase(purchase) that make internally the withdraw. When I persist the CreditsAccount with a CreditsAccountRepository.save(account) I persist also the purchase in the proper table.

But this means that every time I load a CreditsAccount to make a new purchase I have also to load the list of Purchases on that account right?

Solution 2

I build a new PurchaseTransaction entity, that hold a reference to the CreditsAccount and the Product, and offer a method like PurchaseTransaction.commit(). On commit it actually make the withdrawal from the referenced account, based on Product price, and its inner state switch from uncommitted to committed. I have then a PurchaseTransactionRepo.save(purchaseTransaction) that:

In this case the PurchaseTransactionRepo offers only a method for writing on db, since actually a PurchaseTransaction doesn't really belong to any table, while the PurchaseRepository is readonly and offer only methods to get "committed" purchases.

What I don't like of this solution is to have a Repo that actually make a sort of validation logic on the state of the PurchaseTransaction (committed or not), and also it seems misleading that an Uncommitted PurchaseTransaction is not really persisted.

Can you suggest me other solutions compliants with DDD concepts?

Some context: not a real application, but a kata exercise in which I have to refactor from an anemic domain to a rich one. In the anemic one there is a service that withdraw the account and save the purchase in a single transaction. Then I can get a list of purchase with an api call. I want to model it with rich entities and "enforce" invalid states to be not "persistable"

Upvotes: 0

Views: 111

Answers (1)

Levi Ramsey
Levi Ramsey

Reputation: 20561

Purchase seems like a good candidate for a saga, which can be thought of as an entity that represents a process. Without more context, I'd model it similarly to the way that payments often process in the real world (apologies for the very CQRS/actor-modely anthropomorphization) and has purchases in four state: open, finalized, funded, expired, and completed.

  • once the purchase entity knows the items to be purchased and their cost(s), it can be finalized, which puts it into a finalized state and prevents future updates of the items and cost. It's saved in this state.

  • It then attempts to reserve the credits from the employee's account, with a deadline for the withdrawal to be finalized or the reservation to disappear. If successful, it moves to state "funded" and that's saved.

I would probably have some other process scan (since we're not event sourcing here) for purchases which are funded and effect the actual withdrawal, which allows the purchase to be picked up by a fulfillment component, as well as saving the purchase in the state "completed" (there are a lot of choices for what to do if the purchase should be expired.

Upvotes: 0

Related Questions