Implement SAGA Pattern commit and rollback.

This commit is contained in:
Ali CANLI
2022-07-14 15:45:32 +03:00
parent 1f68e35324
commit 22f85a86d8
40 changed files with 343 additions and 118 deletions

View File

@@ -0,0 +1,14 @@
package com.food.order.sysyem.event;
public final class EmptyEvent implements DomainEvent<Void> {
public static final EmptyEvent INSTANCE = new EmptyEvent();
private EmptyEvent() {
}
@Override
public void fire() {
}
}

View File

@@ -10,6 +10,10 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>infrastructure</artifactId>
<packaging>pom</packaging>
<modules>
<module>saga</module>
</modules>
</project>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>infrastructure</artifactId>
<groupId>com.food.order</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>saga</artifactId>
<dependencies>
<dependency>
<groupId>com.food.order</groupId>
<artifactId>common-domain</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,8 @@
package com.food.order.system.saga;
import com.food.order.sysyem.event.DomainEvent;
public interface SagaStep<T, S extends DomainEvent, U extends DomainEvent> {
S process(T data);
U rollback(T data);
}

View File

@@ -10,6 +10,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.UUID;
@Service
@RequiredArgsConstructor
@@ -26,6 +27,12 @@ public class OrderRepositoryImpl implements OrderRepository {
.save(orderDataAccessMapper.orderToOrderEntity(order)));
}
@Override
public Optional<Order> findById(String orderId) {
return orderJpaRepository.findById(UUID.fromString(orderId))
.map(orderDataAccessMapper::orderEntityToOrder);
}
@Override
public Optional<Order> findByTrackingId(TrackingId trackingId) {
return orderJpaRepository.findByTrackingId(trackingId.getValue())

View File

@@ -36,6 +36,11 @@
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>com.food.order</groupId>
<artifactId>saga</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>

View File

@@ -2,6 +2,7 @@ package com.food.order.sysyem;
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.mapper.OrderDataMapper;
import com.food.order.sysyem.ports.output.message.publisher.payment.OrderCreatedPaymentRequestMessagePublisher;
import lombok.RequiredArgsConstructor;

View File

@@ -2,6 +2,7 @@ package com.food.order.sysyem;
import com.food.order.sysyem.dto.message.PaymentResponse;
import com.food.order.sysyem.ports.input.message.listener.payment.PaymentResponseMessageListener;
import com.food.order.sysyem.saga.OrderPaymentSaga;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -13,13 +14,18 @@ import org.springframework.validation.annotation.Validated;
@RequiredArgsConstructor
public class PaymentResponseMessageListenerImpl implements PaymentResponseMessageListener {
private final OrderPaymentSaga orderPaymentSaga;
@Override
public void paymentCompleted(PaymentResponse paymentResponse) {
var paidEvent = orderPaymentSaga.process(paymentResponse);
log.info("Payment completed for order with id: {}", paidEvent.getOrder().getId());
paidEvent.fire();
}
@Override
public void paymentCancelled(PaymentResponse paymentResponse) {
orderPaymentSaga.rollback(paymentResponse);
log.info("Payment cancelled for order with id: {}", paymentResponse.getOrderId());
}
}

View File

@@ -2,6 +2,7 @@ package com.food.order.sysyem;
import com.food.order.sysyem.dto.message.RestaurantApprovalResponse;
import com.food.order.sysyem.ports.input.message.listener.restaurantapproval.RestaurantApprovalResponseMessageListener;
import com.food.order.sysyem.saga.OrderApprovalSaga;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -11,13 +12,18 @@ import org.springframework.stereotype.Service;
@Service
public class RestaurantApprovalResponseMessageListenerImpl implements RestaurantApprovalResponseMessageListener {
private final OrderApprovalSaga orderApprovalSaga;
@Override
public void orderApproved(RestaurantApprovalResponse restaurantApprovalResponse) {
orderApprovalSaga.process(restaurantApprovalResponse);
log.info("Order Approved: {}", restaurantApprovalResponse);
}
@Override
public void orderRejected(RestaurantApprovalResponse restaurantApprovalResponse) {
var event = orderApprovalSaga.rollback(restaurantApprovalResponse);
log.info("Order Rejected: {}", restaurantApprovalResponse);
event.fire();
}
}

View File

@@ -1,4 +1,4 @@
package com.food.order.sysyem;
package com.food.order.sysyem.helper;
import com.food.order.sysyem.dto.create.CreateOrderCommand;
import com.food.order.sysyem.event.publisher.DomainEventPublisher;

View File

@@ -0,0 +1,27 @@
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.sysyem.ports.output.repository.OrderRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@RequiredArgsConstructor
public class OrderSagaHelper {
private final OrderRepository orderRepository;
public Order findOrder(String orderId) {
return orderRepository.findById(orderId)
.orElseThrow(() -> new OrderNotFoundException("Order not found -> Order id :" + orderId));
}
public void saveOrder(Order order) {
orderRepository.save(order);
}
}

View File

@@ -9,6 +9,8 @@ public interface OrderRepository {
Order save(Order order);
Optional<Order> findById(String trackingId);
Optional<Order> findByTrackingId(TrackingId trackingId);

View File

@@ -0,0 +1,47 @@
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 com.food.order.sysyem.ports.output.message.publisher.payment.OrderCancelledPaymentRequestMessagePublisher;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@Component
@RequiredArgsConstructor
public class OrderApprovalSaga implements SagaStep<RestaurantApprovalResponse, EmptyEvent, OrderCancelledEvent> {
private final OrderDomainService orderDomainService;
private final OrderCancelledPaymentRequestMessagePublisher messagePublisher;
private final OrderSagaHelper orderSagaHelper;
@Override
@Transactional
public EmptyEvent process(RestaurantApprovalResponse data) {
log.info("Approving order with id: {}", data.getOrderId());
var order = orderSagaHelper.findOrder(data.getOrderId());
orderDomainService.approve(order);
orderSagaHelper.saveOrder(order);
log.info("Order approved: {}", order);
return EmptyEvent.INSTANCE;
}
@Override
@Transactional
public OrderCancelledEvent rollback(RestaurantApprovalResponse data) {
log.info("Approving order with id: {}", data.getOrderId());
var order = orderSagaHelper.findOrder(data.getOrderId());
var cancelEvent = orderDomainService.cancelOrderPayment(order,data.getFailureMessages(),
messagePublisher);
orderSagaHelper.saveOrder(order);
log.info("Order cancelled: {}", order);
return cancelEvent;
}
}

View File

@@ -0,0 +1,45 @@
package com.food.order.sysyem.saga;
import com.food.order.system.domain.event.OrderPaidEvent;
import com.food.order.system.domain.service.OrderDomainService;
import com.food.order.system.saga.SagaStep;
import com.food.order.sysyem.dto.message.PaymentResponse;
import com.food.order.sysyem.event.EmptyEvent;
import com.food.order.sysyem.helper.OrderSagaHelper;
import com.food.order.sysyem.ports.output.message.publisher.restaurantapproval.OrderPaidRestaurantRequestMessagePublisher;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@Component
@RequiredArgsConstructor
public class OrderPaymentSaga implements SagaStep<PaymentResponse, OrderPaidEvent, EmptyEvent> {
private final OrderDomainService orderDomainService;
private final OrderSagaHelper orderSagaHelper;
private final OrderPaidRestaurantRequestMessagePublisher orderPaidRestaurantRequestMessagePublisher;
@Override
@Transactional
public OrderPaidEvent process(PaymentResponse data) {
log.info("Completing payment for order with id: {}", data.getOrderId());
var order = orderSagaHelper.findOrder(data.getOrderId());
var paidEvent = orderDomainService.payOrder(order,orderPaidRestaurantRequestMessagePublisher);
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;
}
}

View File

@@ -49,7 +49,7 @@ public class OrderMessagingDataMapper {
.setRestaurantId(order.getRestaurantId().getValue().toString())
.setProducts(order.getItems().stream()
.map(item -> Product.newBuilder()
.setId(item.getId().getValue().toString())
.setId(item.getProduct().getId().getValue().toString())
.setQuantity(item.getQuantity())
.build())
.toList())

View File

@@ -2,6 +2,7 @@ package com.food.order.system.payment.data.access.credithistory.adapter;
import com.food.order.system.payment.application.service.ports.output.repository.CreditHistoryRepository;
import com.food.order.system.payment.data.access.credithistory.entity.CreditHistoryEntity;
import com.food.order.system.payment.data.access.credithistory.mapper.CreditHistoryDataAccessMapper;
import com.food.order.system.payment.data.access.credithistory.repository.CreditHistoryJpaRepository;
import com.food.order.system.payment.service.domain.entity.CreditHistory;
@@ -10,6 +11,7 @@ import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Component
public class CreditHistoryRepositoryImpl implements CreditHistoryRepository {
@@ -29,13 +31,15 @@ public class CreditHistoryRepositoryImpl implements CreditHistoryRepository {
.save(creditHistoryDataAccessMapper.creditHistoryToCreditHistoryEntity(creditHistory)));
}
@Override
public Optional<List<CreditHistory>> findByCustomerId(CustomerId customerId) {
return creditHistoryJpaRepository.findByCustomerId(customerId.getValue())
Optional<List<CreditHistoryEntity>> creditHistory =
creditHistoryJpaRepository.findByCustomerId(customerId.getValue());
return creditHistory
.map(creditHistoryList ->
creditHistoryList.stream()
.map(creditHistoryDataAccessMapper::creditHistoryEntityToCreditHistory)
.toList());
.collect(Collectors.toList()));
}
}

View File

@@ -100,7 +100,9 @@ public class PaymentDomainServiceImpl implements PaymentDomainService {
.reduce(Money.ZERO, Money::add);
}
private void updateCreditHistory(Payment payment, List<CreditHistory> creditHistory, TransactionType transactionType) {
private void updateCreditHistory(Payment payment,
List<CreditHistory> creditHistory,
TransactionType transactionType) {
creditHistory.add(
CreditHistory.builder()
.id(new CreditHistoryId(UUID.randomUUID()))

View File

@@ -61,6 +61,13 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.food.order</groupId>
<artifactId>saga</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.food.order</groupId>
<artifactId>payment-domain-core</artifactId>

View File

@@ -6,10 +6,10 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RestaurantBeanConfig {
public class BeanConfiguration {
@Bean
public RestaurantDomainService restaurantService() {
public RestaurantDomainService restaurantDomainService() {
return new RestaurantDomainServiceImpl();
}
}

View File

@@ -75,7 +75,8 @@ AS
p.id AS product_id,
p.name AS product_name,
p.price AS product_price,
p.available AS product_available
p.available AS product_available,
r.active AS product_active
FROM restaurant.restaurants r,
restaurant.products p,
restaurant.restaurant_products rp

View File

@@ -3,7 +3,7 @@ package com.food.order.system.data.access.restaurant.adapter;
import com.food.order.system.data.access.restaurant.mapper.RestaurantDataAccessMapper;
import com.food.order.system.data.access.restaurant.repository.OrderApprovalJpaRepository;
import com.food.order.system.restaurant.domain.core.entity.OrderApproval;
import com.food.ordery.system.restaurant.domain.service.ports.output.repository.OrderApprovalRepository;
import com.food.order.system.restaurant.domain.service.ports.output.repository.OrderApprovalRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

View File

@@ -4,7 +4,7 @@ import com.food.order.system.common.data.access.entity.RestaurantEntity;
import com.food.order.system.common.data.access.repository.RestaurantJpaRepository;
import com.food.order.system.data.access.restaurant.mapper.RestaurantDataAccessMapper;
import com.food.order.system.restaurant.domain.core.entity.Restaurant;
import com.food.ordery.system.restaurant.domain.service.ports.output.repository.RestaurantRepository;
import com.food.order.system.restaurant.domain.service.ports.output.repository.RestaurantRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

View File

@@ -16,7 +16,6 @@ import org.springframework.stereotype.Component;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@Component
public class RestaurantDataAccessMapper {
@@ -39,7 +38,7 @@ public class RestaurantDataAccessMapper {
.price(new Money(entity.getProductPrice()))
.available(entity.getProductActive())
.build())
.collect(Collectors.toList());
.toList();
return Restaurant.builder()
.restaurantId(new RestaurantId(restaurantEntity.getRestaurantId()))

View File

@@ -0,0 +1,83 @@
package com.food.order.system.restaurant.domain.service;
import com.food.order.system.restaurant.domain.core.RestaurantDomainService;
import com.food.order.system.restaurant.domain.core.entity.Restaurant;
import com.food.order.system.restaurant.domain.core.event.OrderApprovalEvent;
import com.food.order.system.restaurant.domain.core.exception.RestaurantNotFoundException;
import com.food.order.system.restaurant.domain.service.dto.RestaurantApprovalRequest;
import com.food.order.system.restaurant.domain.service.mapper.RestaurantDataMapper;
import com.food.order.system.restaurant.domain.service.ports.output.message.publisher.OrderApprovedMessagePublisher;
import com.food.order.system.restaurant.domain.service.ports.output.message.publisher.OrderRejectedMessagePublisher;
import com.food.order.system.restaurant.domain.service.ports.output.repository.OrderApprovalRepository;
import com.food.order.system.restaurant.domain.service.ports.output.repository.RestaurantRepository;
import com.food.order.sysyem.valueobject.OrderId;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@Slf4j
@Component
public class RestaurantApprovalRequestHelper {
private final RestaurantDomainService restaurantDomainService;
private final RestaurantDataMapper restaurantDataMapper;
private final RestaurantRepository restaurantRepository;
private final OrderApprovalRepository orderApprovalRepository;
private final OrderApprovedMessagePublisher orderApprovedMessagePublisher;
private final OrderRejectedMessagePublisher orderRejectedMessagePublisher;
public RestaurantApprovalRequestHelper(RestaurantDomainService restaurantDomainService,
RestaurantDataMapper restaurantDataMapper,
RestaurantRepository restaurantRepository,
OrderApprovalRepository orderApprovalRepository,
OrderApprovedMessagePublisher orderApprovedMessagePublisher,
OrderRejectedMessagePublisher orderRejectedMessagePublisher) {
this.restaurantDomainService = restaurantDomainService;
this.restaurantDataMapper = restaurantDataMapper;
this.restaurantRepository = restaurantRepository;
this.orderApprovalRepository = orderApprovalRepository;
this.orderApprovedMessagePublisher = orderApprovedMessagePublisher;
this.orderRejectedMessagePublisher = orderRejectedMessagePublisher;
}
@Transactional
public OrderApprovalEvent persistOrderApproval(RestaurantApprovalRequest request) {
log.info("Persisting order approval request: {}", request);
List<String> failureMessages = new ArrayList<>();
var restaurant = findRestaurant(request);
var event = restaurantDomainService.validateOrder
(restaurant, failureMessages, orderApprovedMessagePublisher, orderRejectedMessagePublisher);
orderApprovalRepository.save(restaurant.getOrderApproval());
return event;
}
private Restaurant findRestaurant(RestaurantApprovalRequest restaurantApprovalRequest) {
Restaurant restaurant = restaurantDataMapper
.restaurantApprovalRequestToRestaurant(restaurantApprovalRequest);
Optional<Restaurant> restaurantResult = restaurantRepository.findRestaurantInformation(restaurant);
if (restaurantResult.isEmpty()) {
log.error("Restaurant with id " + restaurant.getId().getValue() + " not found!");
throw new RestaurantNotFoundException("Restaurant with id " + restaurant.getId().getValue() +
" not found!");
}
Restaurant restaurantEntity = restaurantResult.get();
restaurant.setActive(restaurantEntity.isActive());
restaurant.getOrderDetail().getProducts().forEach(product ->
restaurantEntity.getOrderDetail().getProducts().forEach(p -> {
if (p.getId().equals(product.getId())) {
product.updateWithConfirmedNamePriceAndAvailablity(p.getName(), p.getPrice(), p.isAvailable());
}
}));
restaurant.getOrderDetail().setId(new OrderId(UUID.fromString(restaurantApprovalRequest.getOrderId())));
return restaurant;
}
}

View File

@@ -1,7 +1,7 @@
package com.food.ordery.system.restaurant.domain.service;
package com.food.order.system.restaurant.domain.service;
import com.food.ordery.system.restaurant.domain.service.dto.RestaurantApprovalRequest;
import com.food.ordery.system.restaurant.domain.service.ports.input.message.listener.RestaurantApprovalRequestMessageListener;
import com.food.order.system.restaurant.domain.service.dto.RestaurantApprovalRequest;
import com.food.order.system.restaurant.domain.service.ports.input.message.listener.RestaurantApprovalRequestMessageListener;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -10,7 +10,6 @@ import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class RestaurantApprovalRequestMessageListenerImpl implements RestaurantApprovalRequestMessageListener {
private final RestaurantApprovalRequestHelper restaurantApprovalRequestHelper;
@Override

View File

@@ -1,4 +1,4 @@
package com.food.ordery.system.restaurant.domain.service.config;
package com.food.order.system.restaurant.domain.service.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -7,9 +7,7 @@ import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "restaurant-service")
public class RestaurantServiceConfig {
public class RestaurantServiceConfigData {
private String restaurantApprovalRequestTopicName;
private String restaurantApprovalResponseTopicName;
}

View File

@@ -1,4 +1,4 @@
package com.food.ordery.system.restaurant.domain.service.dto;
package com.food.order.system.restaurant.domain.service.dto;
import com.food.order.system.restaurant.domain.core.entity.Product;
import com.food.order.sysyem.valueobject.RestaurantOrderStatus;

View File

@@ -1,4 +1,4 @@
package com.food.ordery.system.restaurant.domain.service.mapper;
package com.food.order.system.restaurant.domain.service.mapper;
import com.food.order.system.restaurant.domain.core.entity.OrderDetail;
import com.food.order.system.restaurant.domain.core.entity.Product;
@@ -7,7 +7,7 @@ import com.food.order.sysyem.valueobject.Money;
import com.food.order.sysyem.valueobject.OrderId;
import com.food.order.sysyem.valueobject.OrderStatus;
import com.food.order.sysyem.valueobject.RestaurantId;
import com.food.ordery.system.restaurant.domain.service.dto.RestaurantApprovalRequest;
import com.food.order.system.restaurant.domain.service.dto.RestaurantApprovalRequest;
import org.springframework.stereotype.Component;
import java.util.UUID;
@@ -24,7 +24,6 @@ public class RestaurantDataMapper {
product -> Product.builder()
.productId(product.getId())
.quantity(product.getQuantity())
.build()
).toList())
.totalAmount(new Money(request.getPrice()))

View File

@@ -0,0 +1,7 @@
package com.food.order.system.restaurant.domain.service.ports.input.message.listener;
import com.food.order.system.restaurant.domain.service.dto.RestaurantApprovalRequest;
public interface RestaurantApprovalRequestMessageListener {
void approveOrder(RestaurantApprovalRequest restaurantApprovalRequest);
}

View File

@@ -1,8 +1,8 @@
package com.food.ordery.system.restaurant.domain.service.ports.output.message.publisher;
package com.food.order.system.restaurant.domain.service.ports.output.message.publisher;
import com.food.order.system.restaurant.domain.core.event.OrderApprovedEvent;
import com.food.order.sysyem.event.publisher.DomainEventPublisher;
public interface OrderApprovedMessagePublisher extends DomainEventPublisher<OrderApprovedEvent> {
}

View File

@@ -1,8 +1,8 @@
package com.food.ordery.system.restaurant.domain.service.ports.output.message.publisher;
package com.food.order.system.restaurant.domain.service.ports.output.message.publisher;
import com.food.order.system.restaurant.domain.core.event.OrderRejectedEvent;
import com.food.order.sysyem.event.publisher.DomainEventPublisher;
public interface OrderRejectedMessagePublisher extends DomainEventPublisher<OrderRejectedEvent> {
}

View File

@@ -1,4 +1,4 @@
package com.food.ordery.system.restaurant.domain.service.ports.output.repository;
package com.food.order.system.restaurant.domain.service.ports.output.repository;
import com.food.order.system.restaurant.domain.core.entity.OrderApproval;

View File

@@ -1,4 +1,4 @@
package com.food.ordery.system.restaurant.domain.service.ports.output.repository;
package com.food.order.system.restaurant.domain.service.ports.output.repository;
import com.food.order.system.restaurant.domain.core.entity.Restaurant;

View File

@@ -1,63 +0,0 @@
package com.food.ordery.system.restaurant.domain.service;
import com.food.order.system.restaurant.domain.core.RestaurantDomainService;
import com.food.order.system.restaurant.domain.core.entity.Restaurant;
import com.food.order.system.restaurant.domain.core.event.OrderApprovalEvent;
import com.food.order.system.restaurant.domain.core.exception.RestaurantNotFoundException;
import com.food.order.sysyem.valueobject.OrderId;
import com.food.ordery.system.restaurant.domain.service.dto.RestaurantApprovalRequest;
import com.food.ordery.system.restaurant.domain.service.mapper.RestaurantDataMapper;
import com.food.ordery.system.restaurant.domain.service.ports.output.message.publisher.OrderApprovedMessagePublisher;
import com.food.ordery.system.restaurant.domain.service.ports.output.message.publisher.OrderRejectedMessagePublisher;
import com.food.ordery.system.restaurant.domain.service.ports.output.repository.OrderApprovalRepository;
import com.food.ordery.system.restaurant.domain.service.ports.output.repository.RestaurantRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Slf4j
@Component
@RequiredArgsConstructor
public class RestaurantApprovalRequestHelper {
private final RestaurantDomainService restaurantDomainService;
private final RestaurantDataMapper restaurantDataMapper;
private final RestaurantRepository restaurantRepository;
private final OrderApprovalRepository orderApprovalRepository;
private final OrderApprovedMessagePublisher orderApprovedMessagePublisher;
private final OrderRejectedMessagePublisher orderRejectedMessagePublisher;
@Transactional
public OrderApprovalEvent persistOrderApproval(RestaurantApprovalRequest request) {
log.info("Persisting order approval request: {}", request);
List<String> failureMessages = new ArrayList<>();
var restaurant = findRestaurant(request);
var event = restaurantDomainService.validateOrder
(restaurant, failureMessages, orderApprovedMessagePublisher, orderRejectedMessagePublisher);
orderApprovalRepository.save(restaurant.getOrderApproval());
return event;
}
private Restaurant findRestaurant(RestaurantApprovalRequest request) {
var restaurant = restaurantDataMapper.restaurantApprovalRequestToRestaurant(request);
var resultRestaurant = restaurantRepository.findRestaurantInformation(restaurant).orElseThrow(
() -> new RestaurantNotFoundException("Restaurant not found")
);
restaurant.setActive(resultRestaurant.isActive());
restaurant.getOrderDetail().getProducts().forEach(product -> {
resultRestaurant.getOrderDetail().getProducts().forEach(p -> {
if (p.getId().equals(product.getId())) {
p.updateWithConfirmedNamePriceAndAvailablity(p.getName(),p.getPrice(), p.isAvailable());
}});
});
restaurant.getOrderDetail().setId(new OrderId(UUID.fromString(request.getOrderId())));
return restaurant;
}
}

View File

@@ -1,7 +0,0 @@
package com.food.ordery.system.restaurant.domain.service.ports.input.message.listener;
import com.food.ordery.system.restaurant.domain.service.dto.RestaurantApprovalRequest;
public interface RestaurantApprovalRequestMessageListener {
void approveOrder(RestaurantApprovalRequest request);
}

View File

@@ -4,25 +4,28 @@ package com.food.order.system.restaurant.messaging.listener.kafka;
import com.food.order.system.kafka.consumer.KafkaConsumer;
import com.food.order.system.kafka.order.avro.model.RestaurantApprovalRequestAvroModel;
import com.food.order.system.restaurant.messaging.mapper.RestaurantMessagingDataMapper;
import com.food.ordery.system.restaurant.domain.service.ports.input.message.listener.RestaurantApprovalRequestMessageListener;
import lombok.RequiredArgsConstructor;
import com.food.order.system.restaurant.domain.service.ports.input.message.listener.RestaurantApprovalRequestMessageListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.support.KafkaHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.List;
@Slf4j
@Component
@RequiredArgsConstructor
@Service
public class RestaurantApprovalRequestKafkaListener implements KafkaConsumer<RestaurantApprovalRequestAvroModel> {
private final RestaurantApprovalRequestMessageListener restaurantApprovalRequestMessageListener;
private final RestaurantMessagingDataMapper restaurantMessagingDataMapper;
public RestaurantApprovalRequestKafkaListener(RestaurantApprovalRequestMessageListener restaurantApprovalRequestMessageListener,
RestaurantMessagingDataMapper restaurantMessagingDataMapper) {
this.restaurantApprovalRequestMessageListener = restaurantApprovalRequestMessageListener;
this.restaurantMessagingDataMapper = restaurantMessagingDataMapper;
}
@Override
@KafkaListener(id = "${kafka-consumer-config.restaurant-approval-consumer-group-id}",

View File

@@ -9,7 +9,7 @@ import com.food.order.system.restaurant.domain.core.event.OrderApprovedEvent;
import com.food.order.system.restaurant.domain.core.event.OrderRejectedEvent;
import com.food.order.sysyem.valueobject.ProductId;
import com.food.order.sysyem.valueobject.RestaurantOrderStatus;
import com.food.ordery.system.restaurant.domain.service.dto.RestaurantApprovalRequest;
import com.food.order.system.restaurant.domain.service.dto.RestaurantApprovalRequest;
import org.springframework.stereotype.Component;
import java.util.UUID;

View File

@@ -6,8 +6,8 @@ import com.food.order.system.kafka.producer.KafkaMessageHelper;
import com.food.order.system.kafka.producer.service.KafkaProducer;
import com.food.order.system.restaurant.domain.core.event.OrderApprovedEvent;
import com.food.order.system.restaurant.messaging.mapper.RestaurantMessagingDataMapper;
import com.food.ordery.system.restaurant.domain.service.config.RestaurantServiceConfig;
import com.food.ordery.system.restaurant.domain.service.ports.output.message.publisher.OrderApprovedMessagePublisher;
import com.food.order.system.restaurant.domain.service.config.RestaurantServiceConfigData;
import com.food.order.system.restaurant.domain.service.ports.output.message.publisher.OrderApprovedMessagePublisher;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@@ -19,7 +19,7 @@ public class OrderApprovedKafkaMessagePublisher implements OrderApprovedMessageP
private final RestaurantMessagingDataMapper restaurantMessagingDataMapper;
private final KafkaProducer<String, RestaurantApprovalResponseAvroModel> kafkaProducer;
private final RestaurantServiceConfig restaurantServiceConfigData;
private final RestaurantServiceConfigData restaurantServiceConfigData;
private final KafkaMessageHelper kafkaMessageHelper;

View File

@@ -6,8 +6,8 @@ import com.food.order.system.kafka.producer.KafkaMessageHelper;
import com.food.order.system.kafka.producer.service.KafkaProducer;
import com.food.order.system.restaurant.domain.core.event.OrderRejectedEvent;
import com.food.order.system.restaurant.messaging.mapper.RestaurantMessagingDataMapper;
import com.food.ordery.system.restaurant.domain.service.config.RestaurantServiceConfig;
import com.food.ordery.system.restaurant.domain.service.ports.output.message.publisher.OrderRejectedMessagePublisher;
import com.food.order.system.restaurant.domain.service.config.RestaurantServiceConfigData;
import com.food.order.system.restaurant.domain.service.ports.output.message.publisher.OrderRejectedMessagePublisher;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@@ -19,7 +19,7 @@ public class OrderRejectedKafkaMessagePublisher implements OrderRejectedMessageP
private final RestaurantMessagingDataMapper restaurantMessagingDataMapper;
private final KafkaProducer<String, RestaurantApprovalResponseAvroModel> kafkaProducer;
private final RestaurantServiceConfig restaurantServiceConfigData;
private final RestaurantServiceConfigData restaurantServiceConfigData;
private final KafkaMessageHelper kafkaMessageHelper;