Polish payment service

This commit is contained in:
Kenny Bastani
2016-12-26 07:30:34 -05:00
parent 6f022b8ee6
commit 1418ee2bbf
12 changed files with 55 additions and 48 deletions

View File

@@ -11,8 +11,6 @@ import javax.persistence.*;
/** /**
* The domain event {@link PaymentEvent} tracks the type and state of events as applied to the {@link Payment} domain * The domain event {@link PaymentEvent} tracks the type and state of events as applied to the {@link Payment} domain
* object. This event resource can be used to event source the aggregate state of {@link Payment}. * object. This event resource can be used to event source the aggregate state of {@link Payment}.
* <p>
* This event resource also provides a transaction log that can be used to append actions to the event.
* *
* @author kbastani * @author kbastani
*/ */

View File

@@ -1,4 +1,9 @@
package demo.event; package demo.event;
/**
* The repository for managing the persistence of {@link PaymentEvent}s.
*
* @author Kenny Bastani
*/
public interface PaymentEventRepository extends EventRepository<PaymentEvent, Long> { public interface PaymentEventRepository extends EventRepository<PaymentEvent, Long> {
} }

View File

@@ -4,8 +4,8 @@ import demo.payment.Payment;
import demo.payment.PaymentStatus; import demo.payment.PaymentStatus;
/** /**
* The {@link PaymentEventType} represents a collection of possible events that describe * The {@link PaymentEventType} represents a collection of possible events that describe state transitions of
* state transitions of {@link PaymentStatus} on the {@link Payment} aggregate. * {@link PaymentStatus} on the {@link Payment} aggregate.
* *
* @author kbastani * @author kbastani
*/ */

View File

