├── README.md
└── saga-choreography-pattern
├── common-dtos
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── javatechie
│ └── saga
│ └── commons
│ ├── CommonsDtoApplication.java
│ ├── dto
│ ├── OrderRequestDto.java
│ ├── OrderResponseDto.java
│ └── PaymentRequestDto.java
│ └── event
│ ├── Event.java
│ ├── OrderEvent.java
│ ├── OrderStatus.java
│ ├── PaymentEvent.java
│ └── PaymentStatus.java
├── order-service
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── javatechie
│ │ └── saga
│ │ └── order
│ │ ├── OrderServiceApplication.java
│ │ ├── config
│ │ ├── EventConsumerConfig.java
│ │ ├── OrderPublisherConfig.java
│ │ └── OrderStatusUpdateHandler.java
│ │ ├── controller
│ │ └── OrderController.java
│ │ ├── entity
│ │ └── PurchaseOrder.java
│ │ ├── repository
│ │ └── OrderRepository.java
│ │ └── service
│ │ ├── OrderService.java
│ │ └── OrderStatusPublisher.java
│ └── resources
│ ├── application.properties
│ └── application.yaml
├── payment-service
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── javatechie
│ │ └── saga
│ │ └── payment
│ │ ├── PaymentServiceApplication.java
│ │ ├── config
│ │ └── PaymentConsumerConfig.java
│ │ ├── entity
│ │ ├── UserBalance.java
│ │ └── UserTransaction.java
│ │ ├── repository
│ │ ├── UserBalanceRepository.java
│ │ └── UserTransactionRepository.java
│ │ └── service
│ │ └── PaymentService.java
│ └── resources
│ ├── application.properties
│ └── application.yaml
├── pom.xml
└── saga-choreography-pattern.iml
/README.md:
--------------------------------------------------------------------------------
1 | # saga-choreography-example
2 |
3 |
4 | ## request
5 | ```bash
6 | curl --location --request POST 'http://localhost:8081/order/create' \
7 | --header 'Content-Type: application/json' \
8 | --data-raw '{
9 | "userId": 103,
10 | "productId": 33,
11 | "amount": 4000
12 | }'
13 | ```
14 | ## Kafka payload
15 |
16 | ### Happy scenario
17 | ```bash
18 | {"eventId":"b0e47448-eeb8-4cf4-bd29-b3a4315fc592","date":"2021-09-03T17:26:46.777+00:00","orderRequestDto":{"userId":103,"productId":33,"amount":4000,"orderId":1},"orderStatus":"ORDER_CREATED"}
19 | ```
20 |
21 | ```bash
22 | {"eventId":"c48c5593-9f81-4ab4-9de8-b9fca2d2bef2","date":"2021-09-03T17:26:51.989+00:00","paymentRequestDto":{"orderId":1,"userId":103,"amount":4000},"paymentStatus":"PAYMENT_COMPLETED"}
23 | ```
24 |
25 | ## Request
26 | ```bash
27 | curl --location --request POST 'http://localhost:8081/orders' \
28 | --header 'Content-Type: application/json' \
29 | --data-raw '{
30 | "userId": 103,
31 | "productId": 12,
32 | "amount": 800
33 | }'
34 | ```
35 |
36 | ### insufficent amount
37 | ```bash
38 | {"eventId":"fecacc77-017d-49cd-bdfa-58e47170da49","date":"2021-09-03T17:28:23.126+00:00","orderRequestDto":{"userId":103,"productId":12,"amount":800,"orderId":2},"orderStatus":"ORDER_CANCELLED"}
39 | ```
40 |
41 | ```bash
42 | {"eventId":"46378bbc-5d15-4436-bed1-c6f3ddb1dc31","date":"2021-09-03T17:28:15.940+00:00","paymentRequestDto":{"orderId":2,"userId":103,"amount":800},"paymentStatus":"PAYMENT_FAILED"}
43 | ```
44 |
45 | ```bash
46 | curl --location --request GET 'http://localhost:8081/orders' \
47 | --header 'Content-Type: application/json' \
48 | --data-raw ''
49 | ```
50 |
51 | ## Response
52 |
53 | ```bash
54 | [
55 | {
56 | "id": 1,
57 | "userId": 103,
58 | "productId": 33,
59 | "price": 4000,
60 | "orderStatus": "ORDER_COMPLETED",
61 | "paymentStatus": "PAYMENT_COMPLETED"
62 | },
63 | {
64 | "id": 2,
65 | "userId": 103,
66 | "productId": 12,
67 | "price": 800,
68 | "orderStatus": "ORDER_CANCELLED",
69 | "paymentStatus": "PAYMENT_FAILED"
70 | }
71 | ]
72 | ```
73 |
74 | ## user Balance
75 | 
76 |
77 | ## Order repo
78 | 
79 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/common-dtos/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | saga-choreography-pattern
7 | com.javatechie
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | common-dtos
13 |
14 |
15 |
16 | org.projectlombok
17 | lombok
18 | true
19 |
20 |
21 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/common-dtos/src/main/java/com/javatechie/saga/commons/CommonsDtoApplication.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.commons;
2 |
3 | public class CommonsDtoApplication {
4 |
5 | public static void main(String[] args) {
6 |
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/common-dtos/src/main/java/com/javatechie/saga/commons/dto/OrderRequestDto.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.commons.dto;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | @Data
8 | @AllArgsConstructor
9 | @NoArgsConstructor
10 | public class OrderRequestDto {
11 |
12 | private Integer userId;
13 | private Integer productId;
14 | private Integer amount;
15 | private Integer orderId;
16 | }
17 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/common-dtos/src/main/java/com/javatechie/saga/commons/dto/OrderResponseDto.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.commons.dto;
2 |
3 | import com.javatechie.saga.commons.event.OrderStatus;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | @Data
9 | @AllArgsConstructor
10 | @NoArgsConstructor
11 | public class OrderResponseDto {
12 |
13 | private Integer userId;
14 | private Integer productId;
15 | private Integer amount;
16 | private Integer orderId;
17 | private OrderStatus orderStatus;
18 | }
19 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/common-dtos/src/main/java/com/javatechie/saga/commons/dto/PaymentRequestDto.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.commons.dto;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | @Data
8 | @AllArgsConstructor
9 | @NoArgsConstructor
10 | public class PaymentRequestDto {
11 |
12 | private Integer orderId;
13 | private Integer userId;
14 | private Integer amount;
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/common-dtos/src/main/java/com/javatechie/saga/commons/event/Event.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.commons.event;
2 |
3 | import java.util.Date;
4 | import java.util.UUID;
5 |
6 | public interface Event {
7 |
8 | UUID getEventId();
9 |
10 | Date getDate();
11 | }
12 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/common-dtos/src/main/java/com/javatechie/saga/commons/event/OrderEvent.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.commons.event;
2 |
3 | import com.javatechie.saga.commons.dto.OrderRequestDto;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | import java.util.Date;
8 | import java.util.UUID;
9 | @NoArgsConstructor
10 | @Data
11 | public class OrderEvent implements Event{
12 |
13 | private UUID eventId=UUID.randomUUID();
14 | private Date eventDate=new Date();
15 | private OrderRequestDto orderRequestDto;
16 | private OrderStatus orderStatus;
17 |
18 | @Override
19 | public UUID getEventId() {
20 | return eventId;
21 | }
22 |
23 | @Override
24 | public Date getDate() {
25 | return eventDate;
26 | }
27 |
28 | public OrderEvent(OrderRequestDto orderRequestDto, OrderStatus orderStatus) {
29 | this.orderRequestDto = orderRequestDto;
30 | this.orderStatus = orderStatus;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/common-dtos/src/main/java/com/javatechie/saga/commons/event/OrderStatus.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.commons.event;
2 |
3 | public enum OrderStatus {
4 |
5 | ORDER_CREATED,ORDER_COMPLETED,ORDER_CANCELLED
6 | }
7 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/common-dtos/src/main/java/com/javatechie/saga/commons/event/PaymentEvent.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.commons.event;
2 |
3 | import com.javatechie.saga.commons.dto.PaymentRequestDto;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | import java.util.Date;
8 | import java.util.UUID;
9 | @NoArgsConstructor
10 | @Data
11 | public class PaymentEvent implements Event{
12 |
13 | private UUID eventId=UUID.randomUUID();
14 | private Date eventDate=new Date();
15 | private PaymentRequestDto paymentRequestDto;
16 | private PaymentStatus paymentStatus;
17 |
18 | @Override
19 | public UUID getEventId() {
20 | return eventId;
21 | }
22 |
23 | @Override
24 | public Date getDate() {
25 | return eventDate;
26 | }
27 |
28 | public PaymentEvent(PaymentRequestDto paymentRequestDto, PaymentStatus paymentStatus) {
29 | this.paymentRequestDto = paymentRequestDto;
30 | this.paymentStatus = paymentStatus;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/common-dtos/src/main/java/com/javatechie/saga/commons/event/PaymentStatus.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.commons.event;
2 |
3 | public enum PaymentStatus {
4 |
5 | PAYMENT_COMPLETED,PAYMENT_FAILED
6 | }
7 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/order-service/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | saga-choreography-pattern
7 | com.javatechie
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | order-service
13 |
14 |
15 |
16 | org.springframework.boot
17 | spring-boot-starter-data-jpa
18 |
19 |
20 | org.springframework.boot
21 | spring-boot-starter-webflux
22 |
23 |
24 | org.springframework.cloud
25 | spring-cloud-stream
26 |
27 |
28 | org.springframework.cloud
29 | spring-cloud-stream-binder-kafka
30 |
31 |
32 | org.springframework.kafka
33 | spring-kafka
34 |
35 |
36 |
37 | mysql
38 | mysql-connector-java
39 | runtime
40 |
41 |
42 | org.projectlombok
43 | lombok
44 | true
45 |
46 |
47 | com.javatechie
48 | common-dtos
49 | 0.0.1-SNAPSHOT
50 |
51 |
52 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/order-service/src/main/java/com/javatechie/saga/order/OrderServiceApplication.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.order;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class OrderServiceApplication {
8 |
9 |
10 | public static void main(String[] args) {
11 | SpringApplication.run(OrderServiceApplication.class);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/order-service/src/main/java/com/javatechie/saga/order/config/EventConsumerConfig.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.order.config;
2 |
3 | import com.javatechie.saga.commons.event.PaymentEvent;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 |
8 | import java.util.function.Consumer;
9 |
10 | @Configuration
11 | public class EventConsumerConfig {
12 |
13 | @Autowired
14 | private OrderStatusUpdateHandler handler;
15 |
16 |
17 | @Bean
18 | public Consumer paymentEventConsumer(){
19 | //listen payment-event-topic
20 | //will check payment status
21 | //if payment status completed -> complete the order
22 | //if payment status failed -> cancel the order
23 | return (payment)-> handler.updateOrder(payment.getPaymentRequestDto().getOrderId(),po->{
24 | po.setPaymentStatus(payment.getPaymentStatus());
25 | });
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/order-service/src/main/java/com/javatechie/saga/order/config/OrderPublisherConfig.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.order.config;
2 |
3 | import com.javatechie.saga.commons.event.OrderEvent;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 | import reactor.core.publisher.Flux;
7 | import reactor.core.publisher.Sinks;
8 |
9 | import java.util.function.Supplier;
10 |
11 | @Configuration
12 | public class OrderPublisherConfig {
13 |
14 | @Bean
15 | public Sinks.Many orderSinks(){
16 | return Sinks.many().multicast().onBackpressureBuffer();
17 | }
18 |
19 | @Bean
20 | public Supplier> orderSupplier(Sinks.Many sinks){
21 | return sinks::asFlux;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/order-service/src/main/java/com/javatechie/saga/order/config/OrderStatusUpdateHandler.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.order.config;
2 |
3 | import com.javatechie.saga.commons.dto.OrderRequestDto;
4 | import com.javatechie.saga.commons.event.OrderStatus;
5 | import com.javatechie.saga.commons.event.PaymentStatus;
6 | import com.javatechie.saga.order.entity.PurchaseOrder;
7 | import com.javatechie.saga.order.repository.OrderRepository;
8 | import com.javatechie.saga.order.service.OrderStatusPublisher;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.context.annotation.Configuration;
11 | import org.springframework.transaction.annotation.Transactional;
12 |
13 | import java.util.function.Consumer;
14 |
15 | @Configuration
16 | public class OrderStatusUpdateHandler {
17 |
18 | @Autowired
19 | private OrderRepository repository;
20 |
21 | @Autowired
22 | private OrderStatusPublisher publisher;
23 |
24 | @Transactional
25 | public void updateOrder(int id, Consumer consumer) {
26 | repository.findById(id).ifPresent(consumer.andThen(this::updateOrder));
27 | }
28 |
29 | private void updateOrder(PurchaseOrder purchaseOrder) {
30 | boolean isPaymentComplete = PaymentStatus.PAYMENT_COMPLETED.equals(purchaseOrder.getPaymentStatus());
31 | OrderStatus orderStatus = isPaymentComplete ? OrderStatus.ORDER_COMPLETED : OrderStatus.ORDER_CANCELLED;
32 | purchaseOrder.setOrderStatus(orderStatus);
33 | if (!isPaymentComplete) {
34 | publisher.publishOrderEvent(convertEntityToDto(purchaseOrder), orderStatus);
35 | }
36 | }
37 |
38 | public OrderRequestDto convertEntityToDto(PurchaseOrder purchaseOrder) {
39 | OrderRequestDto orderRequestDto = new OrderRequestDto();
40 | orderRequestDto.setOrderId(purchaseOrder.getId());
41 | orderRequestDto.setUserId(purchaseOrder.getUserId());
42 | orderRequestDto.setAmount(purchaseOrder.getPrice());
43 | orderRequestDto.setProductId(purchaseOrder.getProductId());
44 | return orderRequestDto;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/order-service/src/main/java/com/javatechie/saga/order/controller/OrderController.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.order.controller;
2 |
3 | import com.javatechie.saga.commons.dto.OrderRequestDto;
4 | import com.javatechie.saga.order.entity.PurchaseOrder;
5 | import com.javatechie.saga.order.service.OrderService;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.web.bind.annotation.*;
8 |
9 | import java.util.List;
10 |
11 | @RestController
12 | @RequestMapping("/order")
13 | public class OrderController {
14 |
15 | @Autowired
16 | private OrderService orderService;
17 |
18 |
19 | @PostMapping("/create")
20 | public PurchaseOrder createOrder(@RequestBody OrderRequestDto orderRequestDto){
21 | return orderService.createOrder(orderRequestDto);
22 | }
23 |
24 | @GetMapping
25 | public List getOrders(){
26 | return orderService.getAllOrders();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/order-service/src/main/java/com/javatechie/saga/order/entity/PurchaseOrder.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.order.entity;
2 |
3 | import com.javatechie.saga.commons.event.OrderStatus;
4 | import com.javatechie.saga.commons.event.PaymentStatus;
5 | import lombok.AllArgsConstructor;
6 | import lombok.Data;
7 | import lombok.NoArgsConstructor;
8 |
9 | import javax.persistence.*;
10 |
11 | @Entity
12 | @Table(name = "PURCHASE_ORDER_TBL")
13 | @Data
14 | @AllArgsConstructor
15 | @NoArgsConstructor
16 | public class PurchaseOrder {
17 | @Id
18 | @GeneratedValue
19 | private Integer id;
20 | private Integer userId;
21 | private Integer productId;
22 | private Integer price;
23 | @Enumerated(EnumType.STRING)
24 | private OrderStatus orderStatus;
25 | @Enumerated(EnumType.STRING)
26 | private PaymentStatus paymentStatus;
27 | }
28 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/order-service/src/main/java/com/javatechie/saga/order/repository/OrderRepository.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.order.repository;
2 |
3 | import com.javatechie.saga.order.entity.PurchaseOrder;
4 | import org.springframework.data.jpa.repository.JpaRepository;
5 |
6 | public interface OrderRepository extends JpaRepository {
7 | }
8 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/order-service/src/main/java/com/javatechie/saga/order/service/OrderService.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.order.service;
2 |
3 | import com.javatechie.saga.commons.dto.OrderRequestDto;
4 | import com.javatechie.saga.commons.event.OrderStatus;
5 | import com.javatechie.saga.order.entity.PurchaseOrder;
6 | import com.javatechie.saga.order.repository.OrderRepository;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.stereotype.Service;
9 | import org.springframework.transaction.annotation.Transactional;
10 |
11 | import java.util.List;
12 | @Service
13 | public class OrderService {
14 |
15 | @Autowired
16 | private OrderRepository orderRepository;
17 |
18 | @Autowired
19 | private OrderStatusPublisher orderStatusPublisher;
20 |
21 | @Transactional
22 | public PurchaseOrder createOrder(OrderRequestDto orderRequestDto) {
23 | PurchaseOrder order = orderRepository.save(convertDtoToEntity(orderRequestDto));
24 | orderRequestDto.setOrderId(order.getId());
25 | //produce kafka event with status ORDER_CREATED
26 | orderStatusPublisher.publishOrderEvent(orderRequestDto, OrderStatus.ORDER_CREATED);
27 | return order;
28 | }
29 |
30 | public List getAllOrders(){
31 | return orderRepository.findAll();
32 | }
33 |
34 |
35 | private PurchaseOrder convertDtoToEntity(OrderRequestDto dto) {
36 | PurchaseOrder purchaseOrder = new PurchaseOrder();
37 | purchaseOrder.setProductId(dto.getProductId());
38 | purchaseOrder.setUserId(dto.getUserId());
39 | purchaseOrder.setOrderStatus(OrderStatus.ORDER_CREATED);
40 | purchaseOrder.setPrice(dto.getAmount());
41 | return purchaseOrder;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/order-service/src/main/java/com/javatechie/saga/order/service/OrderStatusPublisher.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.order.service;
2 |
3 | import com.javatechie.saga.commons.dto.OrderRequestDto;
4 | import com.javatechie.saga.commons.event.OrderEvent;
5 | import com.javatechie.saga.commons.event.OrderStatus;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.stereotype.Service;
8 | import reactor.core.publisher.Sinks;
9 |
10 | @Service
11 | public class OrderStatusPublisher {
12 |
13 | @Autowired
14 | private Sinks.Many orderSinks;
15 |
16 |
17 | public void publishOrderEvent(OrderRequestDto orderRequestDto, OrderStatus orderStatus){
18 | OrderEvent orderEvent=new OrderEvent(orderRequestDto,orderStatus);
19 | orderSinks.tryEmitNext(orderEvent);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/order-service/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
2 | spring.datasource.url = jdbc:mysql://localhost:3306/javatechie
3 | spring.datasource.username = root
4 | spring.datasource.password = Password
5 | spring.jpa.show-sql = true
6 | spring.jpa.hibernate.ddl-auto = update
7 | spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
8 | spring.jpa.properties.hibernate.format_sql=true
--------------------------------------------------------------------------------
/saga-choreography-pattern/order-service/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | spring:
2 | cloud:
3 | stream:
4 | function:
5 | definition : orderSupplier;paymentEventConsumer
6 | bindings:
7 | orderSupplier-out-0:
8 | destination: order-event
9 | paymentEventConsumer-in-0 :
10 | destination: payment-event
11 |
12 |
13 |
14 | server:
15 | port: 8081
--------------------------------------------------------------------------------
/saga-choreography-pattern/payment-service/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | saga-choreography-pattern
7 | com.javatechie
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | payment-service
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-data-jpa
17 |
18 |
19 | org.springframework.boot
20 | spring-boot-starter-webflux
21 |
22 |
23 | org.springframework.cloud
24 | spring-cloud-stream
25 |
26 |
27 | org.springframework.cloud
28 | spring-cloud-stream-binder-kafka
29 |
30 |
31 | org.springframework.kafka
32 | spring-kafka
33 |
34 |
35 |
36 | mysql
37 | mysql-connector-java
38 | runtime
39 |
40 |
41 | org.projectlombok
42 | lombok
43 | true
44 |
45 |
46 | com.javatechie
47 | common-dtos
48 | 0.0.1-SNAPSHOT
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/payment-service/src/main/java/com/javatechie/saga/payment/PaymentServiceApplication.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.payment;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class PaymentServiceApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(PaymentServiceApplication.class);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/payment-service/src/main/java/com/javatechie/saga/payment/config/PaymentConsumerConfig.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.payment.config;
2 |
3 | import com.javatechie.saga.commons.event.OrderEvent;
4 | import com.javatechie.saga.commons.event.OrderStatus;
5 | import com.javatechie.saga.commons.event.PaymentEvent;
6 | import com.javatechie.saga.payment.service.PaymentService;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.context.annotation.Bean;
9 | import org.springframework.context.annotation.Configuration;
10 | import reactor.core.publisher.Flux;
11 | import reactor.core.publisher.Mono;
12 |
13 | import java.util.function.Function;
14 |
15 | @Configuration
16 | public class PaymentConsumerConfig {
17 |
18 | @Autowired
19 | private PaymentService paymentService;
20 |
21 | @Bean
22 | public Function, Flux> paymentProcessor() {
23 | return orderEventFlux -> orderEventFlux.flatMap(this::processPayment);
24 | }
25 |
26 | private Mono processPayment(OrderEvent orderEvent) {
27 | // get the user id
28 | // check the balance availability
29 | // if balance sufficient -> Payment completed and deduct amount from DB
30 | // if payment not sufficient -> cancel order event and update the amount in DB
31 | if(OrderStatus.ORDER_CREATED.equals(orderEvent.getOrderStatus())){
32 | return Mono.fromSupplier(()->this.paymentService.newOrderEvent(orderEvent));
33 | }else{
34 | return Mono.fromRunnable(()->this.paymentService.cancelOrderEvent(orderEvent));
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/payment-service/src/main/java/com/javatechie/saga/payment/entity/UserBalance.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.payment.entity;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | import javax.persistence.Entity;
8 | import javax.persistence.Id;
9 |
10 | @Entity
11 | @Data
12 | @AllArgsConstructor
13 | @NoArgsConstructor
14 | public class UserBalance {
15 | @Id
16 | private int userId;
17 | private int price;
18 | }
19 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/payment-service/src/main/java/com/javatechie/saga/payment/entity/UserTransaction.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.payment.entity;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | import javax.persistence.Entity;
8 | import javax.persistence.Id;
9 |
10 | @Entity
11 | @Data
12 | @AllArgsConstructor
13 | @NoArgsConstructor
14 | public class UserTransaction {
15 | @Id
16 | private Integer orderId;
17 | private int userId;
18 | private int amount;
19 | }
20 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/payment-service/src/main/java/com/javatechie/saga/payment/repository/UserBalanceRepository.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.payment.repository;
2 |
3 | import com.javatechie.saga.payment.entity.UserBalance;
4 | import org.springframework.data.jpa.repository.JpaRepository;
5 |
6 | public interface UserBalanceRepository extends JpaRepository {
7 | }
8 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/payment-service/src/main/java/com/javatechie/saga/payment/repository/UserTransactionRepository.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.payment.repository;
2 |
3 | import com.javatechie.saga.payment.entity.UserTransaction;
4 | import org.springframework.data.jpa.repository.JpaRepository;
5 |
6 | public interface UserTransactionRepository extends JpaRepository {
7 | }
8 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/payment-service/src/main/java/com/javatechie/saga/payment/service/PaymentService.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.saga.payment.service;
2 |
3 | import com.javatechie.saga.commons.dto.OrderRequestDto;
4 | import com.javatechie.saga.commons.dto.PaymentRequestDto;
5 | import com.javatechie.saga.commons.event.OrderEvent;
6 | import com.javatechie.saga.commons.event.PaymentEvent;
7 | import com.javatechie.saga.commons.event.PaymentStatus;
8 | import com.javatechie.saga.payment.entity.UserBalance;
9 | import com.javatechie.saga.payment.entity.UserTransaction;
10 | import com.javatechie.saga.payment.repository.UserBalanceRepository;
11 | import com.javatechie.saga.payment.repository.UserTransactionRepository;
12 | import org.springframework.beans.factory.annotation.Autowired;
13 | import org.springframework.stereotype.Service;
14 | import org.springframework.transaction.annotation.Transactional;
15 |
16 | import javax.annotation.PostConstruct;
17 | import java.util.stream.Collectors;
18 | import java.util.stream.Stream;
19 |
20 | @Service
21 | public class PaymentService {
22 |
23 | @Autowired
24 | private UserBalanceRepository userBalanceRepository;
25 | @Autowired
26 | private UserTransactionRepository userTransactionRepository;
27 |
28 | @PostConstruct
29 | public void initUserBalanceInDB() {
30 | userBalanceRepository.saveAll(Stream.of(new UserBalance(101, 5000),
31 | new UserBalance(102, 3000),
32 | new UserBalance(103, 4200),
33 | new UserBalance(104, 20000),
34 | new UserBalance(105, 999)).collect(Collectors.toList()));
35 | }
36 |
37 | /**
38 | * // get the user id
39 | * // check the balance availability
40 | * // if balance sufficient -> Payment completed and deduct amount from DB
41 | * // if payment not sufficient -> cancel order event and update the amount in DB
42 | **/
43 | @Transactional
44 | public PaymentEvent newOrderEvent(OrderEvent orderEvent) {
45 | OrderRequestDto orderRequestDto = orderEvent.getOrderRequestDto();
46 |
47 | PaymentRequestDto paymentRequestDto = new PaymentRequestDto(orderRequestDto.getOrderId(),
48 | orderRequestDto.getUserId(), orderRequestDto.getAmount());
49 |
50 | return userBalanceRepository.findById(orderRequestDto.getUserId())
51 | .filter(ub -> ub.getPrice() > orderRequestDto.getAmount())
52 | .map(ub -> {
53 | ub.setPrice(ub.getPrice() - orderRequestDto.getAmount());
54 | userTransactionRepository.save(new UserTransaction(orderRequestDto.getOrderId(), orderRequestDto.getUserId(), orderRequestDto.getAmount()));
55 | return new PaymentEvent(paymentRequestDto, PaymentStatus.PAYMENT_COMPLETED);
56 | }).orElse(new PaymentEvent(paymentRequestDto, PaymentStatus.PAYMENT_FAILED));
57 |
58 | }
59 |
60 | @Transactional
61 | public void cancelOrderEvent(OrderEvent orderEvent) {
62 |
63 | userTransactionRepository.findById(orderEvent.getOrderRequestDto().getOrderId())
64 | .ifPresent(ut->{
65 | userTransactionRepository.delete(ut);
66 | userTransactionRepository.findById(ut.getUserId())
67 | .ifPresent(ub->ub.setAmount(ub.getAmount()+ut.getAmount()));
68 | });
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/payment-service/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
2 | spring.datasource.url = jdbc:mysql://localhost:3306/javatechie
3 | spring.datasource.username = root
4 | spring.datasource.password = Password
5 | spring.jpa.show-sql = true
6 | spring.jpa.hibernate.ddl-auto = update
7 | spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
8 | spring.jpa.properties.hibernate.format_sql=true
--------------------------------------------------------------------------------
/saga-choreography-pattern/payment-service/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | spring:
2 | cloud:
3 | stream:
4 | function:
5 | definition : paymentProcessor
6 | bindings:
7 | paymentProcessor-in-0 :
8 | destination: order-event
9 | paymentProcessor-out-0:
10 | destination: payment-event
11 |
12 |
13 | server:
14 | port: 8082
--------------------------------------------------------------------------------
/saga-choreography-pattern/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | pom
6 |
7 | order-service
8 | payment-service
9 | common-dtos
10 |
11 |
12 | org.springframework.boot
13 | spring-boot-starter-parent
14 | 2.5.4
15 |
16 |
17 | com.javatechie
18 | saga-choreography-pattern
19 | 0.0.1-SNAPSHOT
20 | saga-choreography-pattern
21 | Demo project for Spring Boot
22 |
23 | 1.8
24 | 2020.0.3
25 |
26 |
27 |
28 |
29 | org.springframework.boot
30 | spring-boot-starter-test
31 | test
32 |
33 |
34 | io.projectreactor
35 | reactor-test
36 | test
37 |
38 |
39 | org.springframework.cloud
40 | spring-cloud-stream
41 | test
42 | test-binder
43 | test-jar
44 |
45 |
46 | org.springframework.kafka
47 | spring-kafka-test
48 | test
49 |
50 |
51 |
52 |
53 |
54 | org.springframework.cloud
55 | spring-cloud-dependencies
56 | ${spring-cloud.version}
57 | pom
58 | import
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | org.springframework.boot
67 | spring-boot-maven-plugin
68 |
69 |
70 |
71 | org.projectlombok
72 | lombok
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/saga-choreography-pattern/saga-choreography-pattern.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
--------------------------------------------------------------------------------