From d7939edd0ad2ebdffeb64ea6dcf69768209d8e7d Mon Sep 17 00:00:00 2001 From: Ali CANLI Date: Fri, 15 Jul 2022 12:01:28 +0300 Subject: [PATCH 1/3] Create OutboxTable and enum types. --- infrastructure/outbox/pom.xml | 16 +++++ .../order/system/outbox/OutboxStatus.java | 5 ++ infrastructure/pom.xml | 1 + .../food/order/system/saga/SagaStatus.java | 5 ++ .../src/main/resources/application.yml | 2 + .../src/main/resources/init-schema.sql | 60 ++++++++++++++++++- pom.xml | 6 ++ 7 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 infrastructure/outbox/pom.xml create mode 100644 infrastructure/outbox/src/main/java/com/food/order/system/outbox/OutboxStatus.java create mode 100644 infrastructure/saga/src/main/java/com/food/order/system/saga/SagaStatus.java diff --git a/infrastructure/outbox/pom.xml b/infrastructure/outbox/pom.xml new file mode 100644 index 0000000..da2aaff --- /dev/null +++ b/infrastructure/outbox/pom.xml @@ -0,0 +1,16 @@ + + + + infrastructure + com.food.order + 1.0-SNAPSHOT + + 4.0.0 + + outbox + + + + \ No newline at end of file diff --git a/infrastructure/outbox/src/main/java/com/food/order/system/outbox/OutboxStatus.java b/infrastructure/outbox/src/main/java/com/food/order/system/outbox/OutboxStatus.java new file mode 100644 index 0000000..218fffc --- /dev/null +++ b/infrastructure/outbox/src/main/java/com/food/order/system/outbox/OutboxStatus.java @@ -0,0 +1,5 @@ +package com.food.order.system.outbox; + +public enum OutboxStatus { + STARTED,COMPLETED,FAILED +} diff --git a/infrastructure/pom.xml b/infrastructure/pom.xml index dc4487f..09357cc 100644 --- a/infrastructure/pom.xml +++ b/infrastructure/pom.xml @@ -13,6 +13,7 @@ pom saga + outbox diff --git a/infrastructure/saga/src/main/java/com/food/order/system/saga/SagaStatus.java b/infrastructure/saga/src/main/java/com/food/order/system/saga/SagaStatus.java new file mode 100644 index 0000000..8ac6dcc --- /dev/null +++ b/infrastructure/saga/src/main/java/com/food/order/system/saga/SagaStatus.java @@ -0,0 +1,5 @@ +package com.food.order.system.saga; + +public enum SagaStatus { + STARTED,FAILED,SUCCEEDED,PROCESSING, COMPENSATING,COMPENSATED +} diff --git a/order-service/order-container/src/main/resources/application.yml b/order-service/order-container/src/main/resources/application.yml index fc6e593..20f2d05 100644 --- a/order-service/order-container/src/main/resources/application.yml +++ b/order-service/order-container/src/main/resources/application.yml @@ -9,6 +9,8 @@ order-service: payment-response-topic-name: payment-response-value restaurant-approval-request-topic-name: restaurant-approval-request-value restaurant-approval-response-topic-name: restaurant-approval-response-value + outbox-scheduler-fixed-rate: 10000 + outbox-scheduler-initial-delay: 10000 spring: jpa: diff --git a/order-service/order-container/src/main/resources/init-schema.sql b/order-service/order-container/src/main/resources/init-schema.sql index b4b72fb..3331922 100644 --- a/order-service/order-container/src/main/resources/init-schema.sql +++ b/order-service/order-container/src/main/resources/init-schema.sql @@ -58,4 +58,62 @@ ALTER TABLE "order".order_address REFERENCES "order".orders (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE - NOT VALID; \ No newline at end of file + NOT VALID; + +DROP TYPE IF EXISTS saga_status; +CREATE TYPE saga_status AS ENUM ('STARTED','FAILED','SUCCEEDED','PROCESSING', 'COMPENSATING','COMPENSATED'); + +DROP TYPE IF EXISTS outbox_status; +CREATE TYPE outbox_status AS ENUM ('STARTED','COMPLETED','FAILED'); + + +DROP TABLE IF EXISTS "order".payment_outbox CASCADE; + +CREATE TABLE "order".payment_outbox +( + id uuid NOT NULL, + saga_id uuid NOT NULL, + created_at TIMESTAMP WITH TIME ZONE NOT NULL, + processed_at TIMESTAMP WITH TIME ZONE, + type character varying COLLATE pg_catalog."default" NOT NULL, + payload jsonb NOT NULL, + outbox_status outbox_status NOT NULL, + saga_status saga_status NOT NULL, + order_status order_status NOT NULL, + version integer NOT NULL, + CONSTRAINT payment_outbox_pkey PRIMARY KEY (id) +); + +CREATE INDEX "payment_outbox_saga_status" + ON "order".payment_outbox + (type, outbox_status, saga_status); + +CREATE UNIQUE INDEX "payment_outbox_saga_id" + ON "order".payment_outbox + (type, saga_id, saga_status); + + +DROP TABLE IF EXISTS "order".restaurant_approval_outbox CASCADE; + +CREATE TABLE "order".restaurant_approval_outbox +( + id uuid NOT NULL, + saga_id uuid NOT NULL, + created_at TIMESTAMP WITH TIME ZONE NOT NULL, + processed_at TIMESTAMP WITH TIME ZONE, + type character varying COLLATE pg_catalog."default" NOT NULL, + payload jsonb NOT NULL, + outbox_status outbox_status NOT NULL, + saga_status saga_status NOT NULL, + order_status order_status NOT NULL, + version integer NOT NULL, + CONSTRAINT restaurant_approval_outbox_pkey PRIMARY KEY (id) +); + +CREATE INDEX "restaurant_approval_outbox_saga_status" + ON "order".restaurant_approval_outbox + (type, outbox_status, saga_status); + +CREATE UNIQUE INDEX "restaurant_approval_outbox_saga_id" + ON "order".restaurant_approval_outbox + (type, saga_id, saga_status); diff --git a/pom.xml b/pom.xml index 1b6ef04..1a2cb8f 100644 --- a/pom.xml +++ b/pom.xml @@ -43,6 +43,12 @@ ${project.version} + + com.food.order + outbox + ${project.version} + + com.food.order payment-domain From d49b533f7e45e4c78e41f15e99fae9d55e87006c Mon Sep 17 00:00:00 2001 From: Ali CANLI Date: Fri, 15 Jul 2022 20:37:22 +0300 Subject: [PATCH 2/3] Outbox Message and Scheduler class implemented part - 1. --- infrastructure/outbox/pom.xml | 6 ++ .../order/system/outbox/OutboxScheduler.java | 5 + .../system/outbox/config/SchedulerConfig.java | 12 +++ .../order/system/outbox/order/SagaConst.java | 12 +++ .../order-application-service/pom.xml | 10 ++ .../sysyem/OrderCreateCommandHandler.java | 27 ++++- .../sysyem/helper/OrderCreateHelper.java | 4 +- .../order/sysyem/helper/OrderSagaHelper.java | 12 +++ .../order/sysyem/mapper/OrderDataMapper.java | 17 ++- .../approval/OrderApprovalEventPayload.java | 36 +++++++ .../approval/OrderApprovalOutboxMessage.java | 42 ++++++++ .../model/approval/OrderApprovalProduct.java | 19 ++++ .../payment/OrderPaymentEventPayload.java | 27 +++++ .../payment/OrderPaymentOutboxMessage.java | 39 +++++++ .../approval/ApprovalOutboxHelper.java | 66 ++++++++++++ .../RestaurantApprovalOutboxCleaner.java | 51 +++++++++ .../RestaurantApprovalOutboxMessage.java | 64 +++++++++++ .../payment/PaymentOutboxCleaner.java | 51 +++++++++ .../payment/PaymentOutboxHelper.java | 100 ++++++++++++++++++ .../payment/PaymentOutboxScheduler.java | 63 +++++++++++ ...ncelledPaymentRequestMessagePublisher.java | 8 -- ...CreatedPaymentRequestMessagePublisher.java | 8 -- .../PaymentRequestMessagePublisher.java | 13 +++ ...PaidRestaurantRequestMessagePublisher.java | 8 -- ...aurantApprovalRequestMessagePublisher.java | 13 +++ .../repository/ApprovalOutboxRepository.java | 25 +++++ .../repository/PaymentOutboxRepository.java | 28 +++++ .../order/sysyem/saga/OrderApprovalSaga.java | 8 +- .../order/sysyem/saga/OrderPaymentSaga.java | 1 - .../CancelOrderKafkaMessagePublisher.java | 4 +- .../CreateOrderKafkaMessagePublisher.java | 4 +- .../kafka/PayOrderKafkaMessagePublisher.java | 4 +- 32 files changed, 743 insertions(+), 44 deletions(-) create mode 100644 infrastructure/outbox/src/main/java/com/food/order/system/outbox/OutboxScheduler.java create mode 100644 infrastructure/outbox/src/main/java/com/food/order/system/outbox/config/SchedulerConfig.java create mode 100644 infrastructure/outbox/src/main/java/com/food/order/system/outbox/order/SagaConst.java create mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/approval/OrderApprovalEventPayload.java create mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/approval/OrderApprovalOutboxMessage.java create mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/approval/OrderApprovalProduct.java create mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/payment/OrderPaymentEventPayload.java create mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/payment/OrderPaymentOutboxMessage.java create mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/approval/ApprovalOutboxHelper.java create mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/approval/RestaurantApprovalOutboxCleaner.java create mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/approval/RestaurantApprovalOutboxMessage.java create mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/payment/PaymentOutboxCleaner.java create mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/payment/PaymentOutboxHelper.java create mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/payment/PaymentOutboxScheduler.java delete mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/payment/OrderCancelledPaymentRequestMessagePublisher.java delete mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/payment/OrderCreatedPaymentRequestMessagePublisher.java create mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/payment/PaymentRequestMessagePublisher.java delete mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/restaurantapproval/OrderPaidRestaurantRequestMessagePublisher.java create mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/restaurantapproval/RestaurantApprovalRequestMessagePublisher.java create mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/repository/ApprovalOutboxRepository.java create mode 100644 order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/repository/PaymentOutboxRepository.java diff --git a/infrastructure/outbox/pom.xml b/infrastructure/outbox/pom.xml index da2aaff..b7c309e 100644 --- a/infrastructure/outbox/pom.xml +++ b/infrastructure/outbox/pom.xml @@ -12,5 +12,11 @@ outbox + + + org.springframework.boot + spring-boot-starter + + \ No newline at end of file diff --git a/infrastructure/outbox/src/main/java/com/food/order/system/outbox/OutboxScheduler.java b/infrastructure/outbox/src/main/java/com/food/order/system/outbox/OutboxScheduler.java new file mode 100644 index 0000000..f37e0f5 --- /dev/null +++ b/infrastructure/outbox/src/main/java/com/food/order/system/outbox/OutboxScheduler.java @@ -0,0 +1,5 @@ +package com.food.order.system.outbox; + +public interface OutboxScheduler { + void processOutboxMessage(); +} diff --git a/infrastructure/outbox/src/main/java/com/food/order/system/outbox/config/SchedulerConfig.java b/infrastructure/outbox/src/main/java/com/food/order/system/outbox/config/SchedulerConfig.java new file mode 100644 index 0000000..43adacd --- /dev/null +++ b/infrastructure/outbox/src/main/java/com/food/order/system/outbox/config/SchedulerConfig.java @@ -0,0 +1,12 @@ +package com.food.order.system.outbox.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; + +@Configuration +@EnableScheduling +public class SchedulerConfig { + + + +} diff --git a/infrastructure/outbox/src/main/java/com/food/order/system/outbox/order/SagaConst.java b/infrastructure/outbox/src/main/java/com/food/order/system/outbox/order/SagaConst.java new file mode 100644 index 0000000..3ec7b83 --- /dev/null +++ b/infrastructure/outbox/src/main/java/com/food/order/system/outbox/order/SagaConst.java @@ -0,0 +1,12 @@ +package com.food.order.system.outbox.order; + +public class SagaConst { + + private SagaConst() { + } + + public static final String ORDER_PROCESSING_SAGA = "OrderProcessingSaga"; + + + +} diff --git a/order-service/order-domain/order-application-service/pom.xml b/order-service/order-domain/order-application-service/pom.xml index 69a718c..8a4fc93 100644 --- a/order-service/order-domain/order-application-service/pom.xml +++ b/order-service/order-domain/order-application-service/pom.xml @@ -31,6 +31,16 @@ spring-boot-starter-validation + + com.food.order + outbox + + + + org.springframework.boot + spring-boot-starter-json + + org.springframework spring-tx diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/OrderCreateCommandHandler.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/OrderCreateCommandHandler.java index 0e0a51c..5bca96a 100644 --- a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/OrderCreateCommandHandler.java +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/OrderCreateCommandHandler.java @@ -1,13 +1,18 @@ package com.food.order.sysyem; +import com.food.order.system.outbox.OutboxStatus; import com.food.order.sysyem.dto.create.CreateOrderCommand; import com.food.order.sysyem.dto.create.CreateOrderResponse; import com.food.order.sysyem.helper.OrderCreateHelper; +import com.food.order.sysyem.helper.OrderSagaHelper; import com.food.order.sysyem.mapper.OrderDataMapper; -import com.food.order.sysyem.ports.output.message.publisher.payment.OrderCreatedPaymentRequestMessagePublisher; +import com.food.order.sysyem.outbox.scheduler.payment.PaymentOutboxHelper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.UUID; @Component @Slf4j @@ -17,14 +22,28 @@ public class OrderCreateCommandHandler { private final OrderCreateHelper orderCreateHelper; private final OrderDataMapper orderDataMapper; - private final OrderCreatedPaymentRequestMessagePublisher orderCreatedPaymentRequestMessagePublisher; + private final OrderSagaHelper orderSagaHelper; + private final PaymentOutboxHelper paymentOutboxHelper; + + @Transactional public CreateOrderResponse createOrder(CreateOrderCommand createOrderCommand) { var persistOrder = orderCreateHelper.persistOrder(createOrderCommand); log.info("createOrder with id: {}", persistOrder.getOrder().getId().getValue()); - orderCreatedPaymentRequestMessagePublisher.publish(persistOrder); - return orderDataMapper.orderToCreateOrderResponse(persistOrder.getOrder(),"Order created successfully"); + var response = orderDataMapper.orderToCreateOrderResponse(persistOrder.getOrder(),"Order created successfully"); + + paymentOutboxHelper.savePaymentOutboxMessage( + orderDataMapper.orderCreatedEventToOrderPaymentEventPayload(persistOrder), + persistOrder.getOrder().getStatus(), + orderSagaHelper.orderStatusToSagaStatus(persistOrder.getOrder().getStatus()), + OutboxStatus.STARTED, + UUID.randomUUID() + ); + + log.info("Returning CreateOrderResponse with order id : {}", persistOrder.getOrder().getId()); + + return response; } diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/helper/OrderCreateHelper.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/helper/OrderCreateHelper.java index fd1163e..7afb14a 100644 --- a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/helper/OrderCreateHelper.java +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/helper/OrderCreateHelper.java @@ -28,7 +28,6 @@ public class OrderCreateHelper { private final CustomerRepository customerRepository; private final RestaurantRepository restaurantRepository; private final OrderDataMapper orderDataMapper; - private final DomainEventPublisher publisher; @Transactional public OrderCreatedEvent persistOrder(CreateOrderCommand createOrderCommand) { @@ -36,7 +35,8 @@ public class OrderCreateHelper { checkCustomer(createOrderCommand.customerId()); Restaurant restaurant = checkRestaurant(createOrderCommand); var order = orderDataMapper.createOrderCommandToOrder(createOrderCommand); - var createdEventOrder = orderDomainService.validateAndInitiateOrder(order, restaurant,publisher); + var createdEventOrder = orderDomainService.validateAndInitiateOrder + (order, restaurant); saveOrder(order); log.info("Created Order Event : {}", createdEventOrder); return createdEventOrder; diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/helper/OrderSagaHelper.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/helper/OrderSagaHelper.java index 55eb54c..f0bc541 100644 --- a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/helper/OrderSagaHelper.java +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/helper/OrderSagaHelper.java @@ -2,7 +2,9 @@ package com.food.order.sysyem.helper; import com.food.order.system.domain.entity.Order; import com.food.order.system.domain.exception.OrderNotFoundException; +import com.food.order.system.saga.SagaStatus; import com.food.order.sysyem.ports.output.repository.OrderRepository; +import com.food.order.sysyem.valueobject.OrderStatus; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -23,5 +25,15 @@ public class OrderSagaHelper { orderRepository.save(order); } + public SagaStatus orderStatusToSagaStatus(OrderStatus orderStatus) { + return switch (orderStatus) { + case PAID -> SagaStatus.PROCESSING; + case APPROVED -> SagaStatus.SUCCEEDED; + case CANCELLING -> SagaStatus.COMPENSATING; + case CANCELLED -> SagaStatus.COMPENSATED; + default -> SagaStatus.STARTED; + }; + } + } diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/mapper/OrderDataMapper.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/mapper/OrderDataMapper.java index 10e023c..be99271 100644 --- a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/mapper/OrderDataMapper.java +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/mapper/OrderDataMapper.java @@ -4,15 +4,14 @@ import com.food.order.system.domain.entity.Order; import com.food.order.system.domain.entity.OrderItem; import com.food.order.system.domain.entity.Product; import com.food.order.system.domain.entity.Restaurant; +import com.food.order.system.domain.event.OrderCreatedEvent; import com.food.order.system.domain.valueobject.StreetAddress; import com.food.order.sysyem.dto.create.CreateOrderCommand; import com.food.order.sysyem.dto.create.CreateOrderResponse; import com.food.order.sysyem.dto.create.OrderAddress; import com.food.order.sysyem.dto.track.TrackOrderResponse; -import com.food.order.sysyem.valueobject.CustomerId; -import com.food.order.sysyem.valueobject.Money; -import com.food.order.sysyem.valueobject.ProductId; -import com.food.order.sysyem.valueobject.RestaurantId; +import com.food.order.sysyem.outbox.model.payment.OrderPaymentEventPayload; +import com.food.order.sysyem.valueobject.*; import org.springframework.stereotype.Component; import java.util.List; @@ -21,6 +20,16 @@ import java.util.UUID; @Component public class OrderDataMapper { + public OrderPaymentEventPayload orderCreatedEventToOrderPaymentEventPayload(OrderCreatedEvent order) { + return OrderPaymentEventPayload.builder() + .orderId(order.getOrder().getId().getValue().toString()) + .customerId(order.getOrder().getCustomerId().getValue().toString()) + .price(order.getOrder().getPrice().getAmount()) + .createdAt(order.getCreatedAt()) + .paymentOrderStatus(PaymentOrderStatus.PENDING.name()) + .build(); + } + public TrackOrderResponse orderToTrackOrderResponse(Order order) { return TrackOrderResponse.builder() diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/approval/OrderApprovalEventPayload.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/approval/OrderApprovalEventPayload.java new file mode 100644 index 0000000..832ca19 --- /dev/null +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/approval/OrderApprovalEventPayload.java @@ -0,0 +1,36 @@ +package com.food.order.sysyem.outbox.model.approval; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.math.BigDecimal; +import java.time.ZonedDateTime; +import java.util.List; + +@Getter +@Builder +@AllArgsConstructor +public class OrderApprovalEventPayload { + + @JsonProperty + private String orderId; + + @JsonProperty + private String restaurantId; + + @JsonProperty + private BigDecimal price; + + @JsonProperty + private ZonedDateTime createdAt; + + @JsonProperty + private String restaurantOrderStatus; + + @JsonProperty + private List products; + + +} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/approval/OrderApprovalOutboxMessage.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/approval/OrderApprovalOutboxMessage.java new file mode 100644 index 0000000..7d58527 --- /dev/null +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/approval/OrderApprovalOutboxMessage.java @@ -0,0 +1,42 @@ +package com.food.order.sysyem.outbox.model.approval; + +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.system.saga.SagaStatus; +import com.food.order.sysyem.valueobject.OrderStatus; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +import java.time.ZonedDateTime; +import java.util.UUID; + +@Getter +@Builder +@AllArgsConstructor +public class OrderApprovalOutboxMessage { + + private UUID id; + + private UUID sagaId; + + private ZonedDateTime createdAt; + + @Setter + private ZonedDateTime processedAt; + + private String type; + + private String payload; + + @Setter + private SagaStatus sagaStatus; + + @Setter + private OrderStatus orderStatus; + + @Setter + private OutboxStatus outboxStatus; + + private int version; +} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/approval/OrderApprovalProduct.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/approval/OrderApprovalProduct.java new file mode 100644 index 0000000..9c1524e --- /dev/null +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/approval/OrderApprovalProduct.java @@ -0,0 +1,19 @@ +package com.food.order.sysyem.outbox.model.approval; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor +public class OrderApprovalProduct { + + @JsonProperty + private String id; + @JsonProperty + private Integer quantity; + +} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/payment/OrderPaymentEventPayload.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/payment/OrderPaymentEventPayload.java new file mode 100644 index 0000000..d175728 --- /dev/null +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/payment/OrderPaymentEventPayload.java @@ -0,0 +1,27 @@ +package com.food.order.sysyem.outbox.model.payment; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.math.BigDecimal; +import java.time.ZonedDateTime; + +@Getter +@Builder +@AllArgsConstructor +public class OrderPaymentEventPayload { + + @JsonProperty + private String orderId; + @JsonProperty + private String customerId; + @JsonProperty + private BigDecimal price; + @JsonProperty + private ZonedDateTime createdAt; + @JsonProperty + private String paymentOrderStatus; + +} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/payment/OrderPaymentOutboxMessage.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/payment/OrderPaymentOutboxMessage.java new file mode 100644 index 0000000..4e3facd --- /dev/null +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/payment/OrderPaymentOutboxMessage.java @@ -0,0 +1,39 @@ +package com.food.order.sysyem.outbox.model.payment; + +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.system.saga.SagaStatus; +import com.food.order.sysyem.valueobject.OrderStatus; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +import java.time.ZonedDateTime; +import java.util.UUID; + +@Getter +@Builder +@AllArgsConstructor +public class OrderPaymentOutboxMessage { + private UUID id; + + private UUID sagaId; + + private ZonedDateTime createdAt; + + @Setter + private ZonedDateTime processedAt; + + private String type; + + private String payload; + @Setter + private SagaStatus sagaStatus; + @Setter + private OrderStatus orderStatus; + @Setter + private OutboxStatus outboxStatus; + private int version; + + +} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/approval/ApprovalOutboxHelper.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/approval/ApprovalOutboxHelper.java new file mode 100644 index 0000000..fbe42e8 --- /dev/null +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/approval/ApprovalOutboxHelper.java @@ -0,0 +1,66 @@ +package com.food.order.sysyem.outbox.scheduler.approval; + +import com.food.order.system.domain.exception.OrderDomainException; +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.system.saga.SagaStatus; +import com.food.order.sysyem.outbox.model.approval.OrderApprovalOutboxMessage; +import com.food.order.sysyem.ports.output.repository.ApprovalOutboxRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; + +import static com.food.order.system.outbox.order.SagaConst.ORDER_PROCESSING_SAGA; + +@Slf4j +@RequiredArgsConstructor +@Component +public class ApprovalOutboxHelper { + + private final ApprovalOutboxRepository approvalOutboxRepository; + + @Transactional(readOnly = true) + public Optional> getApprovalOutboxMessageByOutboxStatusAndSagaStatus + (OutboxStatus outboxStatus, SagaStatus... sagaStatus) { + return approvalOutboxRepository.findByTypeAndOutboxStatusAndSagaStatus( + ORDER_PROCESSING_SAGA + ,outboxStatus, + sagaStatus); + } + + + @Transactional(readOnly = true) + public Optional getApprovalOutboxMessageBySagaIdAndSagaStatus( + UUID sagaId, SagaStatus... sagaStatus) { + return approvalOutboxRepository.findByTypeAndSagaIdAndSagaStatus( + ORDER_PROCESSING_SAGA + ,sagaId, + sagaStatus); + } + + @Transactional + public void save(OrderApprovalOutboxMessage approvalOutboxMessage) { + var response = approvalOutboxRepository.save(approvalOutboxMessage); + if (Objects.isNull(response)) { + throw new OrderDomainException("Failed to save outbox message id : " + + approvalOutboxMessage.getId()); + } + log.info("Outbox message id : {} saved successfully", response.getId()); + } + + @Transactional + public void deleteApprovalOutboxMessageByOutboxStatusAndSagaStatus(OutboxStatus outboxStatus, + SagaStatus... sagaStatus) { + + approvalOutboxRepository.deleteByTypeAndOutboxStatusAndSagaStatus( + ORDER_PROCESSING_SAGA, + outboxStatus, + sagaStatus); + + } +} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/approval/RestaurantApprovalOutboxCleaner.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/approval/RestaurantApprovalOutboxCleaner.java new file mode 100644 index 0000000..c6c6273 --- /dev/null +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/approval/RestaurantApprovalOutboxCleaner.java @@ -0,0 +1,51 @@ +package com.food.order.sysyem.outbox.scheduler.approval; + +import com.food.order.system.domain.exception.OrderDomainException; +import com.food.order.system.outbox.OutboxScheduler; +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.system.saga.SagaStatus; +import com.food.order.sysyem.outbox.model.approval.OrderApprovalOutboxMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.Objects; +import java.util.stream.Collectors; + +@Slf4j +@Component +@RequiredArgsConstructor +public class RestaurantApprovalOutboxCleaner implements OutboxScheduler { + + private final ApprovalOutboxHelper approvalOutboxHelper; + + @Override + @Scheduled(cron = "@midnight") + public void processOutboxMessage() { + + var outboxMessageResponse = + approvalOutboxHelper.getApprovalOutboxMessageByOutboxStatusAndSagaStatus( + OutboxStatus.COMPLETED, + SagaStatus.SUCCEEDED, + SagaStatus.FAILED, + SagaStatus.COMPENSATING). + orElseThrow(() -> new OrderDomainException("No outbox message found for processing")); + + if (Objects.nonNull(outboxMessageResponse)) { + + log.info("Received {} OrderPaymentOutboxMessage for clean-up. The Payloads :{}", + outboxMessageResponse.size(), + outboxMessageResponse.stream().map(OrderApprovalOutboxMessage::getPayload) + .collect(Collectors.joining(","))); + approvalOutboxHelper.deleteApprovalOutboxMessageByOutboxStatusAndSagaStatus( + OutboxStatus.COMPLETED, + SagaStatus.SUCCEEDED, + SagaStatus.FAILED, + SagaStatus.COMPENSATING); + log.info("Clean-up completed ! DELETED LOG SIZE : {}", outboxMessageResponse.size()); + + } + + } +} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/approval/RestaurantApprovalOutboxMessage.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/approval/RestaurantApprovalOutboxMessage.java new file mode 100644 index 0000000..8d9d9f9 --- /dev/null +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/approval/RestaurantApprovalOutboxMessage.java @@ -0,0 +1,64 @@ +package com.food.order.sysyem.outbox.scheduler.approval; + +import com.food.order.system.domain.exception.OrderDomainException; +import com.food.order.system.outbox.OutboxScheduler; +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.system.saga.SagaStatus; +import com.food.order.sysyem.outbox.model.approval.OrderApprovalOutboxMessage; +import com.food.order.sysyem.ports.output.message.publisher.restaurantapproval.RestaurantApprovalRequestMessagePublisher; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Objects; +import java.util.stream.Collectors; + +@Slf4j +@Component +@RequiredArgsConstructor +public class RestaurantApprovalOutboxMessage implements OutboxScheduler { + + private final ApprovalOutboxHelper approvalOutboxHelper; + private final RestaurantApprovalRequestMessagePublisher restaurantApprovalRequestMessagePublisher; + + @Override + @Transactional + @Scheduled(fixedDelayString = "${order-service.outbox-scheduler-fixed-rate}", + initialDelayString = "${order-service.outbox-scheduler-initial-delay}") + public void processOutboxMessage() { + + log.info("Processing outbox message STARTED !"); + + var outboxMessageResponse = + approvalOutboxHelper.getApprovalOutboxMessageByOutboxStatusAndSagaStatus( + OutboxStatus.STARTED, + SagaStatus.STARTED, + SagaStatus.COMPENSATING) + .orElseThrow( + () -> new OrderDomainException("No outbox message found for processing")); + + if (Objects.nonNull(outboxMessageResponse) && outboxMessageResponse.size() > 0) { + + log.info("Received {} OrderPaymentOutboxMessage with ids : {} , sending message bus !" , + outboxMessageResponse.size(), + outboxMessageResponse.stream().map(orderPaymentOutboxMessage -> orderPaymentOutboxMessage.getId().toString()) + .collect(Collectors.joining(","))); + outboxMessageResponse.forEach(orderPaymentOutboxMessage -> { + restaurantApprovalRequestMessagePublisher.publish + (orderPaymentOutboxMessage,this::updateOutboxStatus); + }); + log.info("Processing outbox message completed ! "); + } + + } + + + private void updateOutboxStatus(OrderApprovalOutboxMessage orderPaymentOutboxMessage, + OutboxStatus outboxStatus) { + orderPaymentOutboxMessage.setOutboxStatus(outboxStatus); + approvalOutboxHelper.save(orderPaymentOutboxMessage); + log.info("Outbox message id : {} updated successfully", orderPaymentOutboxMessage.getId()); + } +} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/payment/PaymentOutboxCleaner.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/payment/PaymentOutboxCleaner.java new file mode 100644 index 0000000..2d7ce1e --- /dev/null +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/payment/PaymentOutboxCleaner.java @@ -0,0 +1,51 @@ +package com.food.order.sysyem.outbox.scheduler.payment; + +import com.food.order.system.domain.exception.OrderDomainException; +import com.food.order.system.outbox.OutboxScheduler; +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.system.saga.SagaStatus; +import com.food.order.sysyem.outbox.model.payment.OrderPaymentOutboxMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.Objects; +import java.util.stream.Collectors; + +@Slf4j +@Component +@RequiredArgsConstructor +public class PaymentOutboxCleaner implements OutboxScheduler { + + private final PaymentOutboxHelper paymentOutboxHelper; + + @Override + @Scheduled(cron = "@midnight") + public void processOutboxMessage() { + + var outboxMessageResponse = + paymentOutboxHelper.getPaymentOutboxMessageByOutboxMessageStatusAndSagaStatus( + OutboxStatus.COMPLETED, + SagaStatus.SUCCEEDED, + SagaStatus.FAILED, + SagaStatus.COMPENSATING). + orElseThrow(() -> new OrderDomainException("No outbox message found for processing")); + + if (Objects.nonNull(outboxMessageResponse)) { + + log.info("Received {} OrderPaymentOutboxMessage for clean-up. The Payloads :{}", + outboxMessageResponse.size(), + outboxMessageResponse.stream().map(OrderPaymentOutboxMessage::getPayload) + .collect(Collectors.joining(","))); + paymentOutboxHelper.deletePaymentOutboxMessageByOutboxStatusAndSagaStatus( + OutboxStatus.COMPLETED, + SagaStatus.SUCCEEDED, + SagaStatus.FAILED, + SagaStatus.COMPENSATING); + log.info("Clean-up completed ! DELETED LOG SIZE : {}", outboxMessageResponse.size()); + + } + + } +} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/payment/PaymentOutboxHelper.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/payment/PaymentOutboxHelper.java new file mode 100644 index 0000000..1ef0ca2 --- /dev/null +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/payment/PaymentOutboxHelper.java @@ -0,0 +1,100 @@ +package com.food.order.sysyem.outbox.scheduler.payment; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.food.order.system.domain.exception.OrderDomainException; +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.system.saga.SagaStatus; +import com.food.order.sysyem.outbox.model.payment.OrderPaymentEventPayload; +import com.food.order.sysyem.outbox.model.payment.OrderPaymentOutboxMessage; +import com.food.order.sysyem.ports.output.repository.PaymentOutboxRepository; +import com.food.order.sysyem.valueobject.OrderStatus; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; + +import static com.food.order.system.outbox.order.SagaConst.ORDER_PROCESSING_SAGA; + +@Slf4j +@Component +@RequiredArgsConstructor +public class PaymentOutboxHelper { + + private final PaymentOutboxRepository paymentOutboxRepository; + private final ObjectMapper objectMapper; + + @Transactional(readOnly = true) + public Optional> getPaymentOutboxMessageByOutboxMessageStatusAndSagaStatus( + OutboxStatus outboxStatus, SagaStatus... sagaStatus) { + return paymentOutboxRepository.findByTypeAndOutboxStatusAndSagaStatus( + ORDER_PROCESSING_SAGA, + outboxStatus, + sagaStatus); + + } + + @Transactional(readOnly = true) + public Optional getPaymentOutboxMessageBySagaIdAndSagaStatus( + UUID sagaId, SagaStatus... sagaStatus) { + return paymentOutboxRepository.findByTypeAndSagaIdAndSagaStatus( + ORDER_PROCESSING_SAGA + ,sagaId, + sagaStatus); + } + + @Transactional + public void save(OrderPaymentOutboxMessage orderPaymentOutboxMessage) { + var response = paymentOutboxRepository.save(orderPaymentOutboxMessage); + if (Objects.isNull(response)) { + throw new OrderDomainException("Failed to save outbox message id : " + + orderPaymentOutboxMessage.getId()); + } + log.info("Outbox message id : {} saved successfully", response.getId()); + } + + @Transactional + public void savePaymentOutboxMessage(OrderPaymentEventPayload payload, + OrderStatus orderStatus, + SagaStatus sagaStatus, + OutboxStatus outboxStatus, + UUID sagaId) { + + save(OrderPaymentOutboxMessage.builder() + .id(UUID.randomUUID()) + .sagaId(sagaId) + .createdAt(payload.getCreatedAt()) + .type(ORDER_PROCESSING_SAGA) + .payload(createPayload(payload)) + .outboxStatus(outboxStatus) + .orderStatus(orderStatus) + .sagaStatus(sagaStatus) + .build()); + + } + + private String createPayload(OrderPaymentEventPayload payload) { + try { + return objectMapper.writeValueAsString(payload); + } catch (JsonProcessingException e) { + log.error("Failed to create payload for outbox message", e); + throw new OrderDomainException("Failed to create payload for outbox message"); + } + } + + @Transactional + public void deletePaymentOutboxMessageByOutboxStatusAndSagaStatus(OutboxStatus outboxStatus, + SagaStatus... sagaStatus) { + + paymentOutboxRepository.deleteByTypeAndOutboxStatusAndSagaStatus( + ORDER_PROCESSING_SAGA, + outboxStatus, + sagaStatus); + + } +} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/payment/PaymentOutboxScheduler.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/payment/PaymentOutboxScheduler.java new file mode 100644 index 0000000..1afbfff --- /dev/null +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/payment/PaymentOutboxScheduler.java @@ -0,0 +1,63 @@ +package com.food.order.sysyem.outbox.scheduler.payment; + +import com.food.order.system.domain.exception.OrderDomainException; +import com.food.order.system.outbox.OutboxScheduler; +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.system.saga.SagaStatus; +import com.food.order.sysyem.outbox.model.payment.OrderPaymentOutboxMessage; +import com.food.order.sysyem.ports.output.message.publisher.payment.PaymentRequestMessagePublisher; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Objects; +import java.util.stream.Collectors; + +@Slf4j +@Component +@RequiredArgsConstructor +public class PaymentOutboxScheduler implements OutboxScheduler { + + private final PaymentOutboxHelper paymentOutboxHelper; + private final PaymentRequestMessagePublisher paymentRequestMessagePublisher; + + @Override + @Transactional + @Scheduled(fixedDelayString = "${order-service.outbox-scheduler-fixed-rate}", + initialDelayString = "${order-service.outbox-scheduler-initial-delay}") + public void processOutboxMessage() { + + log.info("Processing outbox message STARTED !"); + + var outboxMessageResponse = + paymentOutboxHelper.getPaymentOutboxMessageByOutboxMessageStatusAndSagaStatus( + OutboxStatus.STARTED, + SagaStatus.STARTED, + SagaStatus.COMPENSATING) + .orElseThrow( + () -> new OrderDomainException("No outbox message found for processing")); + + if (Objects.nonNull(outboxMessageResponse) && outboxMessageResponse.size() > 0) { + + log.info("Received {} OrderPaymentOutboxMessage with ids : {} , sending message bus !" , + outboxMessageResponse.size(), + outboxMessageResponse.stream().map(orderPaymentOutboxMessage -> orderPaymentOutboxMessage.getId().toString()) + .collect(Collectors.joining(","))); + outboxMessageResponse.forEach(orderPaymentOutboxMessage -> { + paymentRequestMessagePublisher.publish(orderPaymentOutboxMessage,this::updateOutboxStatus); + }); + log.info("Processing outbox message completed ! "); + } + + } + + + private void updateOutboxStatus(OrderPaymentOutboxMessage orderPaymentOutboxMessage, + OutboxStatus outboxStatus) { + orderPaymentOutboxMessage.setOutboxStatus(outboxStatus); + paymentOutboxHelper.save(orderPaymentOutboxMessage); + log.info("Outbox message id : {} updated successfully", orderPaymentOutboxMessage.getId()); + } +} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/payment/OrderCancelledPaymentRequestMessagePublisher.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/payment/OrderCancelledPaymentRequestMessagePublisher.java deleted file mode 100644 index 6a84a49..0000000 --- a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/payment/OrderCancelledPaymentRequestMessagePublisher.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.food.order.sysyem.ports.output.message.publisher.payment; - -import com.food.order.sysyem.event.publisher.DomainEventPublisher; -import com.food.order.system.domain.event.OrderCancelledEvent; - -public interface OrderCancelledPaymentRequestMessagePublisher extends DomainEventPublisher { - -} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/payment/OrderCreatedPaymentRequestMessagePublisher.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/payment/OrderCreatedPaymentRequestMessagePublisher.java deleted file mode 100644 index fb83ed0..0000000 --- a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/payment/OrderCreatedPaymentRequestMessagePublisher.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.food.order.sysyem.ports.output.message.publisher.payment; - -import com.food.order.sysyem.event.publisher.DomainEventPublisher; -import com.food.order.system.domain.event.OrderCreatedEvent; - -public interface OrderCreatedPaymentRequestMessagePublisher extends DomainEventPublisher { - -} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/payment/PaymentRequestMessagePublisher.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/payment/PaymentRequestMessagePublisher.java new file mode 100644 index 0000000..05750f7 --- /dev/null +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/payment/PaymentRequestMessagePublisher.java @@ -0,0 +1,13 @@ +package com.food.order.sysyem.ports.output.message.publisher.payment; + +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.sysyem.outbox.model.payment.OrderPaymentOutboxMessage; + +import java.util.function.BiConsumer; + +public interface PaymentRequestMessagePublisher { + + void publish(OrderPaymentOutboxMessage message, + BiConsumer outboxCallback); + +} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/restaurantapproval/OrderPaidRestaurantRequestMessagePublisher.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/restaurantapproval/OrderPaidRestaurantRequestMessagePublisher.java deleted file mode 100644 index 1d03f3f..0000000 --- a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/restaurantapproval/OrderPaidRestaurantRequestMessagePublisher.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.food.order.sysyem.ports.output.message.publisher.restaurantapproval; - -import com.food.order.sysyem.event.publisher.DomainEventPublisher; -import com.food.order.system.domain.event.OrderPaidEvent; - -public interface OrderPaidRestaurantRequestMessagePublisher extends DomainEventPublisher { - -} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/restaurantapproval/RestaurantApprovalRequestMessagePublisher.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/restaurantapproval/RestaurantApprovalRequestMessagePublisher.java new file mode 100644 index 0000000..8f14f8d --- /dev/null +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/message/publisher/restaurantapproval/RestaurantApprovalRequestMessagePublisher.java @@ -0,0 +1,13 @@ +package com.food.order.sysyem.ports.output.message.publisher.restaurantapproval; + +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.sysyem.outbox.model.approval.OrderApprovalOutboxMessage; + +import java.util.function.BiConsumer; + +public interface RestaurantApprovalRequestMessagePublisher { + + void publish(OrderApprovalOutboxMessage message, + BiConsumer outboxCallback); + +} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/repository/ApprovalOutboxRepository.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/repository/ApprovalOutboxRepository.java new file mode 100644 index 0000000..4cc7355 --- /dev/null +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/repository/ApprovalOutboxRepository.java @@ -0,0 +1,25 @@ +package com.food.order.sysyem.ports.output.repository; + +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.system.saga.SagaStatus; +import com.food.order.sysyem.outbox.model.approval.OrderApprovalOutboxMessage; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public interface ApprovalOutboxRepository { + OrderApprovalOutboxMessage save(OrderApprovalOutboxMessage message); + + Optional> findByTypeAndOutboxStatusAndSagaStatus(String type, + OutboxStatus outboxStatus, + SagaStatus... sagaStatus); + + Optional findByTypeAndSagaIdAndSagaStatus(String type, + UUID sagaId, + SagaStatus... sagaStatus); + + void deleteByTypeAndOutboxStatusAndSagaStatus(String type, + OutboxStatus outboxStatus, + SagaStatus... sagaStatus); +} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/repository/PaymentOutboxRepository.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/repository/PaymentOutboxRepository.java new file mode 100644 index 0000000..a3016a7 --- /dev/null +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/repository/PaymentOutboxRepository.java @@ -0,0 +1,28 @@ +package com.food.order.sysyem.ports.output.repository; + +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.system.saga.SagaStatus; +import com.food.order.sysyem.outbox.model.payment.OrderPaymentOutboxMessage; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public interface PaymentOutboxRepository { + + OrderPaymentOutboxMessage save(OrderPaymentOutboxMessage message); + + Optional> findByTypeAndOutboxStatusAndSagaStatus( String type, + OutboxStatus outboxStatus, + SagaStatus... sagaStatus); + + Optional findByTypeAndSagaIdAndSagaStatus(String type, + UUID sagaId, + SagaStatus... sagaStatus); + + void deleteByTypeAndOutboxStatusAndSagaStatus(String type, + OutboxStatus outboxStatus, + SagaStatus... sagaStatus); + + +} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/saga/OrderApprovalSaga.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/saga/OrderApprovalSaga.java index d309c10..314aa46 100644 --- a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/saga/OrderApprovalSaga.java +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/saga/OrderApprovalSaga.java @@ -6,7 +6,6 @@ import com.food.order.system.saga.SagaStep; import com.food.order.sysyem.dto.message.RestaurantApprovalResponse; import com.food.order.sysyem.event.EmptyEvent; import com.food.order.sysyem.helper.OrderSagaHelper; -import com.food.order.sysyem.ports.output.message.publisher.payment.OrderCancelledPaymentRequestMessagePublisher; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -18,9 +17,10 @@ import org.springframework.transaction.annotation.Transactional; public class OrderApprovalSaga implements SagaStep { private final OrderDomainService orderDomainService; - private final OrderCancelledPaymentRequestMessagePublisher messagePublisher; private final OrderSagaHelper orderSagaHelper; + + @Override @Transactional public EmptyEvent process(RestaurantApprovalResponse data) { @@ -38,7 +38,9 @@ public class OrderApprovalSaga implements SagaStep { private final OrderMessagingDataMapper orderMessagingDataMapper; private final OrderServiceConfigData configData; diff --git a/order-service/order-messaging/src/main/java/com/food/order/system/order/messaging/publisher/kafka/CreateOrderKafkaMessagePublisher.java b/order-service/order-messaging/src/main/java/com/food/order/system/order/messaging/publisher/kafka/CreateOrderKafkaMessagePublisher.java index 5ee687c..cce3831 100644 --- a/order-service/order-messaging/src/main/java/com/food/order/system/order/messaging/publisher/kafka/CreateOrderKafkaMessagePublisher.java +++ b/order-service/order-messaging/src/main/java/com/food/order/system/order/messaging/publisher/kafka/CreateOrderKafkaMessagePublisher.java @@ -7,7 +7,7 @@ import com.food.order.system.kafka.producer.KafkaMessageHelper; import com.food.order.system.kafka.producer.service.KafkaProducer; import com.food.order.system.order.messaging.mapper.OrderMessagingDataMapper; import com.food.order.sysyem.config.OrderServiceConfigData; -import com.food.order.sysyem.ports.output.message.publisher.payment.OrderCreatedPaymentRequestMessagePublisher; +import com.food.order.sysyem.event.publisher.DomainEventPublisher; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -15,7 +15,7 @@ import org.springframework.stereotype.Component; @Component @Slf4j @RequiredArgsConstructor -public class CreateOrderKafkaMessagePublisher implements OrderCreatedPaymentRequestMessagePublisher { +public class CreateOrderKafkaMessagePublisher implements DomainEventPublisher { private final OrderMessagingDataMapper orderMessagingDataMapper; private final OrderServiceConfigData configData; diff --git a/order-service/order-messaging/src/main/java/com/food/order/system/order/messaging/publisher/kafka/PayOrderKafkaMessagePublisher.java b/order-service/order-messaging/src/main/java/com/food/order/system/order/messaging/publisher/kafka/PayOrderKafkaMessagePublisher.java index bf5c71b..24551fb 100644 --- a/order-service/order-messaging/src/main/java/com/food/order/system/order/messaging/publisher/kafka/PayOrderKafkaMessagePublisher.java +++ b/order-service/order-messaging/src/main/java/com/food/order/system/order/messaging/publisher/kafka/PayOrderKafkaMessagePublisher.java @@ -6,7 +6,7 @@ import com.food.order.system.kafka.producer.KafkaMessageHelper; import com.food.order.system.kafka.producer.service.KafkaProducer; import com.food.order.system.order.messaging.mapper.OrderMessagingDataMapper; import com.food.order.sysyem.config.OrderServiceConfigData; -import com.food.order.sysyem.ports.output.message.publisher.restaurantapproval.OrderPaidRestaurantRequestMessagePublisher; +import com.food.order.sysyem.event.publisher.DomainEventPublisher; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -14,7 +14,7 @@ import org.springframework.stereotype.Component; @Component @Slf4j @RequiredArgsConstructor -public class PayOrderKafkaMessagePublisher implements OrderPaidRestaurantRequestMessagePublisher { +public class PayOrderKafkaMessagePublisher implements DomainEventPublisher { private final OrderMessagingDataMapper orderMessagingDataMapper; private final OrderServiceConfigData configData; From 3affe7ccdda23988ef58892995850abe4e1c03e7 Mon Sep 17 00:00:00 2001 From: Ali CANLI Date: Fri, 15 Jul 2022 21:45:48 +0300 Subject: [PATCH 3/3] Outbox Message and Scheduler class implemented part - 2. --- .idea/sonarlint/issuestore/index.pb | 337 ++++++++++++++++-- .../food/order/sysyem/event/DomainEvent.java | 2 - .../com/food/order/system/saga/SagaStep.java | 8 +- .../adapter/PaymentOutboxRepositoryImpl.java | 63 ++++ .../payment/entity/PaymentOutboxEntity.java | 53 +++ .../PaymentOutboxNotFoundException.java | 8 + .../mapper/PaymentOutboxDataAccessMapper.java | 40 +++ .../PaymentOutboxJpaRepository.java | 28 ++ .../adapter/ApprovalOutboxRepositoryImpl.java | 65 ++++ .../entity/ApprovalOutboxEntity.java | 52 +++ .../ApprovalOutboxNotFoundException.java | 8 + .../ApprovalOutboxDataAccessMapper.java | 40 +++ .../ApprovalOutboxJpaRepository.java | 29 ++ .../PaymentResponseMessageListenerImpl.java | 5 +- ...ntApprovalResponseMessageListenerImpl.java | 3 +- .../order/sysyem/mapper/OrderDataMapper.java | 19 + .../payment/OrderPaymentOutboxMessage.java | 4 + .../approval/ApprovalOutboxHelper.java | 35 ++ .../order/sysyem/saga/OrderApprovalSaga.java | 13 +- .../order/sysyem/saga/OrderPaymentSaga.java | 147 +++++++- .../domain/event/OrderCancelledEvent.java | 10 +- .../domain/event/OrderCreatedEvent.java | 9 +- .../system/domain/event/OrderPaidEvent.java | 9 +- .../domain/service/OrderDomainService.java | 7 +- .../service/impl/OrderDomainServiceImpl.java | 15 +- 25 files changed, 913 insertions(+), 96 deletions(-) create mode 100644 order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/adapter/PaymentOutboxRepositoryImpl.java create mode 100644 order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/entity/PaymentOutboxEntity.java create mode 100644 order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/exception/PaymentOutboxNotFoundException.java create mode 100644 order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/mapper/PaymentOutboxDataAccessMapper.java create mode 100644 order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/repository/PaymentOutboxJpaRepository.java create mode 100644 order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/adapter/ApprovalOutboxRepositoryImpl.java create mode 100644 order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/entity/ApprovalOutboxEntity.java create mode 100644 order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/exception/ApprovalOutboxNotFoundException.java create mode 100644 order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/mapper/ApprovalOutboxDataAccessMapper.java create mode 100644 order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/repository/ApprovalOutboxJpaRepository.java diff --git a/.idea/sonarlint/issuestore/index.pb b/.idea/sonarlint/issuestore/index.pb index d0315ab..ae06964 100644 --- a/.idea/sonarlint/issuestore/index.pb +++ b/.idea/sonarlint/issuestore/index.pb @@ -7,27 +7,322 @@ U %order-service/order-container/pom.xml,5\e\5edc4f773e0205d9c5812143cbbd1132b3c3de4c O order-service/order-app/pom.xml,2\5\255f6fd0140e765f25a0c5ea7d6c33af3bb1ecec -U -%order-service/order-messaging/pom.xml,c\1\c19aa6db8e37c3805307311ca17ddea372646d36 -R -"order-service/order-domain/pom.xml,6\c\6c488d4235eb8af9fe6c7a0dcd6d396f5fb3b9db -L -common/common-domain/pom.xml,3\e\3ebe72cff67b25b3f376cc963b57146cc5d43487 -> -common/pom.xml,4\8\488bdbea7e8b73c44bd2a54e63f6012f10969196 -7 -pom.xml,4\4\442292b8a7efeabbe4cc176709b833b1792140ec +� +vorder-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/valueobject/StreetAddress.java,f\7\f74133100119166e8b876daa424b93e0d64b9b80 +� +sorder-service/order-data-access/src/main/java/com/food/order/system/data/access/customer/entity/CustomerEntity.java,a\3\a3d0249dc9ec483df31a5f520a586bcdbe6f777c +� +~order-service/order-data-access/src/main/java/com/food/order/system/data/access/customer/repository/CustomerJPARepository.java,5\4\548371b5e6ebbbbe24b839de042f70fc2f764316 +� +xorder-service/order-data-access/src/main/java/com/food/order/system/data/access/order/repository/OrderJpaRepository.java,4\0\40da9fb64727cbca8732bf490a71748e13ad2197 E order-service/pom.xml,d\3\d3bf53ecc442740843266a635cbfe9807b992566 - -Qcommon/common-domain/src/main/java/com/food/order/domain/valueobject/OrderId.java,7\3\73533e03c456839a625223f6070a82fe26a051d1 +� +Rcommon/common-domain/src/main/java/com/food/order/sysyem/entity/AggregateRoot.java,c\c\cc687f6a595bab80c5e573f18747043940db2e05  -Ocommon/common-domain/src/main/java/com/food/order/domain/valueobject/Money.java,1\9\19055dd2bdebc31a1868d96dadab8510f24e71ee - -Ucommon/common-domain/src/main/java/com/food/order/domain/valueobject/OrderStatus.java,6\a\6a4ab22cb9f8e43dd5b01a714d3f60035154029f - -Scommon/common-domain/src/main/java/com/food/order/domain/valueobject/ProductId.java,9\0\90ca37cd91bd840352dc5fe9846f4b160b829982 - -Vcommon/common-domain/src/main/java/com/food/order/domain/valueobject/RestaurantId.java,b\d\bd99e8fd918c1d01925143dbc2e08a8064258f70 - -Tcommon/common-domain/src/main/java/com/food/order/domain/valueobject/CustomerId.java,c\2\c2aadaf5638ecab2404c0c3149e923f596cb777d \ No newline at end of file +Ocommon/common-domain/src/main/java/com/food/order/sysyem/entity/BaseEntity.java,f\b\fbda5fe4b42ac2a311824c02836f11c7d24db0d3 +� +^order-service/order-container/src/main/java/com/food/order/system/order/domain/BeanConfig.java,a\1\a1c09366ca4c2abdfc90bc1beacf797ac2103a3b +� +{order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/dto/create/CreateOrderCommand.java,d\c\dc92bf07b0bed6658fc54cbe0ff01dbeee0c414c +� +lorder-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/entity/Customer.java,7\4\74a68a7e9f08b7fca383a0d424516f5987b099dd +� +}order-service/order-data-access/src/main/java/com/food/order/system/data/access/customer/mapper/CustomerDataAccessMapper.java,8\6\869ec14f296d908f53b6edd9f087f51238dffec0 +� +Tcommon/common-domain/src/main/java/com/food/order/sysyem/valueobject/CustomerId.java,5\0\500e4021725078024436d6eaddaf3a5ce80f87c3 +� +�order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/repository/CustomerRepository.java,2\4\243d2baeb6d671f73acce01d5673f03f03328ae7 +� +|order-service/order-data-access/src/main/java/com/food/order/system/data/access/customer/adapter/CustomerRepositoryImpl.java,8\0\808a4e6022bab0f1bb8e5502a77c933f746cb380 +� +Wcommon/common-domain/src/main/java/com/food/order/sysyem/exception/DomainException.java,b\c\bc831ad083de7cf458fb4a2ebce1d9434a1922ee +� +uorder-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/dto/create/OrderAddress.java,5\e\5e090b61d663849123e622ef98bfe4742229191a +� +morder-service/order-data-access/src/main/java/com/food/order/system/data/access/order/entity/OrderEntity.java,e\d\ede9a92270a067f79a85b238a144b459eca8662b +� +Qcommon/common-domain/src/main/java/com/food/order/sysyem/valueobject/OrderId.java,c\d\cdff6de4b3446e3efaa50f9d01263053fea2139c +� +rorder-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/dto/create/OrderItem.java,f\b\fb43567b0066ced0815bbf02432cd16fed9c608a +� +torder-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/valueobject/OrderItemId.java,3\3\331b29df1b4bc0c1299f8bb1af8f70edf79e923a +� +}order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/exception/OrderNotFoundException.java,a\3\a31c0821fec53a50af20e3383ba61e44d9dd567d +� +vorder-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/OrderTrackCommandHandler.java,f\2\f2d0dd97f438393a8cc6c9859c953f3d005925ed +� +korder-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/entity/Product.java,0\5\05295c0ef124c56014b3b6b04e0808e407995151 +� +Scommon/common-domain/src/main/java/com/food/order/sysyem/valueobject/ProductId.java,b\0\b0d8076babec863faea4aefc2e42755cde57012c +� +�order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/dto/message/RestaurantApprovalResponse.java,c\b\cb772c96c93d2fc2e4fc760f3d40a29945b2c327 +� +�order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/input/message/listener/restaurantapproval/RestaurantApprovalResponseMessageListener.java,6\9\690a6444f8a326a40054c16acbcb8c4d3e14ebf4 +� +Vcommon/common-domain/src/main/java/com/food/order/sysyem/valueobject/RestaurantId.java,2\b\2b3a056b9b3fde4b15f1d05d7861846d59926dfa +� +sorder-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/valueobject/TrackingId.java,0\c\0c483d5138a4e1e7237a72d7888989bcd840fe2c +� +worder-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/dto/track/TrackOrderQuery.java,1\4\149b5ecef8503b928e6b223bb6db52a4a3a6afe5 +� +zorder-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/dto/track/TrackOrderResponse.java,3\4\34e02ed9f253ce066f33cd48e40346510e7fb7cf +� +qorder-service/order-data-access/src/main/java/com/food/order/system/data/access/order/entity/OrderItemEntity.java,c\5\c520ec00a75c1b87bc1497a34f1b0acab96aedeb +� +sorder-service/order-data-access/src/main/java/com/food/order/system/data/access/order/entity/OrderItemEntityId.java,f\2\f20575f9c726daa75f956eb5c320094d8a1b65f8 +� +�order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/input/message/listener/payment/PaymentResponseMessageListener.java,d\7\d7462ec5060343a2ca3aa497ba1550597538e5d5 +9 + README.md,8\e\8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d +� +minfrastructure/kafka/kafka-config/src/main/java/com/food/order/kafka/config/data/KafkaProducerConfigData.java,1\4\147bb8ad56bcda10398aa4b9b972f2386b09c40a +� +oinfrastructure/kafka/kafka-producer/src/main/java/com/food/order/system/kafka/producer/KafkaProducerConfig.java,4\e\4e08bd5115c637166f591a535fffd0ab92a42d18 +H +customer-service/pom.xml,5\8\5828c3b58c65a5dabc2340b48a37b3e92462b9b0 +L +infrastructure/kafka/pom.xml,5\7\57d8d8349e1f824686cd44fe2200377b17148804 +[ ++infrastructure/docker-compose/zookeeper.yml,2\5\25078b0f9296433af7750df1603d7c084625fc0f +X +(infrastructure/docker-compose/common.yml,5\9\59e2079c706300654edfdfefb1b0258617f514b7 +� +{order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/config/OrderServiceConfigData.java,9\5\954530d266381bab329eaec2c32fb8af09ce6299 +_ +/infrastructure/docker-compose/kafka_cluster.yml,5\e\5e8bb3a16c76d6344e1b199d9d28ca41c06d612b +� +Pcommon/common-domain/src/main/java/com/food/order/sysyem/valueobject/BaseId.java,8\f\8f3bd12b2221204ce618b7bdc1917bf908f691b5 +� +Wcommon/common-domain/src/main/java/com/food/order/sysyem/valueobject/PaymentStatus.java,9\d\9d04fc94054bc74c3883af7f460ea23903ce071b +\ +,infrastructure/docker-compose/init_kafka.yml,1\2\12c3aaa4a5ab6d91f1d0c2439dd959f1f4446e70 +R +"infrastructure/docker-compose/.env,8\4\84825da1de32497b36ece2e7280bf9e7ea2fc807 +: + +.gitignore,a\5\a5cc2925ca8258af241be7e5b0381edf30266302 +� +�payment-service/payment-dataaccess/src/main/java/com/food/order/system/payment/data/access/creditentry/exception/CreditEntryDataaccessException.java,9\9\99186cf850e4c3c7b7cead5e8f5e10f4a2990dd7 +� +�payment-service/payment-dataaccess/src/main/java/com/food/order/system/payment/data/access/creditentry/mapper/CreditEntryDataAccessMapper.java,5\3\5312c112e34b9c3095b953321c14a4abdd5a5c98 +� +�payment-service/payment-domain/payment-domain-core/src/main/java/com/food/order/system/payment/service/domain/valueobject/CreditEntryId.java,8\b\8bb3443859c229b62ae98e219040b1aacb946386 +� +�payment-service/payment-dataaccess/src/main/java/com/food/order/system/payment/data/access/creditentry/repository/CreditEntryJpaRepository.java,f\b\fbeba1493b7f71dfd93b47fbff84da00e2007f84 +� +�payment-service/payment-domain/payment-application-service/src/main/java/com/food/order/system/payment/application/service/ports/output/repository/CreditEntryRepository.java,c\a\caa192dc17f91ee8b749c660717a36184a3c05d6 +� +�payment-service/payment-dataaccess/src/main/java/com/food/order/system/payment/data/access/creditentry/adapter/CreditEntryRepositoryImpl.java,6\a\6aa6e8ce5f9da7083389c855e733743d921bfba9 +� +�payment-service/payment-dataaccess/src/main/java/com/food/order/system/payment/data/access/credithistory/exception/CreditHistoryDataaccessException.java,d\3\d3f4a2c48e4ecf8c7e126c04208ec05e507174fb +� +�payment-service/payment-dataaccess/src/main/java/com/food/order/system/payment/data/access/credithistory/mapper/CreditHistoryDataAccessMapper.java,9\8\98c57fc875d10886d09efd0ddb7b39a82e37cb78 +� +�payment-service/payment-dataaccess/src/main/java/com/food/order/system/payment/data/access/credithistory/entity/CreditHistoryEntity.java,6\5\651230dc70c13cbfad780494568f27d0ef9209bd +� +�payment-service/payment-domain/payment-domain-core/src/main/java/com/food/order/system/payment/service/domain/valueobject/CreditHistoryId.java,8\0\80c44aeefda382eff76cd9f8c74bd0c670c73e65 +} +Mcommon/common-domain/src/main/java/com/food/order/sysyem/DomainConstants.java,e\5\e5b584bef3ba718cae24b87e69ece8ea43c2fb8f + +Ocommon/common-domain/src/main/java/com/food/order/sysyem/valueobject/Money.java,f\2\f2f10975dd8765fc764570c28a233a3a9c89cdd0 +� +�payment-service/payment-domain/payment-domain-core/src/main/java/com/food/order/system/payment/service/domain/exception/PayemntDomainException.java,c\6\c623edfbee9db5cb30319890907999902d4ccae0 +� +�payment-service/payment-domain/payment-application-service/src/main/java/com/food/order/system/payment/application/service/exception/PaymentApplicationServiceException.java,d\9\d9d957f98d7f0d30a2adea129ac4631b206fc061 +� +�payment-service/payment-domain/payment-domain-core/src/main/java/com/food/order/system/payment/service/domain/event/PaymentCancelledEvent.java,5\6\560399a9750e4d9dafde7225b7302534c0ed9d4a +� +�payment-service/payment-domain/payment-application-service/src/main/java/com/food/order/system/payment/application/service/ports/output/message/publisher/PaymentCancelledMessagePublisher.java,0\b\0ba58cf064e11f82c83aa840d4af085fe0cc0c0d +� +�payment-service/payment-domain/payment-domain-core/src/main/java/com/food/order/system/payment/service/domain/event/PaymentCompletedEvent.java,5\0\509a6d6aab18102a92a942a4dd736bd4110afb6c +� +�payment-service/payment-domain/payment-application-service/src/main/java/com/food/order/system/payment/application/service/ports/output/message/publisher/PaymentCompletedMessagePublisher.java,4\b\4b8f46ac54964317cd5f358f1a462e6b73fba663 +� +�payment-service/payment-dataaccess/src/main/java/com/food/order/system/payment/data/access/payment/exception/PaymentDataaccessException.java,a\5\a5ece490bea2fd2276a3bf51703a92fffd10a2fd +� +�payment-service/payment-domain/payment-domain-core/src/main/java/com/food/order/system/payment/service/domain/event/PaymentEvent.java,1\5\1596dfb82cc42ada4d77f57e2707ef856a3c491d +� +�payment-service/payment-domain/payment-domain-core/src/main/java/com/food/order/system/payment/service/domain/event/PaymentFailedEvent.java,3\a\3af8e86e31391041e86e161d3eec0e009faa038e +� +�payment-service/payment-domain/payment-application-service/src/main/java/com/food/order/system/payment/application/service/ports/output/message/publisher/PaymentFailedMessagePublisher.java,5\f\5fe51c223681307c2b7b97965c69c53d8e3ee085 +� +�payment-service/payment-domain/payment-domain-core/src/main/java/com/food/order/system/payment/service/domain/valueobject/PaymentId.java,8\5\8517e077559b83db762f2451a5dabe31961fb8b7 +� +�payment-service/payment-dataaccess/src/main/java/com/food/order/system/payment/data/access/payment/repository/PaymentJpaRepository.java,d\0\d08709acd3c6ce0ea804ce4d7b2272e450c9c58b +� +�payment-service/payment-domain/payment-domain-core/src/main/java/com/food/order/system/payment/service/domain/exception/PaymentNotFoundException.java,d\5\d578afe6988eefbb0bc312beb1538ca09b3d016d +� +\common/common-domain/src/main/java/com/food/order/sysyem/valueobject/PaymentOrderStatus.java,8\c\8cfc34311283c9ce79c84a4bda4eec7d30ed49f8 +� +�payment-service/payment-domain/payment-application-service/src/main/java/com/food/order/system/payment/application/service/ports/output/repository/PaymentRepository.java,f\9\f9261dfed13ff6ca5d1ea0b592251a98717ade8a +� +�payment-service/payment-domain/payment-application-service/src/main/java/com/food/order/system/payment/application/service/dto/PaymentRequest.java,5\4\5491134569866d37cf17fafc904a9cc411b03901 +Z +*payment-service/payment-dataaccess/pom.xml,e\1\e111ed261baa772cb4d6e08c563771132311486d +G +payment-service/pom.xml,2\5\25baee198b18cf264e9c0dcfc72630a226ed792c +� +�payment-service/payment-dataaccess/src/main/java/com/food/order/system/payment/data/access/credithistory/repository/CreditHistoryJpaRepository.java,b\4\b49bb07e3985f218ed2efad0cefe57332bd4048b +� +�payment-service/payment-dataaccess/src/main/java/com/food/order/system/payment/data/access/creditentry/entity/CreditEntryEntity.java,a\7\a79105dd255d06e1d0efa727796643bd3386bf96 +U +%order-service/order-messaging/pom.xml,c\1\c19aa6db8e37c3805307311ca17ddea372646d36 +[ ++infrastructure/kafka/kafka-producer/pom.xml,c\7\c7d5ce453d66546bb66d7c5bfe5df1f88d0c5862 +� +�payment-service/payment-domain/payment-application-service/src/main/java/com/food/order/system/payment/application/service/config/PaymentServiceConfigData.java,e\b\eb7f9fbb0b2cace2df92dc9d5819ed6a5456d638 +X +(infrastructure/kafka/kafka-model/pom.xml,b\5\b5ebdc449af44ac439d81aad072e585df2d63cc3 +� +lpayment-service/payment-container/src/main/java/com/food/order/system/payment/service/domain/BeanConfig.java,0\e\0e2f043af840a6f0c4c5f494dfa373e6a3287249 +� +ninfrastructure/kafka/kafka-producer/src/main/java/com/food/order/system/kafka/producer/KafkaMessageHelper.java,a\5\a5b1ed3b934da0d7bc46e6247f017849d828fa02 +� +tinfrastructure/kafka/kafka-model/src/main/java/com/food/order/system/kafka/order/avro/model/OrderApprovalStatus.java,b\7\b7f6ce9c30dde5a5d9eb8055e83a4d3ee604e570 +� +�payment-service/payment-messaging/src/main/java/com/food/order/system/payment/messaging/publisher/kafka/PaymentCancelledKafkaMessagePublisher.java,4\2\42f2413f9746aa30ed7d496b9575ecff8f20a2d5 +� +�payment-service/payment-messaging/src/main/java/com/food/order/system/payment/messaging/publisher/kafka/PaymentCompletedKafkaMessagePublisher.java,f\4\f429e4c46229dca472857e4323ab89434a74ee69 +� +�payment-service/payment-messaging/src/main/java/com/food/order/system/payment/messaging/publisher/kafka/PaymentFailedKafkaMessagePublisher.java,1\a\1a308906df5384e6c40641fda8a58d0a640d3f03 +� +sinfrastructure/kafka/kafka-model/src/main/java/com/food/order/system/kafka/order/avro/model/PaymentOrderStatus.java,2\9\290b3da55c67b50389607e38ae95a4cb6cba4d71 +� +xinfrastructure/kafka/kafka-model/src/main/java/com/food/order/system/kafka/order/avro/model/PaymentRequestAvroModel.java,f\7\f77456f16799869df9c5c5715d2f8ed3497c2519 +� +yinfrastructure/kafka/kafka-model/src/main/java/com/food/order/system/kafka/order/avro/model/PaymentResponseAvroModel.java,1\e\1e0c9165eec9a3a8770a07d72e38f1137af3eb60 +� +ninfrastructure/kafka/kafka-model/src/main/java/com/food/order/system/kafka/order/avro/model/PaymentStatus.java,3\6\3672be2bd188cd93638a2a699d7fee6459eed2b4 +Y +)payment-service/payment-messaging/pom.xml,5\6\56ed16c723f2655c00ae8a35fc001c9d1a71de99 +� +hinfrastructure/kafka/kafka-model/src/main/java/com/food/order/system/kafka/order/avro/model/Product.java,e\9\e91d1d55ba1a38aca2893c014a5b2ff03826ac50 +� +�infrastructure/kafka/kafka-model/src/main/java/com/food/order/system/kafka/order/avro/model/RestaurantApprovalResponseAvroModel.java,1\a\1a8cdd6c032310eea6aa5e9eea4a4f44064cf6c4 +� +vinfrastructure/kafka/kafka-model/src/main/java/com/food/order/system/kafka/order/avro/model/RestaurantOrderStatus.java,5\0\50e4cb633ca3265d27ba402a4fa00cfe3710ab77 +� +_common/common-domain/src/main/java/com/food/order/sysyem/valueobject/RestaurantOrderStatus.java,d\e\de5a9499cfd1de9312944b07ebf035e6f38db482 +Y +)payment-service/payment-container/pom.xml,d\3\d319507d8930e27b35fa7ea166ac3b4a66d68640 +U +%order-service/order-container/pom.xml,5\e\5edc4f773e0205d9c5812143cbbd1132b3c3de4c +j +:payment-service/payment-domain/payment-domain-core/pom.xml,8\0\8064d0f20ca168afe879cc5b60a151cdacfb6d52 +� +�payment-service/payment-domain/payment-application-service/src/main/java/com/food/order/system/payment/application/service/mapper/PaymentDataMapper.java,f\1\f1792c5c7fb68c718b5d80fa1ba2e668f2890e6a +V +&payment-service/payment-domain/pom.xml,f\d\fdf4fe8ba7be6f4753e22faf477b9766890d753d +R +"order-service/order-domain/pom.xml,6\c\6c488d4235eb8af9fe6c7a0dcd6d396f5fb3b9db +� +�restaurant-service/restaurant-domain/restaurant-core-domain/src/main/java/com/food/order/system/restaurant/domain/core/entity/OrderApproval.java,4\8\489b3cdfab8fecd434040344d80ecad6ceee200c +� +�restaurant-service/restaurant-domain/restaurant-core-domain/src/main/java/com/food/order/system/restaurant/domain/core/event/OrderApprovalEvent.java,c\4\c418505b723576c31849d8c877c81542930f66f8 +� +�restaurant-service/restaurant-domain/restaurant-core-domain/src/main/java/com/food/order/system/restaurant/domain/core/valueobject/OrderApprovalId.java,9\e\9e982695e9dc9b623937a3d13ac8887af6084c33 +� +�restaurant-service/restaurant-dataaccess/src/main/java/com/food/order/system/data/access/restaurant/repository/OrderApprovalJpaRepository.java,4\c\4c30cce37540f6fb9aceb3833e8dc5ea4ba8e977 +� +�restaurant-service/restaurant-domain/restaurant-core-domain/src/main/java/com/food/order/system/restaurant/domain/core/event/OrderApprovedEvent.java,3\b\3b201270232a1a2c94a8188804153294f38e3c38 +� +�restaurant-service/restaurant-domain/restaurant-core-domain/src/main/java/com/food/order/system/restaurant/domain/core/event/OrderRejectedEvent.java,5\d\5dd879e29cd62beb97a50b23474d24e68deabe86 +Q +!common/common-data-access/pom.xml,3\2\32df6d070f4ffc683364c978efd79428b5d050d1 +W +'order-service/order-data-access/pom.xml,f\2\f258deaeb6ebc953857cf7cb5342c2c6d7f03934 +r +Bpayment-service/payment-domain/payment-application-service/pom.xml,6\d\6d756c3c984cf48b9a51e07334cc94cf28ea1fe5 +_ +/restaurant-service/restaurant-container/pom.xml,0\4\04c5d73b814c0fa8d99213001aba2ffead7cf2e2 +` +0restaurant-service/restaurant-dataaccess/pom.xml,4\9\49d0c1c1fb43880d1807f9fed48d285c58a50d73 +{ +Krestaurant-service/restaurant-domain/restaurant-application-service/pom.xml,6\1\6117ad0ec12432616d07d621911c1bc4f02f80d6 +s +Crestaurant-service/restaurant-domain/restaurant-core-domain/pom.xml,f\4\f4182ab70945af3ada6221f0e18bf8f1fac7e2eb +\ +,restaurant-service/restaurant-domain/pom.xml,5\2\528286e8455474a164649915961cfe43c36d78f2 +_ +/restaurant-service/restaurant-messaging/pom.xml,e\3\e3c0a92269005ce0f801ef2dc06d6aa1a00e1a16 +J +restaurant-service/pom.xml,f\1\f1a73488b9e5d5fe78f637b81387a1a03077be0d +� +}common/common-data-access/src/main/java/com/food/order/system/common/data/access/exception/RestaurantDataAccessException.java,2\9\29f5af9e9b97dd442e3b2762d7206f3cdd3d44fc +� +�order-service/order-data-access/src/main/java/com/food/order/system/data/access/restaurant/mapper/RestaurantDataAccessMapper.java,d\f\df56aea6e7cca961acdfb2d7e0845d7c11affe80 +� +�restaurant-service/restaurant-domain/restaurant-core-domain/src/main/java/com/food/order/system/restaurant/domain/core/exception/RestaurantDomainException.java,0\1\01f10855589a69717b3d534bc555c4b4ebf71011 +� +ocommon/common-data-access/src/main/java/com/food/order/system/common/data/access/entity/RestaurantEntityId.java,f\9\f9caeadd0ffb8d25d2a99850208b70c1a8784f2f +� +�restaurant-service/restaurant-domain/restaurant-core-domain/src/main/java/com/food/order/system/restaurant/domain/core/exception/RestaurantNotFoundException.java,2\7\271e8c6599a9b47753cdb7561d54460059472bd6 +� +ecustomer-service/src/main/java/com/food/order/system/customer/service/CustomerServiceApplication.java,3\9\393f6fa7c918c4529954de3370ff366d10d7a212 +� +�restaurant-service/restaurant-domain/restaurant-core-domain/src/main/java/com/food/order/system/restaurant/domain/core/entity/Restaurant.java,6\a\6a9d2207b0d9ce51a7359468f7a04bcca116f635 +� +�restaurant-service/restaurant-domain/restaurant-core-domain/src/main/java/com/food/order/system/restaurant/domain/core/RestaurantDomainServiceImpl.java,4\d\4d877c39a261599f8c4a8c4a790ff3143fedcb26 +� +�restaurant-service/restaurant-domain/restaurant-application-service/src/main/java/com/food/order/system/restaurant/domain/service/ports/output/repository/RestaurantRepository.java,a\0\a09c218d21582858a2e9072e9616706d9bbca56c +� +zinfrastructure/kafka/kafka-producer/src/main/java/com/food/order/system/kafka/producer/service/impl/KafkaProducerImpl.java,3\6\360e8906e84c982c90c5204b896ff036da101c21 +� +mcommon/common-data-access/src/main/java/com/food/order/system/common/data/access/entity/RestaurantEntity.java,f\6\f6d6367160c5a21258ddb22c3d6e6016faf5ce07 +� +morder-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/entity/OrderItem.java,4\1\41efbf587fa2c3edceea298959ed919f554f5ec6 +� +|order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/dto/create/CreateOrderResponse.java,d\e\defb0c01f5710b54b1cb7fcf9550f2a8df2d65c9 +� +`order-service/order-app/src/main/java/com/food/order/system/service/app/api/OrderController.java,b\b\bb92862728c8d04eaf070a4132867fd23ba8b073 +� +�order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/input/service/OrderApplicationService.java,a\d\ad40314bdf019aafa5a9fa840f32dfc101f16bea +� +vorder-service/order-messaging/src/main/java/com/food/order/system/order/messaging/mapper/OrderMessagingDataMapper.java,1\c\1c7014ddd3deb73cf65c929b0b924af0cff53ddc +� +yorder-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/OrderApplicationServiceImpl.java,5\7\57bd77f4a2092ee1117f6cac595ab47398708c81 +� +|payment-service/payment-dataaccess/src/main/java/com/food/order/system/payment/data/access/payment/entity/PaymentEntity.java,7\7\77ecb63f035d722547f8055c99803d5a146c0c32 +� +�payment-service/payment-dataaccess/src/main/java/com/food/order/system/payment/data/access/payment/mapper/PaymentDataAccessMapper.java,7\2\72f54cc6a6946cf708c67d75e842373fb9f5fc16 +� +�payment-service/payment-dataaccess/src/main/java/com/food/order/system/payment/data/access/payment/adapter/PaymentRepositoryImpl.java,1\0\10d78edd523b7510aa2d72821d4312c8f7f63f55 +� +�payment-service/payment-domain/payment-application-service/src/main/java/com/food/order/system/payment/application/service/PaymentRequestHelper.java,6\1\61be9c5145d9055f5954e0566c9a1e8d6fc9fd4e +� +�payment-service/payment-domain/payment-domain-core/src/main/java/com/food/order/system/payment/service/domain/PaymentDomainService.java,0\a\0ae006e1f47e9ff41c947eda574122a2af37bb76 +� +�payment-service/payment-domain/payment-domain-core/src/main/java/com/food/order/system/payment/service/domain/entity/Payment.java,5\c\5ce3e056edb971712e013f1d85f106545fa82a6b +� +]common/common-domain/src/main/java/com/food/order/sysyem/valueobject/OrderApprovalStatus.java,d\a\da94b4a105d8767dc1a7d3703a0cf9f5aa4a3c8a +� +�restaurant-service/restaurant-dataaccess/src/main/java/com/food/order/system/data/access/restaurant/adapter/RestaurantRepositoryImpl.java,5\2\52a371fa8697b7ce536579a6fb3247c963594327 +� +�restaurant-service/restaurant-dataaccess/src/main/java/com/food/order/system/data/access/restaurant/mapper/RestaurantDataAccessMapper.java,5\c\5cd6a081874366e6d0447efa61720201818dcab1 +� +�restaurant-service/restaurant-domain/restaurant-application-service/src/main/java/com/food/order/system/restaurant/domain/service/RestaurantApprovalRequestHelper.java,8\f\8f0b95d00176e23105986f415113f7143b8c9757 +� +iinfrastructure/kafka/kafka-consumer/src/main/java/com/food/order/system/kafka/consumer/KafkaConsumer.java,6\d\6dd91c904a2e322d48824e8e24b38786ed35276b +� +�payment-service/payment-domain/payment-domain-core/src/main/java/com/food/order/system/payment/service/domain/PaymentDomainServiceImpl.java,d\9\d923a60dc4b36941e851edf5b3a65ecf71ec9538 +� +�order-service/order-messaging/src/main/java/com/food/order/system/order/messaging/listener/kafka/PaymentResponseKafkaListener.java,1\a\1ad0cd701ade8de2f4f99bb72f8885f29aef60f5 +� +vorder-service/order-data-access/src/main/java/com/food/order/system/data/access/order/adapter/OrderRepositoryImpl.java,4\8\48b82a9ce4c6810a73182eb48772eea8c92090ad +� +�order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/ports/output/repository/OrderRepository.java,e\2\e24c8eecc55733b12167b6a7cc830d109996200a +� +�order-service/order-messaging/src/main/java/com/food/order/system/order/messaging/listener/kafka/RestaurantApprovalResponseKafkaListener.java,c\c\cc02e68832a991aa60651a755e207a12f99a2008 +� +{order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/exception/OrderDomainException.java,9\0\904804e65376aea729261c26d93b5f48afa2e558 +� +�restaurant-service/restaurant-container/src/main/java/com/food/order/system/restaurant/container/RestaurantServiceApplication.java,c\1\c133c0387dc7d1323f8f5f81d35cccc39c9f77d3 +� +{payment-service/payment-container/src/main/java/com/food/order/system/payment/service/domain/PaymentServiceApplication.java,7\8\78ff547c94bb4c2bb4c10b68b3a21b7473ca6982 +� +korder-service/order-container/src/main/java/com/food/order/system/order/domain/OrderServiceApplication.java,e\c\ec346e351516b2accd95acebd4dd0f74ca1556d6 +� +worder-service/order-data-access/src/main/java/com/food/order/system/data/access/order/mapper/OrderDataAccessMapper.java,5\3\538c34b412bb29a862b242c09fc827eca5109504 +F +infrastructure/pom.xml,2\b\2bb83dd43ff74bd2017133cd9dea6897cfe43de3 \ No newline at end of file diff --git a/common/common-domain/src/main/java/com/food/order/sysyem/event/DomainEvent.java b/common/common-domain/src/main/java/com/food/order/sysyem/event/DomainEvent.java index b05b464..be1257b 100644 --- a/common/common-domain/src/main/java/com/food/order/sysyem/event/DomainEvent.java +++ b/common/common-domain/src/main/java/com/food/order/sysyem/event/DomainEvent.java @@ -4,6 +4,4 @@ package com.food.order.sysyem.event; // Base Domain Event Generic Class public interface DomainEvent { - void fire(); - } diff --git a/infrastructure/saga/src/main/java/com/food/order/system/saga/SagaStep.java b/infrastructure/saga/src/main/java/com/food/order/system/saga/SagaStep.java index 7f5aa50..91f59ce 100644 --- a/infrastructure/saga/src/main/java/com/food/order/system/saga/SagaStep.java +++ b/infrastructure/saga/src/main/java/com/food/order/system/saga/SagaStep.java @@ -1,8 +1,6 @@ package com.food.order.system.saga; -import com.food.order.sysyem.event.DomainEvent; - -public interface SagaStep { - S process(T data); - U rollback(T data); +public interface SagaStep { + void process(T data); + void rollback(T data); } diff --git a/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/adapter/PaymentOutboxRepositoryImpl.java b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/adapter/PaymentOutboxRepositoryImpl.java new file mode 100644 index 0000000..e2b3df8 --- /dev/null +++ b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/adapter/PaymentOutboxRepositoryImpl.java @@ -0,0 +1,63 @@ +package com.food.order.system.data.access.outbox.payment.adapter; + +import com.food.order.system.data.access.outbox.payment.exception.PaymentOutboxNotFoundException; +import com.food.order.system.data.access.outbox.payment.mapper.PaymentOutboxDataAccessMapper; +import com.food.order.system.data.access.outbox.payment.repository.PaymentOutboxJpaRepository; +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.system.saga.SagaStatus; +import com.food.order.sysyem.outbox.model.payment.OrderPaymentOutboxMessage; +import com.food.order.sysyem.ports.output.repository.PaymentOutboxRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +@Component +@RequiredArgsConstructor +public class PaymentOutboxRepositoryImpl implements PaymentOutboxRepository { + + private final PaymentOutboxJpaRepository paymentOutboxJpaRepository; + private final PaymentOutboxDataAccessMapper paymentOutboxDataAccessMapper; + + + @Override + public OrderPaymentOutboxMessage save(OrderPaymentOutboxMessage orderPaymentOutboxMessage) { + return paymentOutboxDataAccessMapper + .paymentOutboxEntityToOrderPaymentOutboxMessage(paymentOutboxJpaRepository + .save(paymentOutboxDataAccessMapper + .orderPaymentOutboxMessageToOutboxEntity(orderPaymentOutboxMessage))); + } + + @Override + public Optional> findByTypeAndOutboxStatusAndSagaStatus(String sagaType, + OutboxStatus outboxStatus, + SagaStatus... sagaStatus) { + return Optional.of(paymentOutboxJpaRepository.findByTypeAndOutboxStatusAndSagaStatusIn(sagaType, + outboxStatus, + Arrays.asList(sagaStatus)) + .orElseThrow(() -> new PaymentOutboxNotFoundException("Payment outbox object " + + "could not be found for saga type " + sagaType)) + .stream() + .map(paymentOutboxDataAccessMapper::paymentOutboxEntityToOrderPaymentOutboxMessage) + .collect(Collectors.toList())); + } + + @Override + public Optional findByTypeAndSagaIdAndSagaStatus(String type, + UUID sagaId, + SagaStatus... sagaStatus) { + return paymentOutboxJpaRepository + .findByTypeAndSagaIdAndSagaStatusIn(type, sagaId, Arrays.asList(sagaStatus)) + .map(paymentOutboxDataAccessMapper::paymentOutboxEntityToOrderPaymentOutboxMessage); + } + + @Override + public void deleteByTypeAndOutboxStatusAndSagaStatus(String type, OutboxStatus outboxStatus, SagaStatus... sagaStatus) { + paymentOutboxJpaRepository.deleteByTypeAndOutboxStatusAndSagaStatusIn(type, outboxStatus, + Arrays.asList(sagaStatus)); + } +} diff --git a/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/entity/PaymentOutboxEntity.java b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/entity/PaymentOutboxEntity.java new file mode 100644 index 0000000..a69f837 --- /dev/null +++ b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/entity/PaymentOutboxEntity.java @@ -0,0 +1,53 @@ +package com.food.order.system.data.access.outbox.payment.entity; + + +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.system.saga.SagaStatus; +import com.food.order.sysyem.valueobject.OrderStatus; +import lombok.*; + +import javax.persistence.*; +import java.time.ZonedDateTime; +import java.util.Objects; +import java.util.UUID; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "payment_outbox") +@Entity +public class PaymentOutboxEntity { + + @Id + private UUID id; + private UUID sagaId; + private ZonedDateTime createdAt; + private ZonedDateTime processedAt; + private String type; + private String payload; + @Enumerated(EnumType.STRING) + private SagaStatus sagaStatus; + @Enumerated(EnumType.STRING) + private OrderStatus orderStatus; + @Enumerated(EnumType.STRING) + private OutboxStatus outboxStatus; + @Version + private int version; + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PaymentOutboxEntity that = (PaymentOutboxEntity) o; + return id.equals(that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} + diff --git a/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/exception/PaymentOutboxNotFoundException.java b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/exception/PaymentOutboxNotFoundException.java new file mode 100644 index 0000000..f3a1a91 --- /dev/null +++ b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/exception/PaymentOutboxNotFoundException.java @@ -0,0 +1,8 @@ +package com.food.order.system.data.access.outbox.payment.exception; + +public class PaymentOutboxNotFoundException extends RuntimeException { + + public PaymentOutboxNotFoundException(String message) { + super(message); + } +} diff --git a/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/mapper/PaymentOutboxDataAccessMapper.java b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/mapper/PaymentOutboxDataAccessMapper.java new file mode 100644 index 0000000..49cef36 --- /dev/null +++ b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/mapper/PaymentOutboxDataAccessMapper.java @@ -0,0 +1,40 @@ +package com.food.order.system.data.access.outbox.payment.mapper; + +import com.food.order.system.data.access.outbox.payment.entity.PaymentOutboxEntity; +import com.food.order.sysyem.outbox.model.payment.OrderPaymentOutboxMessage; +import org.springframework.stereotype.Component; + +@Component +public class PaymentOutboxDataAccessMapper { + + public PaymentOutboxEntity orderPaymentOutboxMessageToOutboxEntity(OrderPaymentOutboxMessage + orderPaymentOutboxMessage) { + return PaymentOutboxEntity.builder() + .id(orderPaymentOutboxMessage.getId()) + .sagaId(orderPaymentOutboxMessage.getSagaId()) + .createdAt(orderPaymentOutboxMessage.getCreatedAt()) + .type(orderPaymentOutboxMessage.getType()) + .payload(orderPaymentOutboxMessage.getPayload()) + .orderStatus(orderPaymentOutboxMessage.getOrderStatus()) + .sagaStatus(orderPaymentOutboxMessage.getSagaStatus()) + .outboxStatus(orderPaymentOutboxMessage.getOutboxStatus()) + .version(orderPaymentOutboxMessage.getVersion()) + .build(); + } + + public OrderPaymentOutboxMessage paymentOutboxEntityToOrderPaymentOutboxMessage(PaymentOutboxEntity + paymentOutboxEntity) { + return OrderPaymentOutboxMessage.builder() + .id(paymentOutboxEntity.getId()) + .sagaId(paymentOutboxEntity.getSagaId()) + .createdAt(paymentOutboxEntity.getCreatedAt()) + .type(paymentOutboxEntity.getType()) + .payload(paymentOutboxEntity.getPayload()) + .orderStatus(paymentOutboxEntity.getOrderStatus()) + .sagaStatus(paymentOutboxEntity.getSagaStatus()) + .outboxStatus(paymentOutboxEntity.getOutboxStatus()) + .version(paymentOutboxEntity.getVersion()) + .build(); + } + +} diff --git a/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/repository/PaymentOutboxJpaRepository.java b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/repository/PaymentOutboxJpaRepository.java new file mode 100644 index 0000000..b5a6cee --- /dev/null +++ b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/payment/repository/PaymentOutboxJpaRepository.java @@ -0,0 +1,28 @@ +package com.food.order.system.data.access.outbox.payment.repository; + +import com.food.order.system.data.access.outbox.payment.entity.PaymentOutboxEntity; +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.system.saga.SagaStatus; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Repository +public interface PaymentOutboxJpaRepository extends JpaRepository { + + Optional> findByTypeAndOutboxStatusAndSagaStatusIn(String type, + OutboxStatus outboxStatus, + List sagaStatus); + + Optional findByTypeAndSagaIdAndSagaStatusIn(String type, + UUID sagaId, + List sagaStatus); + + void deleteByTypeAndOutboxStatusAndSagaStatusIn(String type, + OutboxStatus outboxStatus, + List sagaStatus); + +} diff --git a/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/adapter/ApprovalOutboxRepositoryImpl.java b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/adapter/ApprovalOutboxRepositoryImpl.java new file mode 100644 index 0000000..e26bdd7 --- /dev/null +++ b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/adapter/ApprovalOutboxRepositoryImpl.java @@ -0,0 +1,65 @@ +package com.food.order.system.data.access.outbox.restaurantapproval.adapter; + + +import com.food.order.system.data.access.outbox.restaurantapproval.exception.ApprovalOutboxNotFoundException; +import com.food.order.system.data.access.outbox.restaurantapproval.mapper.ApprovalOutboxDataAccessMapper; +import com.food.order.system.data.access.outbox.restaurantapproval.repository.ApprovalOutboxJpaRepository; +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.system.saga.SagaStatus; +import com.food.order.sysyem.outbox.model.approval.OrderApprovalOutboxMessage; +import com.food.order.sysyem.ports.output.repository.ApprovalOutboxRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +@Component +@RequiredArgsConstructor +public class ApprovalOutboxRepositoryImpl implements ApprovalOutboxRepository { + + private final ApprovalOutboxJpaRepository approvalOutboxJpaRepository; + private final ApprovalOutboxDataAccessMapper approvalOutboxDataAccessMapper; + + + @Override + public OrderApprovalOutboxMessage save(OrderApprovalOutboxMessage orderApprovalOutboxMessage) { + return approvalOutboxDataAccessMapper + .approvalOutboxEntityToOrderApprovalOutboxMessage(approvalOutboxJpaRepository + .save(approvalOutboxDataAccessMapper + .orderCreatedOutboxMessageToOutboxEntity(orderApprovalOutboxMessage))); + } + + @Override + public Optional> findByTypeAndOutboxStatusAndSagaStatus(String sagaType, + OutboxStatus outboxStatus, + SagaStatus... sagaStatus) { + return Optional.of(approvalOutboxJpaRepository.findByTypeAndOutboxStatusAndSagaStatusIn(sagaType, outboxStatus, + Arrays.asList(sagaStatus)) + .orElseThrow(() -> new ApprovalOutboxNotFoundException("Approval outbox object " + + "could be found for saga type " + sagaType)) + .stream() + .map(approvalOutboxDataAccessMapper::approvalOutboxEntityToOrderApprovalOutboxMessage) + .collect(Collectors.toList())); + } + + @Override + public Optional findByTypeAndSagaIdAndSagaStatus(String type, + UUID sagaId, + SagaStatus... sagaStatus) { + return approvalOutboxJpaRepository + .findByTypeAndSagaIdAndSagaStatusIn(type, sagaId, + Arrays.asList(sagaStatus)) + .map(approvalOutboxDataAccessMapper::approvalOutboxEntityToOrderApprovalOutboxMessage); + + } + + @Override + public void deleteByTypeAndOutboxStatusAndSagaStatus(String type, OutboxStatus outboxStatus, SagaStatus... sagaStatus) { + approvalOutboxJpaRepository.deleteByTypeAndOutboxStatusAndSagaStatusIn(type, outboxStatus, + Arrays.asList(sagaStatus)); + } +} diff --git a/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/entity/ApprovalOutboxEntity.java b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/entity/ApprovalOutboxEntity.java new file mode 100644 index 0000000..0f6df94 --- /dev/null +++ b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/entity/ApprovalOutboxEntity.java @@ -0,0 +1,52 @@ +package com.food.order.system.data.access.outbox.restaurantapproval.entity; + +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.system.saga.SagaStatus; +import com.food.order.sysyem.valueobject.OrderStatus; +import lombok.*; + +import javax.persistence.*; +import java.time.ZonedDateTime; +import java.util.Objects; +import java.util.UUID; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "restaurant_approval_outbox") +@Entity +public class ApprovalOutboxEntity { + + @Id + private UUID id; + private UUID sagaId; + private ZonedDateTime createdAt; + private ZonedDateTime processedAt; + private String type; + private String payload; + @Enumerated(EnumType.STRING) + private SagaStatus sagaStatus; + @Enumerated(EnumType.STRING) + private OrderStatus orderStatus; + @Enumerated(EnumType.STRING) + private OutboxStatus outboxStatus; + @Version + private int version; + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ApprovalOutboxEntity that = (ApprovalOutboxEntity) o; + return id.equals(that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} + diff --git a/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/exception/ApprovalOutboxNotFoundException.java b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/exception/ApprovalOutboxNotFoundException.java new file mode 100644 index 0000000..9516b44 --- /dev/null +++ b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/exception/ApprovalOutboxNotFoundException.java @@ -0,0 +1,8 @@ +package com.food.order.system.data.access.outbox.restaurantapproval.exception; + +public class ApprovalOutboxNotFoundException extends RuntimeException { + + public ApprovalOutboxNotFoundException(String message) { + super(message); + } +} diff --git a/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/mapper/ApprovalOutboxDataAccessMapper.java b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/mapper/ApprovalOutboxDataAccessMapper.java new file mode 100644 index 0000000..cd08ec3 --- /dev/null +++ b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/mapper/ApprovalOutboxDataAccessMapper.java @@ -0,0 +1,40 @@ +package com.food.order.system.data.access.outbox.restaurantapproval.mapper; + +import com.food.order.system.data.access.outbox.restaurantapproval.entity.ApprovalOutboxEntity; +import com.food.order.sysyem.outbox.model.approval.OrderApprovalOutboxMessage; +import org.springframework.stereotype.Component; + +@Component +public class ApprovalOutboxDataAccessMapper { + + public ApprovalOutboxEntity orderCreatedOutboxMessageToOutboxEntity(OrderApprovalOutboxMessage + orderApprovalOutboxMessage) { + return ApprovalOutboxEntity.builder() + .id(orderApprovalOutboxMessage.getId()) + .sagaId(orderApprovalOutboxMessage.getSagaId()) + .createdAt(orderApprovalOutboxMessage.getCreatedAt()) + .type(orderApprovalOutboxMessage.getType()) + .payload(orderApprovalOutboxMessage.getPayload()) + .orderStatus(orderApprovalOutboxMessage.getOrderStatus()) + .sagaStatus(orderApprovalOutboxMessage.getSagaStatus()) + .outboxStatus(orderApprovalOutboxMessage.getOutboxStatus()) + .version(orderApprovalOutboxMessage.getVersion()) + .build(); + } + + public OrderApprovalOutboxMessage approvalOutboxEntityToOrderApprovalOutboxMessage(ApprovalOutboxEntity + approvalOutboxEntity) { + return OrderApprovalOutboxMessage.builder() + .id(approvalOutboxEntity.getId()) + .sagaId(approvalOutboxEntity.getSagaId()) + .createdAt(approvalOutboxEntity.getCreatedAt()) + .type(approvalOutboxEntity.getType()) + .payload(approvalOutboxEntity.getPayload()) + .orderStatus(approvalOutboxEntity.getOrderStatus()) + .sagaStatus(approvalOutboxEntity.getSagaStatus()) + .outboxStatus(approvalOutboxEntity.getOutboxStatus()) + .version(approvalOutboxEntity.getVersion()) + .build(); + } + +} diff --git a/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/repository/ApprovalOutboxJpaRepository.java b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/repository/ApprovalOutboxJpaRepository.java new file mode 100644 index 0000000..740d2b2 --- /dev/null +++ b/order-service/order-data-access/src/main/java/com/food/order/system/data/access/outbox/restaurantapproval/repository/ApprovalOutboxJpaRepository.java @@ -0,0 +1,29 @@ +package com.food.order.system.data.access.outbox.restaurantapproval.repository; + + +import com.food.order.system.data.access.outbox.restaurantapproval.entity.ApprovalOutboxEntity; +import com.food.order.system.outbox.OutboxStatus; +import com.food.order.system.saga.SagaStatus; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Repository +public interface ApprovalOutboxJpaRepository extends JpaRepository { + + Optional> findByTypeAndOutboxStatusAndSagaStatusIn(String type, + OutboxStatus outboxStatus, + List sagaStatus); + + Optional findByTypeAndSagaIdAndSagaStatusIn(String type, + UUID sagaId, + List sagaStatus); + + void deleteByTypeAndOutboxStatusAndSagaStatusIn(String type, + OutboxStatus outboxStatus, + List sagaStatus); + +} diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/PaymentResponseMessageListenerImpl.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/PaymentResponseMessageListenerImpl.java index 9546932..fc56410 100644 --- a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/PaymentResponseMessageListenerImpl.java +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/PaymentResponseMessageListenerImpl.java @@ -18,9 +18,8 @@ public class PaymentResponseMessageListenerImpl implements PaymentResponseMessag @Override public void paymentCompleted(PaymentResponse paymentResponse) { - var paidEvent = orderPaymentSaga.process(paymentResponse); - log.info("Payment completed for order with id: {}", paidEvent.getOrder().getId()); - paidEvent.fire(); + orderPaymentSaga.process(paymentResponse); + log.info("Order Payment saga process completed id {}", paymentResponse.getId()); } @Override diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/RestaurantApprovalResponseMessageListenerImpl.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/RestaurantApprovalResponseMessageListenerImpl.java index 5c4ff5f..f8097bd 100644 --- a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/RestaurantApprovalResponseMessageListenerImpl.java +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/RestaurantApprovalResponseMessageListenerImpl.java @@ -22,8 +22,7 @@ public class RestaurantApprovalResponseMessageListenerImpl implements Restaurant @Override public void orderRejected(RestaurantApprovalResponse restaurantApprovalResponse) { - var event = orderApprovalSaga.rollback(restaurantApprovalResponse); + orderApprovalSaga.rollback(restaurantApprovalResponse); log.info("Order Rejected: {}", restaurantApprovalResponse); - event.fire(); } } diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/mapper/OrderDataMapper.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/mapper/OrderDataMapper.java index be99271..8797924 100644 --- a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/mapper/OrderDataMapper.java +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/mapper/OrderDataMapper.java @@ -5,11 +5,14 @@ import com.food.order.system.domain.entity.OrderItem; import com.food.order.system.domain.entity.Product; import com.food.order.system.domain.entity.Restaurant; import com.food.order.system.domain.event.OrderCreatedEvent; +import com.food.order.system.domain.event.OrderPaidEvent; import com.food.order.system.domain.valueobject.StreetAddress; import com.food.order.sysyem.dto.create.CreateOrderCommand; import com.food.order.sysyem.dto.create.CreateOrderResponse; import com.food.order.sysyem.dto.create.OrderAddress; import com.food.order.sysyem.dto.track.TrackOrderResponse; +import com.food.order.sysyem.outbox.model.approval.OrderApprovalEventPayload; +import com.food.order.sysyem.outbox.model.approval.OrderApprovalProduct; import com.food.order.sysyem.outbox.model.payment.OrderPaymentEventPayload; import com.food.order.sysyem.valueobject.*; import org.springframework.stereotype.Component; @@ -20,6 +23,22 @@ import java.util.UUID; @Component public class OrderDataMapper { + public OrderApprovalEventPayload orderPaidEventToOrderApprovalEventPayload(OrderPaidEvent orderPaidEvent) { + return OrderApprovalEventPayload.builder() + .orderId(orderPaidEvent.getOrder().getId().getValue().toString()) + .restaurantId(orderPaidEvent.getOrder().getRestaurantId().getValue().toString()) + .restaurantOrderStatus(RestaurantOrderStatus.PAID.name()) + .products(orderPaidEvent.getOrder().getItems().stream() + .map(orderItem -> OrderApprovalProduct.builder() + .id(orderItem.getProduct().getId().getValue().toString()) + .quantity(orderItem.getQuantity()) + .build()) + .toList()) + .price(orderPaidEvent.getOrder().getPrice().getAmount()) + .createdAt(orderPaidEvent.getCreatedAt()) + .build(); + } + public OrderPaymentEventPayload orderCreatedEventToOrderPaymentEventPayload(OrderCreatedEvent order) { return OrderPaymentEventPayload.builder() .orderId(order.getOrder().getId().getValue().toString()) diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/payment/OrderPaymentOutboxMessage.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/payment/OrderPaymentOutboxMessage.java index 4e3facd..84ee120 100644 --- a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/payment/OrderPaymentOutboxMessage.java +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/model/payment/OrderPaymentOutboxMessage.java @@ -27,12 +27,16 @@ public class OrderPaymentOutboxMessage { private String type; private String payload; + @Setter private SagaStatus sagaStatus; + @Setter private OrderStatus orderStatus; + @Setter private OutboxStatus outboxStatus; + private int version; diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/approval/ApprovalOutboxHelper.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/approval/ApprovalOutboxHelper.java index fbe42e8..8337a38 100644 --- a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/approval/ApprovalOutboxHelper.java +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/outbox/scheduler/approval/ApprovalOutboxHelper.java @@ -1,10 +1,14 @@ package com.food.order.sysyem.outbox.scheduler.approval; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.food.order.system.domain.exception.OrderDomainException; import com.food.order.system.outbox.OutboxStatus; import com.food.order.system.saga.SagaStatus; +import com.food.order.sysyem.outbox.model.approval.OrderApprovalEventPayload; import com.food.order.sysyem.outbox.model.approval.OrderApprovalOutboxMessage; import com.food.order.sysyem.ports.output.repository.ApprovalOutboxRepository; +import com.food.order.sysyem.valueobject.OrderStatus; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -24,6 +28,8 @@ public class ApprovalOutboxHelper { private final ApprovalOutboxRepository approvalOutboxRepository; + private final ObjectMapper objectMapper; + @Transactional(readOnly = true) public Optional> getApprovalOutboxMessageByOutboxStatusAndSagaStatus (OutboxStatus outboxStatus, SagaStatus... sagaStatus) { @@ -63,4 +69,33 @@ public class ApprovalOutboxHelper { sagaStatus); } + + @Transactional + public void saveApprovalOutboxMessage(OrderApprovalEventPayload payload, + OrderStatus orderStatus, + SagaStatus sagaStatus, + OutboxStatus outboxStatus, + UUID sagaId) { + + save(OrderApprovalOutboxMessage.builder() + .id(UUID.randomUUID()) + .type(ORDER_PROCESSING_SAGA) + .createdAt(payload.getCreatedAt()) + .orderStatus(orderStatus) + .sagaStatus(sagaStatus) + .outboxStatus(outboxStatus) + .sagaId(sagaId) + .payload(createPayload(payload)) + .build()); + + + } + + private String createPayload(OrderApprovalEventPayload payload) { + try { + return objectMapper.writeValueAsString(payload); + } catch (JsonProcessingException e) { + throw new OrderDomainException("Failed to create payload for JSON message"); + } + } } diff --git a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/saga/OrderApprovalSaga.java b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/saga/OrderApprovalSaga.java index 314aa46..9246ff7 100644 --- a/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/saga/OrderApprovalSaga.java +++ b/order-service/order-domain/order-application-service/src/main/java/com/food/order/sysyem/saga/OrderApprovalSaga.java @@ -1,10 +1,8 @@ package com.food.order.sysyem.saga; -import com.food.order.system.domain.event.OrderCancelledEvent; import com.food.order.system.domain.service.OrderDomainService; import com.food.order.system.saga.SagaStep; import com.food.order.sysyem.dto.message.RestaurantApprovalResponse; -import com.food.order.sysyem.event.EmptyEvent; import com.food.order.sysyem.helper.OrderSagaHelper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -14,7 +12,7 @@ import org.springframework.transaction.annotation.Transactional; @Slf4j @Component @RequiredArgsConstructor -public class OrderApprovalSaga implements SagaStep { +public class OrderApprovalSaga implements SagaStep { private final OrderDomainService orderDomainService; private final OrderSagaHelper orderSagaHelper; @@ -23,27 +21,24 @@ public class OrderApprovalSaga implements SagaStep { +public class OrderPaymentSaga implements SagaStep { private final OrderDomainService orderDomainService; private final OrderSagaHelper orderSagaHelper; - private final OrderPaidRestaurantRequestMessagePublisher orderPaidRestaurantRequestMessagePublisher; + private final OrderDataMapper orderDataMapper; + private final ApprovalOutboxHelper approvalOutboxHelper; + private final PaymentOutboxHelper paymentOutboxHelper; + @Override @Transactional - public OrderPaidEvent process(PaymentResponse data) { - log.info("Completing payment for order with id: {}", data.getOrderId()); + public void process(PaymentResponse data) { + + var messageResponse = + paymentOutboxHelper.getPaymentOutboxMessageBySagaIdAndSagaStatus( + UUID.fromString(data.getSagaId()), + SagaStatus.STARTED) + .orElseThrow(() -> { + log.error("Payment outbox message not found for saga id: {}", data.getSagaId()); + return new OrderDomainException("Payment outbox message not found for saga id: " + data.getSagaId()); + }); + + + var paidEvent = completePaymentForOrder(data); + + var sagaStatus = orderSagaHelper.orderStatusToSagaStatus(paidEvent.getOrder().getStatus()); + + paymentOutboxHelper.save(getUpdatedPaymentOutboxMessage(messageResponse, + paidEvent.getOrder().getStatus(), + sagaStatus)); + + approvalOutboxHelper.saveApprovalOutboxMessage( + orderDataMapper.orderPaidEventToOrderApprovalEventPayload(paidEvent), + paidEvent.getOrder().getStatus(), + sagaStatus, + OutboxStatus.STARTED, + messageResponse.getSagaId() + ); + + log.info("Payment completed for order with id: {}", paidEvent.getOrder().getId().getValue()); + } + + private OrderPaymentOutboxMessage getUpdatedPaymentOutboxMessage(OrderPaymentOutboxMessage messageResponse, + OrderStatus status, + SagaStatus sagaStatus) { + messageResponse.setProcessedAt(ZonedDateTime.now(ZoneId.of(UTC))); + messageResponse.setOrderStatus(status); + messageResponse.setSagaStatus(sagaStatus); + return messageResponse; + } + + @Override + @Transactional + public void rollback(PaymentResponse data) { + + var messageResponse = + paymentOutboxHelper.getPaymentOutboxMessageBySagaIdAndSagaStatus( + UUID.fromString(data.getSagaId()), + getCurrentSagaStatus(data.getPaymentStatus())) + .orElseThrow( + () -> { + log.error("Payment outbox message not found for saga id: {}", data.getSagaId()); + return new OrderDomainException("Payment outbox message not found for saga id: " + data.getSagaId()); + } + ); + + var orderRollback = rollbackPaymentForOrder(data); + + var sagaStatus = orderSagaHelper.orderStatusToSagaStatus(orderRollback.getStatus()); + + paymentOutboxHelper.save(getUpdatedPaymentOutboxMessage(messageResponse, + orderRollback.getStatus(), + sagaStatus)); + + if (data.getPaymentStatus().equals(PaymentStatus.CANCELED)) { + approvalOutboxHelper.save(getUpdatedApprovalOutboxMessage(data.getSagaId(), + orderRollback.getStatus(), + sagaStatus)); + } + + log.info("Payment rolled back for order with id: {}", orderRollback.getId()); + } + + private OrderApprovalOutboxMessage getUpdatedApprovalOutboxMessage(String sagaId, + OrderStatus orderStatus, + SagaStatus sagaStatus) { + var orderApprovalOutboxMessageResponse = + approvalOutboxHelper.getApprovalOutboxMessageBySagaIdAndSagaStatus( + UUID.fromString(sagaId), + SagaStatus.COMPENSATING); + if (orderApprovalOutboxMessageResponse.isEmpty()) { + throw new OrderDomainException("Approval outbox message could not be found in " + + SagaStatus.COMPENSATING.name() + " status!"); + } + var orderApprovalOutboxMessage = orderApprovalOutboxMessageResponse.get(); + orderApprovalOutboxMessage.setProcessedAt(ZonedDateTime.now(ZoneId.of(UTC))); + orderApprovalOutboxMessage.setOrderStatus(orderStatus); + orderApprovalOutboxMessage.setSagaStatus(sagaStatus); + return orderApprovalOutboxMessage; + } + + private Order rollbackPaymentForOrder(PaymentResponse paymentResponse) { + log.info("Cancelling order with id: {}", paymentResponse.getOrderId()); + var order = orderSagaHelper.findOrder(paymentResponse.getOrderId()); + orderDomainService.cancelOrder(order, paymentResponse.getFailureMessages()); + orderSagaHelper.saveOrder(order); + return order; + } + + private SagaStatus[] getCurrentSagaStatus(PaymentStatus paymentStatus) { + return switch (paymentStatus) { + case COMPLETED -> new SagaStatus[]{SagaStatus.STARTED}; + case CANCELED -> new SagaStatus[]{SagaStatus.PROCESSING}; + case FAILED -> new SagaStatus[]{SagaStatus.STARTED, SagaStatus.PROCESSING}; + }; + } + + private OrderPaidEvent completePaymentForOrder(PaymentResponse data) { var order = orderSagaHelper.findOrder(data.getOrderId()); - var paidEvent = orderDomainService.payOrder(order,orderPaidRestaurantRequestMessagePublisher); + var paidEvent = orderDomainService.payOrder(order); orderSagaHelper.saveOrder(order); log.info("Payment completed for order with id: {}", order.getId()); return paidEvent; } - - @Override - @Transactional - public EmptyEvent rollback(PaymentResponse data) { - log.info("Rolling back payment for order with id: {}", data.getOrderId()); - var order = orderSagaHelper.findOrder(data.getOrderId()); - orderDomainService.cancelOrder(order,data.getFailureMessages()); - orderSagaHelper.saveOrder(order); - log.info("Payment rolled back for order with id: {}", order.getId()); - return EmptyEvent.INSTANCE; - } } diff --git a/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/event/OrderCancelledEvent.java b/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/event/OrderCancelledEvent.java index 7970c1e..2eefa1c 100644 --- a/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/event/OrderCancelledEvent.java +++ b/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/event/OrderCancelledEvent.java @@ -7,16 +7,8 @@ import java.time.ZonedDateTime; public class OrderCancelledEvent extends OrderEvent { - private final DomainEventPublisher publisher; - - - public OrderCancelledEvent(Order order, ZonedDateTime utc, DomainEventPublisher publisher) { + public OrderCancelledEvent(Order order, ZonedDateTime utc) { super(order, utc); - this.publisher = publisher; } - @Override - public void fire() { - publisher.publish(this); - } } diff --git a/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/event/OrderCreatedEvent.java b/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/event/OrderCreatedEvent.java index b0f98da..53efd53 100644 --- a/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/event/OrderCreatedEvent.java +++ b/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/event/OrderCreatedEvent.java @@ -1,20 +1,13 @@ package com.food.order.system.domain.event; import com.food.order.system.domain.entity.Order; -import com.food.order.sysyem.event.publisher.DomainEventPublisher; import java.time.ZonedDateTime; public class OrderCreatedEvent extends OrderEvent { - private final DomainEventPublisher publisher; - public OrderCreatedEvent(Order order, ZonedDateTime createdAt, DomainEventPublisher publisher) { + public OrderCreatedEvent(Order order, ZonedDateTime createdAt) { super(order, createdAt); - this.publisher = publisher; } - @Override - public void fire() { - publisher.publish(this); - } } diff --git a/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/event/OrderPaidEvent.java b/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/event/OrderPaidEvent.java index 6dfaa2d..b38a388 100644 --- a/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/event/OrderPaidEvent.java +++ b/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/event/OrderPaidEvent.java @@ -7,15 +7,10 @@ import java.time.ZonedDateTime; public class OrderPaidEvent extends OrderEvent { - private final DomainEventPublisher publisher; - public OrderPaidEvent(Order order, ZonedDateTime utc, DomainEventPublisher publisher) { + public OrderPaidEvent(Order order, ZonedDateTime utc) { super(order, utc); - this.publisher = publisher; } - @Override - public void fire() { - publisher.publish(this); - } + } diff --git a/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/service/OrderDomainService.java b/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/service/OrderDomainService.java index e514266..6de2853 100644 --- a/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/service/OrderDomainService.java +++ b/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/service/OrderDomainService.java @@ -5,19 +5,18 @@ import com.food.order.system.domain.entity.Restaurant; import com.food.order.system.domain.event.OrderCancelledEvent; import com.food.order.system.domain.event.OrderCreatedEvent; import com.food.order.system.domain.event.OrderPaidEvent; -import com.food.order.sysyem.event.publisher.DomainEventPublisher; import java.util.List; public interface OrderDomainService { - OrderCreatedEvent validateAndInitiateOrder(Order order, Restaurant restaurant, DomainEventPublisher publisher); + OrderCreatedEvent validateAndInitiateOrder(Order order, Restaurant restaurant); - OrderPaidEvent payOrder(Order order,DomainEventPublisher publisher); + OrderPaidEvent payOrder(Order order); void approve(Order order); - OrderCancelledEvent cancelOrderPayment(Order order, List failureMessages,DomainEventPublisher publisher); + OrderCancelledEvent cancelOrderPayment(Order order, List failureMessages); void cancelOrder(Order order, List failureMessages); diff --git a/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/service/impl/OrderDomainServiceImpl.java b/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/service/impl/OrderDomainServiceImpl.java index 1fb74f2..0ece912 100644 --- a/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/service/impl/OrderDomainServiceImpl.java +++ b/order-service/order-domain/order-core-domain/src/main/java/com/food/order/system/domain/service/impl/OrderDomainServiceImpl.java @@ -7,7 +7,6 @@ import com.food.order.system.domain.event.OrderCreatedEvent; import com.food.order.system.domain.event.OrderPaidEvent; import com.food.order.system.domain.exception.OrderDomainException; import com.food.order.system.domain.service.OrderDomainService; -import com.food.order.sysyem.event.publisher.DomainEventPublisher; import lombok.extern.slf4j.Slf4j; import java.time.ZoneId; @@ -20,14 +19,13 @@ public class OrderDomainServiceImpl implements OrderDomainService { private static final String UTC = "UTC"; @Override - public OrderCreatedEvent validateAndInitiateOrder(Order order, Restaurant restaurant, - DomainEventPublisher publisher) { + public OrderCreatedEvent validateAndInitiateOrder(Order order, Restaurant restaurant) { validateRestaurant(restaurant); setOrderProductInformation(order,restaurant); order.validateOrder(); order.initializeOrder(); log.info("Order with id {} initialize successfully", order.getId().getValue()); - return new OrderCreatedEvent(order, ZonedDateTime.now(ZoneId.of(UTC)),publisher); + return new OrderCreatedEvent(order, ZonedDateTime.now(ZoneId.of(UTC))); } private void setOrderProductInformation(Order order, Restaurant restaurant) { @@ -49,10 +47,10 @@ public class OrderDomainServiceImpl implements OrderDomainService { } @Override - public OrderPaidEvent payOrder(Order order, DomainEventPublisher publisher) { + public OrderPaidEvent payOrder(Order order) { order.pay(); log.info("Order with id {} paid successfully", order.getId().getValue()); - return new OrderPaidEvent(order, ZonedDateTime.now(ZoneId.of(UTC)),publisher); + return new OrderPaidEvent(order, ZonedDateTime.now(ZoneId.of(UTC))); } @Override @@ -62,11 +60,10 @@ public class OrderDomainServiceImpl implements OrderDomainService { } @Override - public OrderCancelledEvent cancelOrderPayment(Order order, List failureMessages, - DomainEventPublisher publisher) { + public OrderCancelledEvent cancelOrderPayment(Order order, List failureMessages) { order.initCancel(failureMessages); log.info("Order with id {} cancelled successfully", order.getId().getValue()); - return new OrderCancelledEvent(order, ZonedDateTime.now(ZoneId.of(UTC)),publisher); + return new OrderCancelledEvent(order, ZonedDateTime.now(ZoneId.of(UTC))); } @Override