Package pl.com.bottega.ecommerce.sales.application.impl

Source Code of pl.com.bottega.ecommerce.sales.application.impl.OrderingServiceImpl

/*
* Copyright 2011-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package pl.com.bottega.ecommerce.sales.application.impl;

import javax.inject.Inject;

import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import pl.com.bottega.ddd.annotations.application.ApplicationService;
import pl.com.bottega.ecommerce.canonicalmodel.publishedlanguage.AggregateId;
import pl.com.bottega.ecommerce.sales.application.api.command.OrderDetailsCommand;
import pl.com.bottega.ecommerce.sales.application.api.service.OfferChangedExcpetion;
import pl.com.bottega.ecommerce.sales.application.api.service.OrderingService;
import pl.com.bottega.ecommerce.sales.domain.client.Client;
import pl.com.bottega.ecommerce.sales.domain.client.ClientRepository;
import pl.com.bottega.ecommerce.sales.domain.equivalent.SuggestionService;
import pl.com.bottega.ecommerce.sales.domain.offer.DiscountFactory;
import pl.com.bottega.ecommerce.sales.domain.offer.DiscountPolicy;
import pl.com.bottega.ecommerce.sales.domain.offer.Offer;
import pl.com.bottega.ecommerce.sales.domain.payment.Payment;
import pl.com.bottega.ecommerce.sales.domain.payment.PaymentRepository;
import pl.com.bottega.ecommerce.sales.domain.productscatalog.Product;
import pl.com.bottega.ecommerce.sales.domain.productscatalog.ProductRepository;
import pl.com.bottega.ecommerce.sales.domain.purchase.Purchase;
import pl.com.bottega.ecommerce.sales.domain.purchase.PurchaseFactory;
import pl.com.bottega.ecommerce.sales.domain.purchase.PurchaseRepository;
import pl.com.bottega.ecommerce.sales.domain.reservation.Reservation;
import pl.com.bottega.ecommerce.sales.domain.reservation.ReservationFactory;
import pl.com.bottega.ecommerce.sales.domain.reservation.ReservationRepository;
import pl.com.bottega.ecommerce.sharedkernel.exceptions.DomainOperationException;
import pl.com.bottega.ecommerce.system.application.SystemContext;

/**
* Ordering Use Case steps<br>
* Each step is a Domain Story<br>
* <br>
* Notice that application language is different (simpler) than domain language, ex: we don'nt want to exposure domain concepts like Purchase and Reservation to the upper layers, we hide them under the Order term 
* <br>
* Technically App Service is just a bunch of procedures, therefore OO principles (ex: CqS, SOLID, GRASP) does not apply here 
*
* @author Slawek
*/
@ApplicationService
public class OrderingServiceImpl implements OrderingService {

  @Inject
  private SystemContext systemContext;
 
  @Inject
  private ClientRepository clientRepository;

  @Inject
  private ReservationRepository reservationRepository;

  @Inject
  private ReservationFactory reservationFactory;

  @Inject
  private PurchaseFactory purchaseFactory;

  @Inject
  private PurchaseRepository purchaseRepository;
 
  @Inject
  private ProductRepository productRepository;
 
  @Inject
  private PaymentRepository paymentRepository;

  @Inject
  private DiscountFactory discountFactory;
 
  @Inject
  private SuggestionService suggestionService;

  // @Secured requires BUYER role
  public AggregateId createOrder() {
    Reservation reservation = reservationFactory.create(loadClient());
    reservationRepository.save(reservation);
    return reservation.getAggregateId();
  }

  /**
   * DOMAIN STORY<br>
   * try to read this as a full sentence, this way: subject.predicate(completion)<br>
   * <br>
   * Load reservation by orderId<br>
   * Load product by productId<br>
   * Check if product is not available<br>
   * -if so, than suggest equivalent for that product based on client<br>
   * Reservation add product by given quantity
   */
  @Override
  public void addProduct(AggregateId orderId, AggregateId productId,
      int quantity) {
    Reservation reservation = reservationRepository.load(orderId);
   
    Product product = productRepository.load(productId);
   
    if (! product.isAvailabe()){
      Client client = loadClient()
      product = suggestionService.suggestEquivalent(product, client);
    }
     
    reservation.add(product, quantity);
   
    reservationRepository.save(reservation);
  }
 
  /**
   * Can be invoked many times for the same order (with different params).<br>
   * Offer VO is not stored in the Repo, it is stored on the Client Tier instead.
   */
  public Offer calculateOffer(AggregateId orderId) {
    Reservation reservation = reservationRepository.load(orderId);

    DiscountPolicy discountPolicy = discountFactory.create(loadClient());
   
    /*
     * Sample pattern: Aggregate generates Value Object using function<br>
     * Higher order function is closured by policy
     */
    return reservation.calculateOffer(discountPolicy);
  }

  /**
   * DOMAIN STORY<br>
   * try to read this as a full sentence, this way: subject.predicate(completion)<br>
   * <br>
   * Load reservation by orderId<br>
   * Check if reservation is closed - if so, than Error<br>
   * Generate new offer from reservation using discount created per client<br>
   * Check if new offer is not the same as seen offer using delta = 5<br>
   * Create purchase per client based on seen offer<br>
   * Check if client can not afford total cost of purchase - if so, than Error<br>
   * Confirm purchase<br>
   * Close reservation<br>
   */
  @Override
  @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.
     */
    Payment payment = client.charge(purchase.getTotalCost());
    paymentRepository.save(payment);
   
    purchase.confirm()
    reservation.close();       
   
    reservationRepository.save(reservation);
    clientRepository.save(client);
   
  }
 
  private Client loadClient() {
    return clientRepository.load(systemContext.getSystemUser().getClientId());
  }
}
TOP

Related Classes of pl.com.bottega.ecommerce.sales.application.impl.OrderingServiceImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.