@Transactional(isolation = Isolation.SERIALIZABLE)//highest isolation needed because of manipulating many Aggregates
public void confirm(AggregateId orderId, OrderDetailsCommand orderDetailsCommand, Offer seenOffer)
throws OfferChangedExcpetion {
Reservation reservation = reservationRepository.load(orderId);
if (reservation.isClosed())
throw new DomainOperationException(reservation.getAggregateId(), "reservation is already closed");
/*
* Sample pattern: Aggregate generates Value Object using function<br>
* Higher order function is closured by policy
*/
Offer newOffer = reservation.calculateOffer(
discountFactory.create(loadClient()));
/*
* Sample pattern: Client Tier sends back old VOs, Server generates new VOs based on Aggregate state<br>
* Notice that this VO is not stored in Repo, it's stored on the Client Tier.
*/
if (! newOffer.sameAs(seenOffer, 5))//TODO load delta from conf.
throw new OfferChangedExcpetion(reservation.getAggregateId(), seenOffer, newOffer);
Client client = loadClient();//create per logged client, not reservation owner
Purchase purchase = purchaseFactory.create(reservation.getAggregateId(), client, seenOffer);
if (! client.canAfford(purchase.getTotalCost()))
throw new DomainOperationException(client.getAggregateId(), "client has insufficent money");
purchaseRepository.save(purchase);//Aggregate must be managed by persistence context before firing events (synchronous listeners may need to load it)
/*
* Sample model where one aggregate creates another. Client does not manage payment lifecycle, therefore application must manage it.