Polish payment service
This commit is contained in:
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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> {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user