@@ -12,12 +12,10 @@ import java.util.Set;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
/** /**
* The {@link Payment} domain object contains information related to * The {@link Payment} domain object contains information related to a user's payment. The status of an payment is
* a user's payment. The status of an payment is event sourced using * event sourced using events logged to the {@link PaymentEvent} collection attached to this resource.
* events logged to the {@link PaymentEvent} collection attached to
* this resource.
* *
* @author kbastani * @author Kenny Bastani
*/ */
@Entity @Entity
public class Payment extends BaseEntity { public class Payment extends BaseEntity {

View File

@@ -1,11 +1,11 @@
package demo.payment; package demo.payment;
/** /**
* The {@link PaymentCommand} represents an action that can be performed to an * The {@link PaymentCommand} represents an action that can be performed to an {@link Payment} aggregate. Commands
* {@link Payment} aggregate. Commands initiate an action that can mutate the state of * initiate an action that can mutate the state of an payment entity as it transitions between {@link PaymentStatus}
* an payment entity as it transitions between {@link PaymentStatus} values. * values.
* *
* @author kbastani * @author Kenneth Bastani
*/ */
public enum PaymentCommand { public enum PaymentCommand {
CONNECT_ORDER, CONNECT_ORDER,

View File

@@ -3,10 +3,9 @@ package demo.payment;
import org.springframework.hateoas.ResourceSupport; import org.springframework.hateoas.ResourceSupport;
/** /**
* A hypermedia resource that describes the collection of commands that * A hypermedia resource that describes the collection of commands that can be applied to a {@link Payment} aggregate.
* can be applied to a {@link Payment} aggregate.
* *
* @author kbastani * @author Kenny Bastani
*/ */
public class PaymentCommands extends ResourceSupport { public class PaymentCommands extends ResourceSupport {
} }

View File

@@ -13,6 +13,11 @@ import java.util.Optional;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
/**
* The REST API for managing {@link Payment} entities and {@link PaymentEvent}s.
*
* @author Kenny Bastani
*/
@RestController @RestController
@RequestMapping("/v1") @RequestMapping("/v1")
@ExposesResourceFor(Payment.class) @ExposesResourceFor(Payment.class)
@@ -107,7 +112,6 @@ public class PaymentController {
if (payment != null) if (payment != null)
paymentResource = getPaymentResource(payment); paymentResource = getPaymentResource(payment);
return paymentResource; return paymentResource;
} }
@@ -138,8 +142,8 @@ public class PaymentController {
} }
/** /**
* Appends an {@link PaymentEvent} domain event to the event log of the {@link Payment} * Appends an {@link PaymentEvent} domain event to the event log of the {@link Payment} aggregate with the
* aggregate with the specified paymentId. * specified paymentId.
* *
* @param paymentId is the unique identifier for the {@link Payment} * @param paymentId is the unique identifier for the {@link Payment}
* @param event is the {@link PaymentEvent} that attempts to alter the state of the {@link Payment} * @param event is the {@link PaymentEvent} that attempts to alter the state of the {@link Payment}
@@ -167,8 +171,8 @@ public class PaymentController {
} }
/** /**
* Get the {@link PaymentCommand} hypermedia resource that lists the available commands that can be applied * Get the {@link PaymentCommand} hypermedia resource that lists the available commands that can be applied to a
* to an {@link Payment} entity. * {@link Payment} entity.
* *
* @param id is the {@link Payment} identifier to provide command links for * @param id is the {@link Payment} identifier to provide command links for
* @return an {@link PaymentCommands} with a collection of embedded command links * @return an {@link PaymentCommands} with a collection of embedded command links

View File

@@ -1,5 +1,10 @@
package demo.payment; package demo.payment;
/**
* The {@link Payment} method for the transaction.
*
* @author Kenny Bastani
*/
public enum PaymentMethod { public enum PaymentMethod {
CREDIT_CARD CREDIT_CARD
} }

View File

@@ -3,6 +3,11 @@ package demo.payment;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
/**
* The repository for managing the persistence of {@link Payment} entities.
*
* @author Kenny Bastani
*/
public interface PaymentRepository extends JpaRepository<Payment, Long> { public interface PaymentRepository extends JpaRepository<Payment, Long> {
Payment findPaymentByOrderId(@Param("orderId") Long orderId); Payment findPaymentByOrderId(@Param("orderId") Long orderId);
} }

View File

@@ -1,29 +1,23 @@
package demo.payment; package demo.payment;
import demo.event.EventService; import demo.event.EventService;
import demo.util.ConsistencyModel;
import demo.event.PaymentEvent; import demo.event.PaymentEvent;
import demo.event.PaymentEventType; import demo.event.PaymentEventType;
import org.springframework.cache.annotation.CacheConfig; import demo.util.ConsistencyModel;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import java.util.Objects; import java.util.Objects;
/** /**
* The {@link PaymentService} provides transactional support for managing {@link Payment} * The {@link PaymentService} provides transactional support for managing {@link Payment} entities. This service also
* entities. This service also provides event sourcing support for {@link PaymentEvent}. * provides event sourcing support for {@link PaymentEvent}. Events can be appended to an {@link Payment}, which
* Events can be appended to an {@link Payment}, which contains a append-only log of * contains a append-only log of actions that can be used to support remediation for distributed transactions that
* actions that can be used to support remediation for distributed transactions that encountered * encountered a partial failure.
* a partial failure.
* *
* @author kbastani * @author Kenny Bastani
*/ */
@Service @Service
@CacheConfig(cacheNames = {"payments"})
public class PaymentService { public class PaymentService {
private final PaymentRepository paymentRepository; private final PaymentRepository paymentRepository;
@@ -34,14 +28,10 @@ public class PaymentService {
this.eventService = eventService; this.eventService = eventService;
} }
@CacheEvict(cacheNames = "payments", key = "#payment.getPaymentId().toString()")
public Payment registerPayment(Payment payment) { public Payment registerPayment(Payment payment) {
payment = createPayment(payment); payment = createPayment(payment);
// cacheManager.getCache("payments")
// .evict(payment.getPaymentId());
// Trigger the payment creation event // Trigger the payment creation event
PaymentEvent event = appendEvent(payment.getPaymentId(), PaymentEvent event = appendEvent(payment.getPaymentId(),
new PaymentEvent(PaymentEventType.PAYMENT_CREATED)); new PaymentEvent(PaymentEventType.PAYMENT_CREATED));
@@ -59,7 +49,6 @@ public class PaymentService {
* @param payment is the {@link Payment} to create * @param payment is the {@link Payment} to create
* @return the newly created {@link Payment} * @return the newly created {@link Payment}
*/ */
@CacheEvict(cacheNames = "payments", key = "#payment.getPaymentId().toString()")
public Payment createPayment(Payment payment) { public Payment createPayment(Payment payment) {
// Save the payment to the repository // Save the payment to the repository
@@ -74,7 +63,6 @@ public class PaymentService {
* @param id is the unique identifier of a {@link Payment} entity * @param id is the unique identifier of a {@link Payment} entity
* @return an {@link Payment} entity * @return an {@link Payment} entity
*/ */
@Cacheable(cacheNames = "payments", key = "#id.toString()")
public Payment getPayment(Long id) { public Payment getPayment(Long id) {
return paymentRepository.findOne(id); return paymentRepository.findOne(id);
} }
@@ -86,7 +74,6 @@ public class PaymentService {
* @param payment is the {@link Payment} containing updated fields * @param payment is the {@link Payment} containing updated fields
* @return the updated {@link Payment} entity * @return the updated {@link Payment} entity
*/ */
@CachePut(cacheNames = "payments", key = "#id.toString()")
public Payment updatePayment(Long id, Payment payment) { public Payment updatePayment(Long id, Payment payment) {
Assert.notNull(id, "Payment id must be present in the resource URL"); Assert.notNull(id, "Payment id must be present in the resource URL");
Assert.notNull(payment, "Payment request body cannot be null"); Assert.notNull(payment, "Payment request body cannot be null");
@@ -112,7 +99,6 @@ public class PaymentService {
* *
* @param id is the unique identifier for the {@link Payment} * @param id is the unique identifier for the {@link Payment}
*/ */
@CacheEvict(cacheNames = "payments", key = "#id.toString()")
public Boolean deletePayment(Long id) { public Boolean deletePayment(Long id) {
Assert.state(paymentRepository.exists(id), Assert.state(paymentRepository.exists(id),
"The payment with the supplied id does not exist"); "The payment with the supplied id does not exist");
@@ -144,13 +130,15 @@ public class PaymentService {
Payment payment = getPayment(paymentId); Payment payment = getPayment(paymentId);
Assert.notNull(payment, "The payment with the supplied id does not exist"); Assert.notNull(payment, "The payment with the supplied id does not exist");
// Add the entity to the event
event.setEntity(payment); event.setEntity(payment);
event = eventService.save(paymentId, event); event = eventService.save(paymentId, event);
// Add the event to the entity
payment.getEvents().add(event); payment.getEvents().add(event);
paymentRepository.saveAndFlush(payment); paymentRepository.saveAndFlush(payment);
// Raise the event using the supplied consistency model // Applies the event for the chosen consistency model
switch (consistencyModel) { switch (consistencyModel) {
case BASE: case BASE:
eventService.sendAsync(event); eventService.sendAsync(event);
@@ -170,10 +158,8 @@ public class PaymentService {
* @param paymentCommand is the command to apply to the {@link Payment} * @param paymentCommand is the command to apply to the {@link Payment}
* @return a hypermedia resource containing the updated {@link Payment} * @return a hypermedia resource containing the updated {@link Payment}
*/ */
@CachePut(cacheNames = "payments", key = "#id.toString()")
public Payment applyCommand(Long id, PaymentCommand paymentCommand) { public Payment applyCommand(Long id, PaymentCommand paymentCommand) {
Payment payment = getPayment(id); Payment payment = getPayment(id);
Assert.notNull(payment, "The payment for the supplied id could not be found"); Assert.notNull(payment, "The payment for the supplied id could not be found");
PaymentStatus status = payment.getStatus(); PaymentStatus status = payment.getStatus();

View File

@@ -1,11 +1,10 @@
package demo.payment; package demo.payment;
/** /**
* The {@link PaymentStatus} describes the state of an {@link Payment}. * The {@link PaymentStatus} describes the state of an {@link Payment}. The aggregate state of a {@link Payment} is
* The aggregate state of a {@link Payment} is sourced from attached domain * sourced from attached domain events in the form of {@link demo.event.PaymentEvent}.
* events in the form of {@link demo.event.PaymentEvent}.
* *
* @author kbastani * @author Kenny Bastani
*/ */
public enum PaymentStatus { public enum PaymentStatus {
PAYMENT_CREATED, PAYMENT_CREATED,

View File

@@ -1,5 +1,13 @@
package demo.util; package demo.util;
/**
* The {@link ConsistencyModel} is used to configure how an {@link demo.event.Event} will be applied to an entity.
* BASE is eventually consistent and will process an event workflow asynchronously using Spring Cloud Stream.
* ACID uses strong eventual consistency and will process an event workflow sequentially and return the result to the
* calling thread.
*
* @author Kenny Bastani
*/
public enum ConsistencyModel { public enum ConsistencyModel {
BASE, BASE,
ACID ACID