I have a payment system as shown below. The payment can be made through multiple gift coupons. The gift coupons are issued along with a purchase. The customer can make use of this gift coupon for future purchase.
When a Payment is made through gift coupon, the UsedForPaymentID column in GiftCoupon table need to be updated with that PaymentID (for the giftcoupon ID).
The GiftCouponIDs are already available in the database. When a customer produces a gift coupon, it has GiftCouponID printed on it. The operator need to enter this CouponID to the system to make the Payment.
For the MakePayment() operation, it necessitates two repositories.
- Gift Coupon Repository
- Payment Repository
CODE
//Use GiftCouponRepository to retrieve the corresponding GiftCoupon object.
This involves use of two repositories for one transaction. Is it a good practice? If not, how can we change the design to overcome this?
Reference: In DDD the Aggregate should represent the transactional boundary. A transaction that requires the involvement of more than one aggregate is often a sign that either the model should be refined, or the transactional requirements should be reviewed, or both. Is CQRS correct for my domain?

C# CODE
public RepositoryLayer.ILijosPaymentRepository repository { get; set; }
public void MakePayment(int giftCouponID)
{
DBML_Project.Payment paymentEntity = new DBML_Project.Payment();
paymentEntity.PaymentID = 1;
DBML_Project.GiftCoupon giftCouponObj;
//Use GiftCouponRepository to retrieve the corresponding GiftCoupon object.
paymentEntity.GiftCouponPayments = new System.Data.Linq.EntitySet<DBML_Project.GiftCoupon>();
paymentEntity.GiftCouponPayments.Add(giftCouponObj);
repository.InsertEntity(paymentEntity);
repository.SubmitChanges();
}
I think what you really meant to ask was regarding ‘Multiple Aggregates in one transaction‘. I don’t believe there is anything wrong with using multiple repositories to fetch data in a transaction. Often during a transaction an aggregate will need information from other aggregates in order to make a decision on whether to, or how to, change state. That’s fine. It is, however, the modifying of state on multiple aggregates within one transaction that is deemed undesirable, and I think this what your referenced quote was trying to imply.
The reason this is undesirable is because of concurrency. As well as protecting the in-variants within it’s boundary, each aggregate should be protected from concurrent transactions. e.g. two users making a change to an aggregate at the same time.
This protection is typically achieved by having a version/timestamp on the aggregates’ DB table. When the aggregate is saved, a comparison is made of the version being saved and the version currently stored in the db (which may now be different from when the transaction started). If they don’t match an exception is raised.
It basically boils down to this: In a collaborative system (many users making many transactions), the more aggregates that are modified in a single transaction will result in an increase of concurrency exceptions.
The exact same thing is true if your aggregate is too large & offers many state changing methods; multiple users can only modify the aggregate one at a time. By designing small aggregates that are modified in isolation in a transaction reduces concurrency collisions.
Vaughn Vernon has done an excellent job explaining this in his 3 part article.
However, this is just a guiding principle and there will be exceptions where more than one aggregate will need to be modified. The fact that you are considering whether the transaction/use case could be re-factored to only modify one aggregate is a good thing.
Having thought about your example, I cannot think of a way of designing it to a single aggregate that fulfills the requirements of the transaction/use case. A payment needs to be created, and the coupon needs to be updated to indicate that it is no longer valid.
But when really analysing the potential concurrency issues with this transaction, I don’t think there would ever actually be a collision on the gift coupon aggregate. They are only ever created (issued) then used for payment. There are no other state changing operations in between. Therefore in this instance we don’t need to be concerned about that fact we are modifying both the payment/order & gift coupon aggregate.
Below is what I quickly came up with as a possible way of modelling it
Code: