From 3e98f884e6c5d99092cc3047ae97eb2886d05875 Mon Sep 17 00:00:00 2001 From: Pramitha Jayasooriya Date: Sun, 10 Aug 2025 21:17:02 +0530 Subject: [PATCH 1/3] feat: Implement inventory reservation and payment processing event handlers - Added InventoryReservationEventHandler to handle inventory reservation responses. - Introduced PaymentEventHandler for processing payment responses and compensation. - Created PaymentProcessingEventHandler to manage payment processing responses. - Developed PaymentApplication and related configurations for payment service. - Established Kafka topics for payment notifications. - Implemented global exception handling for better error management. - Created necessary domain models, repositories, and services for payment processing. - Added product management features including creation, retrieval, and purchasing. - Implemented error response handling for validation and business exceptions. - Added unit tests for application context loading in order and payment services. --- .DS_Store | Bin 6148 -> 6148 bytes .gitignore | 2 + .vscode/settings.json | 3 + IMPLEMENTATION-GUIDE.md | 474 +++++ README.md | 45 +- k8s/README.md | 513 ++++++ k8s/blue-green-deploy.sh | 324 ++++ k8s/build-images.sh | 325 ++++ k8s/helm-charts/Chart.yaml | 24 + k8s/helm-charts/templates/_helpers.tpl | 100 ++ .../templates/config-server-configmap.yaml | 63 + .../templates/discovery-service.yaml | 103 ++ .../templates/gateway-service.yaml | 105 ++ k8s/helm-charts/templates/ingress.yaml | 113 ++ .../templates/monitoring-configmap.yaml | 205 +++ .../templates/network-policies.yaml | 283 +++ k8s/helm-charts/templates/order-service.yaml | 117 ++ k8s/helm-charts/templates/secrets.yaml | 27 + k8s/helm-charts/values.yaml | 311 ++++ learning-platform/README.md | 491 +++++ learning-platform/index.html | 1577 +++++++++++++++++ learning-platform/script.js | 1133 ++++++++++++ learning-platform/styles.css | 1172 ++++++++++++ services/config-server/pom.xml | 2 +- .../configserver/ConfigServerApplication.java | 2 +- .../configurations/notification-service.yml | 2 +- .../configurations/order-service.yml | 2 +- .../configurations/payment-service.yml | 2 +- .../ConfigServerApplicationTests.java | 2 +- services/customer/pom.xml | 2 +- .../ecommerce/CustomerApplication.java | 2 +- .../ecommerce/customer/Address.java | 2 +- .../ecommerce/customer/Customer.java | 2 +- .../customer/CustomerController.java | 2 +- .../ecommerce/customer/CustomerMapper.java | 2 +- .../customer/CustomerRepository.java | 2 +- .../ecommerce/customer/CustomerRequest.java | 2 +- .../ecommerce/customer/CustomerResponse.java | 2 +- .../ecommerce/customer/CustomerService.java | 4 +- .../exception/CustomerNotFoundException.java | 2 +- .../ecommerce/handler/ErrorResponse.java | 2 +- .../handler/GlobalExceptionHandler.java | 4 +- .../ecommerce/CustomerApplicationTests.java | 2 +- services/discovery/pom.xml | 2 +- .../discovery/DiscoveryApplication.java | 2 +- .../discovery/DiscoveryApplicationTests.java | 2 +- services/gateway/pom.xml | 2 +- .../gateway/GatewayApplication.java | 2 +- .../gateway/GatewayApplicationTests.java | 2 +- services/notification/pom.xml | 2 +- .../ecommerce/NotificationApplication.java | 2 +- .../ecommerce/email/EmailService.java | 12 +- .../ecommerce/email/EmailTemplates.java | 2 +- .../kafka/NotificationsConsumer.java | 16 +- .../ecommerce/kafka/order/Customer.java | 2 +- .../kafka/order/OrderConfirmation.java | 4 +- .../ecommerce/kafka/order/Product.java | 2 +- .../kafka/payment/PaymentConfirmation.java | 2 +- .../kafka/payment/PaymentMethod.java | 2 +- .../ecommerce/notification/Notification.java | 6 +- .../notification/NotificationRepository.java | 2 +- .../notification/NotificationType.java | 2 +- .../templates/order-confirmation.html | 2 +- .../NotificationApplicationTests.java | 2 +- services/order/pom.xml | 22 +- .../ecommerce/OrderApplication.java | 2 +- .../config/CircuitBreakerConfiguration.java | 141 ++ .../config/KafkaOrderTopicConfig.java | 2 +- .../ecommerce/config/RestTemplateConfig.java | 2 +- .../ecommerce/customer/CustomerClient.java | 2 +- .../ecommerce/customer/CustomerResponse.java | 2 +- .../exception/BusinessException.java | 2 +- .../ecommerce/handler/ErrorResponse.java | 2 +- .../handler/GlobalExceptionHandler.java | 4 +- .../ecommerce/kafka/OrderConfirmation.java | 8 +- .../ecommerce/kafka/OrderProducer.java | 2 +- .../ecommerce/order/Order.java | 4 +- .../ecommerce/order/OrderController.java | 2 +- .../ecommerce/order/OrderMapper.java | 2 +- .../ecommerce/order/OrderRepository.java | 2 +- .../ecommerce/order/OrderRequest.java | 4 +- .../ecommerce/order/OrderResponse.java | 2 +- .../ecommerce/order/OrderService.java | 22 +- .../ecommerce/order}/PaymentMethod.java | 2 +- .../ecommerce/orderline/OrderLine.java | 4 +- .../orderline/OrderLineController.java | 2 +- .../ecommerce/orderline/OrderLineMapper.java | 4 +- .../orderline/OrderLineRepository.java | 2 +- .../ecommerce/orderline/OrderLineRequest.java | 2 +- .../orderline/OrderLineResponse.java | 2 +- .../ecommerce/orderline/OrderLineService.java | 2 +- .../ecommerce/payment/PaymentClient.java | 2 +- .../ecommerce/payment/PaymentRequest.java | 6 +- .../ecommerce/product/ProductClient.java | 4 +- .../ecommerce/product/PurchaseRequest.java | 2 +- .../ecommerce/product/PurchaseResponse.java | 2 +- .../pramithamj/ecommerce/saga/OrderSaga.java | 62 + .../ecommerce/saga/OrderSagaOrchestrator.java | 308 ++++ .../ecommerce/saga/OrderSagaRepository.java | 23 + .../ecommerce/saga/SagaRecoveryManager.java | 342 ++++ .../pramithamj/ecommerce/saga/SagaStatus.java | 13 + .../saga/events/CustomerValidationEvent.java | 15 + .../events/InventoryCompensationEvent.java | 18 + .../events/InventoryReservationEvent.java | 18 + .../saga/events/OrderCompletionEvent.java | 21 + .../saga/events/PaymentCompensationEvent.java | 15 + .../saga/events/PaymentProcessingEvent.java | 17 + .../CustomerValidationEventHandler.java | 46 + .../InventoryReservationEventHandler.java | 42 + .../saga/handlers/PaymentEventHandler.java | 81 + .../PaymentProcessingEventHandler.java | 46 + .../ecommerce/OrderApplicationTests.java | 2 +- services/payment/pom.xml | 2 +- .../ecommerce/PaymentApplication.java | 2 +- .../KafkaPaymentTopicConfig.java | 2 +- .../exception/BusinessException.java | 2 +- .../ecommerce/handler/ErrorResponse.java | 2 +- .../handler/GlobalExceptionHandler.java | 4 +- .../notification/NotificationProducer.java | 2 +- .../PaymentNotificationRequest.java | 4 +- .../ecommerce/payment/Customer.java | 2 +- .../ecommerce/payment/Payment.java | 2 +- .../ecommerce/payment/PaymentController.java | 2 +- .../ecommerce/payment/PaymentMapper.java | 2 +- .../ecommerce/payment}/PaymentMethod.java | 2 +- .../ecommerce/payment/PaymentRepository.java | 2 +- .../ecommerce/payment/PaymentRequest.java | 2 +- .../ecommerce/payment/PaymentService.java | 6 +- .../ecommerce/PaymentApplicationTests.java | 2 +- services/product/pom.xml | 2 +- .../ecommerce/ProductApplication.java | 2 +- .../ecommerce/category/Category.java | 4 +- .../exception/ProductPurchaseException.java | 2 +- .../ecommerce/handler/ErrorResponse.java | 2 +- .../handler/GlobalExceptionHandler.java | 4 +- .../ecommerce/product/Product.java | 4 +- .../ecommerce/product/ProductController.java | 2 +- .../ecommerce/product/ProductMapper.java | 4 +- .../product/ProductPurchaseRequest.java | 2 +- .../product/ProductPurchaseResponse.java | 2 +- .../ecommerce/product/ProductRepository.java | 2 +- .../ecommerce/product/ProductRequest.java | 2 +- .../ecommerce/product/ProductResponse.java | 2 +- .../ecommerce/product/ProductService.java | 4 +- .../ecommerce/ProductApplicationTests.java | 2 +- 145 files changed, 8890 insertions(+), 154 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 IMPLEMENTATION-GUIDE.md create mode 100644 k8s/README.md create mode 100755 k8s/blue-green-deploy.sh create mode 100755 k8s/build-images.sh create mode 100644 k8s/helm-charts/Chart.yaml create mode 100644 k8s/helm-charts/templates/_helpers.tpl create mode 100644 k8s/helm-charts/templates/config-server-configmap.yaml create mode 100644 k8s/helm-charts/templates/discovery-service.yaml create mode 100644 k8s/helm-charts/templates/gateway-service.yaml create mode 100644 k8s/helm-charts/templates/ingress.yaml create mode 100644 k8s/helm-charts/templates/monitoring-configmap.yaml create mode 100644 k8s/helm-charts/templates/network-policies.yaml create mode 100644 k8s/helm-charts/templates/order-service.yaml create mode 100644 k8s/helm-charts/templates/secrets.yaml create mode 100644 k8s/helm-charts/values.yaml create mode 100644 learning-platform/README.md create mode 100644 learning-platform/index.html create mode 100644 learning-platform/script.js create mode 100644 learning-platform/styles.css rename services/config-server/src/main/java/com/{alibou => pramithamj}/configserver/ConfigServerApplication.java (91%) rename services/config-server/src/test/java/com/{alibou => pramithamj}/configserver/ConfigServerApplicationTests.java (83%) rename services/customer/src/main/java/com/{alibou => pramithamj}/ecommerce/CustomerApplication.java (89%) rename services/customer/src/main/java/com/{alibou => pramithamj}/ecommerce/customer/Address.java (90%) rename services/customer/src/main/java/com/{alibou => pramithamj}/ecommerce/customer/Customer.java (92%) rename services/customer/src/main/java/com/{alibou => pramithamj}/ecommerce/customer/CustomerController.java (97%) rename services/customer/src/main/java/com/{alibou => pramithamj}/ecommerce/customer/CustomerMapper.java (94%) rename services/customer/src/main/java/com/{alibou => pramithamj}/ecommerce/customer/CustomerRepository.java (78%) rename services/customer/src/main/java/com/{alibou => pramithamj}/ecommerce/customer/CustomerRequest.java (91%) rename services/customer/src/main/java/com/{alibou => pramithamj}/ecommerce/customer/CustomerResponse.java (75%) rename services/customer/src/main/java/com/{alibou => pramithamj}/ecommerce/customer/CustomerService.java (94%) rename services/customer/src/main/java/com/{alibou => pramithamj}/ecommerce/exception/CustomerNotFoundException.java (81%) rename services/customer/src/main/java/com/{alibou => pramithamj}/ecommerce/handler/ErrorResponse.java (72%) rename services/customer/src/main/java/com/{alibou => pramithamj}/ecommerce/handler/GlobalExceptionHandler.java (92%) rename services/customer/src/test/java/com/{alibou => pramithamj}/ecommerce/CustomerApplicationTests.java (84%) rename services/discovery/src/main/java/com/{alibou => pramithamj}/discovery/DiscoveryApplication.java (91%) rename services/discovery/src/test/java/com/{alibou => pramithamj}/discovery/DiscoveryApplicationTests.java (84%) rename services/gateway/src/main/java/com/{alibou => pramithamj}/gateway/GatewayApplication.java (89%) rename services/gateway/src/test/java/com/{alibou => pramithamj}/gateway/GatewayApplicationTests.java (85%) rename services/notification/src/main/java/com/{alibou => pramithamj}/ecommerce/NotificationApplication.java (89%) rename services/notification/src/main/java/com/{alibou => pramithamj}/ecommerce/email/EmailService.java (90%) rename services/notification/src/main/java/com/{alibou => pramithamj}/ecommerce/email/EmailTemplates.java (91%) rename services/notification/src/main/java/com/{alibou => pramithamj}/ecommerce/kafka/NotificationsConsumer.java (81%) rename services/notification/src/main/java/com/{alibou => pramithamj}/ecommerce/kafka/order/Customer.java (69%) rename services/notification/src/main/java/com/{alibou => pramithamj}/ecommerce/kafka/order/OrderConfirmation.java (70%) rename services/notification/src/main/java/com/{alibou => pramithamj}/ecommerce/kafka/order/Product.java (80%) rename services/notification/src/main/java/com/{alibou => pramithamj}/ecommerce/kafka/payment/PaymentConfirmation.java (84%) rename services/notification/src/main/java/com/{alibou => pramithamj}/ecommerce/kafka/payment/PaymentMethod.java (65%) rename services/notification/src/main/java/com/{alibou => pramithamj}/ecommerce/notification/Notification.java (77%) rename services/notification/src/main/java/com/{alibou => pramithamj}/ecommerce/notification/NotificationRepository.java (77%) rename services/notification/src/main/java/com/{alibou => pramithamj}/ecommerce/notification/NotificationType.java (63%) rename services/notification/src/test/java/com/{alibou => pramithamj}/ecommerce/NotificationApplicationTests.java (84%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/OrderApplication.java (93%) create mode 100644 services/order/src/main/java/com/pramithamj/ecommerce/config/CircuitBreakerConfiguration.java rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/config/KafkaOrderTopicConfig.java (90%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/config/RestTemplateConfig.java (88%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/customer/CustomerClient.java (91%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/customer/CustomerResponse.java (72%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/exception/BusinessException.java (80%) rename services/{product/src/main/java/com/alibou => order/src/main/java/com/pramithamj}/ecommerce/handler/ErrorResponse.java (68%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/handler/GlobalExceptionHandler.java (93%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/kafka/OrderConfirmation.java (56%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/kafka/OrderProducer.java (95%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/order/Order.java (93%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/order/OrderController.java (96%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/order/OrderMapper.java (94%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/order/OrderRepository.java (77%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/order/OrderRequest.java (90%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/order/OrderResponse.java (89%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/order/OrderService.java (81%) rename services/{payment/src/main/java/com/alibou/ecommerce/payment => order/src/main/java/com/pramithamj/ecommerce/order}/PaymentMethod.java (69%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/orderline/OrderLine.java (88%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/orderline/OrderLineController.java (94%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/orderline/OrderLineMapper.java (89%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/orderline/OrderLineRepository.java (83%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/orderline/OrderLineRequest.java (75%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/orderline/OrderLineResponse.java (65%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/orderline/OrderLineService.java (94%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/payment/PaymentClient.java (90%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/payment/PaymentRequest.java (56%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/product/ProductClient.java (94%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/product/PurchaseRequest.java (89%) rename services/order/src/main/java/com/{alibou => pramithamj}/ecommerce/product/PurchaseResponse.java (82%) create mode 100644 services/order/src/main/java/com/pramithamj/ecommerce/saga/OrderSaga.java create mode 100644 services/order/src/main/java/com/pramithamj/ecommerce/saga/OrderSagaOrchestrator.java create mode 100644 services/order/src/main/java/com/pramithamj/ecommerce/saga/OrderSagaRepository.java create mode 100644 services/order/src/main/java/com/pramithamj/ecommerce/saga/SagaRecoveryManager.java create mode 100644 services/order/src/main/java/com/pramithamj/ecommerce/saga/SagaStatus.java create mode 100644 services/order/src/main/java/com/pramithamj/ecommerce/saga/events/CustomerValidationEvent.java create mode 100644 services/order/src/main/java/com/pramithamj/ecommerce/saga/events/InventoryCompensationEvent.java create mode 100644 services/order/src/main/java/com/pramithamj/ecommerce/saga/events/InventoryReservationEvent.java create mode 100644 services/order/src/main/java/com/pramithamj/ecommerce/saga/events/OrderCompletionEvent.java create mode 100644 services/order/src/main/java/com/pramithamj/ecommerce/saga/events/PaymentCompensationEvent.java create mode 100644 services/order/src/main/java/com/pramithamj/ecommerce/saga/events/PaymentProcessingEvent.java create mode 100644 services/order/src/main/java/com/pramithamj/ecommerce/saga/handlers/CustomerValidationEventHandler.java create mode 100644 services/order/src/main/java/com/pramithamj/ecommerce/saga/handlers/InventoryReservationEventHandler.java create mode 100644 services/order/src/main/java/com/pramithamj/ecommerce/saga/handlers/PaymentEventHandler.java create mode 100644 services/order/src/main/java/com/pramithamj/ecommerce/saga/handlers/PaymentProcessingEventHandler.java rename services/order/src/test/java/com/{alibou => pramithamj}/ecommerce/OrderApplicationTests.java (84%) rename services/payment/src/main/java/com/{alibou => pramithamj}/ecommerce/PaymentApplication.java (91%) rename services/payment/src/main/java/com/{alibou => pramithamj}/ecommerce/configuration/KafkaPaymentTopicConfig.java (89%) rename services/payment/src/main/java/com/{alibou => pramithamj}/ecommerce/exception/BusinessException.java (80%) rename services/{order/src/main/java/com/alibou => payment/src/main/java/com/pramithamj}/ecommerce/handler/ErrorResponse.java (68%) rename services/payment/src/main/java/com/{alibou => pramithamj}/ecommerce/handler/GlobalExceptionHandler.java (93%) rename services/payment/src/main/java/com/{alibou => pramithamj}/ecommerce/notification/NotificationProducer.java (94%) rename services/payment/src/main/java/com/{alibou => pramithamj}/ecommerce/notification/PaymentNotificationRequest.java (72%) rename services/payment/src/main/java/com/{alibou => pramithamj}/ecommerce/payment/Customer.java (92%) rename services/payment/src/main/java/com/{alibou => pramithamj}/ecommerce/payment/Payment.java (96%) rename services/payment/src/main/java/com/{alibou => pramithamj}/ecommerce/payment/PaymentController.java (94%) rename services/payment/src/main/java/com/{alibou => pramithamj}/ecommerce/payment/PaymentMapper.java (90%) rename services/{order/src/main/java/com/alibou/ecommerce/order => payment/src/main/java/com/pramithamj/ecommerce/payment}/PaymentMethod.java (68%) rename services/payment/src/main/java/com/{alibou => pramithamj}/ecommerce/payment/PaymentRepository.java (77%) rename services/payment/src/main/java/com/{alibou => pramithamj}/ecommerce/payment/PaymentRequest.java (83%) rename services/payment/src/main/java/com/{alibou => pramithamj}/ecommerce/payment/PaymentService.java (82%) rename services/payment/src/test/java/com/{alibou => pramithamj}/ecommerce/PaymentApplicationTests.java (84%) rename services/product/src/main/java/com/{alibou => pramithamj}/ecommerce/ProductApplication.java (89%) rename services/product/src/main/java/com/{alibou => pramithamj}/ecommerce/category/Category.java (87%) rename services/product/src/main/java/com/{alibou => pramithamj}/ecommerce/exception/ProductPurchaseException.java (76%) rename services/{payment/src/main/java/com/alibou => product/src/main/java/com/pramithamj}/ecommerce/handler/ErrorResponse.java (68%) rename services/product/src/main/java/com/{alibou => pramithamj}/ecommerce/handler/GlobalExceptionHandler.java (93%) rename services/product/src/main/java/com/{alibou => pramithamj}/ecommerce/product/Product.java (88%) rename services/product/src/main/java/com/{alibou => pramithamj}/ecommerce/product/ProductController.java (97%) rename services/product/src/main/java/com/{alibou => pramithamj}/ecommerce/product/ProductMapper.java (93%) rename services/product/src/main/java/com/{alibou => pramithamj}/ecommerce/product/ProductPurchaseRequest.java (87%) rename services/product/src/main/java/com/{alibou => pramithamj}/ecommerce/product/ProductPurchaseResponse.java (82%) rename services/product/src/main/java/com/{alibou => pramithamj}/ecommerce/product/ProductRepository.java (84%) rename services/product/src/main/java/com/{alibou => pramithamj}/ecommerce/product/ProductRequest.java (93%) rename services/product/src/main/java/com/{alibou => pramithamj}/ecommerce/product/ProductResponse.java (87%) rename services/product/src/main/java/com/{alibou => pramithamj}/ecommerce/product/ProductService.java (96%) rename services/product/src/test/java/com/{alibou => pramithamj}/ecommerce/ProductApplicationTests.java (84%) diff --git a/.DS_Store b/.DS_Store index f071087a1a2a24c89d8f90ba8efa96a2d661baab..333b5d0a5f57f81a7c5780a4fe7191d40a98943b 100644 GIT binary patch delta 351 zcmZoMXfc=|#>B!ku~2NHo+2aX#(>?7izhHMF>+4kVOm`;$dJR3%86P zx(o$Cu|$RvhBStJpm=Uld2vBfPJR*t1H+D_f}G6a5(9&4j7-cdtZeKY>>S)2vB4Sn z<-sM1C8fnqiAB*MUPyj^P7;irm=uvInIXOg?^{s>A zvvYFu^1FbJ0|G`y2+hC?rD0Szkb%vs(rA7y3ogpb$B)qu~2NHo+2ar#(>?7jO>$nSXOVo%xc24u|bA;Gdl-A2T;joL5}at Vlles)IT(O|k%56_bA-qmW&lvF5552Z diff --git a/.gitignore b/.gitignore index 485dee6..dfc2fd7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .idea +.DS_Store +.fake \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e0f15db --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/IMPLEMENTATION-GUIDE.md b/IMPLEMENTATION-GUIDE.md new file mode 100644 index 0000000..4300080 --- /dev/null +++ b/IMPLEMENTATION-GUIDE.md @@ -0,0 +1,474 @@ +# Complete Implementation Summary & Deployment Guide + +## 🎯 Project Overview + +This comprehensive microservices architecture implementation showcases advanced patterns including Circuit Breaker, Saga Pattern, and Blue-Green deployment with Kubernetes. The project demonstrates real-world enterprise-grade solutions for distributed systems. + +## πŸ—οΈ Architecture Components + +### Core Infrastructure Services +- **Discovery Service** (Eureka) - Service registration and discovery +- **Config Server** - Centralized configuration management +- **API Gateway** - Single entry point with load balancing + +### Business Microservices +- **Customer Service** - Customer profile management (MongoDB) +- **Product Service** - Product catalog and inventory (PostgreSQL) +- **Order Service** - Order processing with Saga orchestration (PostgreSQL + MongoDB for saga state) +- **Payment Service** - Payment processing with circuit breakers (PostgreSQL) +- **Notification Service** - Event-driven notifications (MongoDB) + +### Advanced Patterns Implemented + +#### 1. Circuit Breaker Pattern +- **Implementation**: Resilience4j +- **Services**: All inter-service communications +- **Features**: + - Configurable failure thresholds (30-50%) + - Automatic recovery mechanisms + - Fallback strategies + - Metrics and monitoring integration + +#### 2. Saga Pattern (Orchestration) +- **Implementation**: Event-driven with Kafka +- **Coordinator**: OrderSagaOrchestrator +- **State Management**: MongoDB for saga persistence +- **Features**: + - Automatic compensation logic + - Timeout and retry mechanisms + - Saga recovery manager + - Comprehensive error handling + +#### 3. Blue-Green Deployment +- **Implementation**: Kubernetes + Helm +- **Strategy**: Zero-downtime deployments +- **Features**: + - Automated health checks + - Traffic switching + - Canary deployment support + - Instant rollback capability + +## πŸ“ Project Structure + +``` +fully-completed-microservices/ +β”œβ”€β”€ services/ # All microservices +β”‚ β”œβ”€β”€ discovery/ # Eureka server +β”‚ β”œβ”€β”€ config-server/ # Spring Cloud Config +β”‚ β”œβ”€β”€ gateway/ # API Gateway +β”‚ β”œβ”€β”€ customer/ # Customer management +β”‚ β”œβ”€β”€ product/ # Product catalog +β”‚ β”œβ”€β”€ order/ # Order processing + Saga +β”‚ β”‚ └── src/main/java/com/alibou/ecommerce/ +β”‚ β”‚ β”œβ”€β”€ order/ # Order entities and controllers +β”‚ β”‚ └── saga/ # Saga pattern implementation +β”‚ β”‚ β”œβ”€β”€ OrderSaga.java # Saga state entity +β”‚ β”‚ β”œβ”€β”€ SagaStatus.java # Saga states enum +β”‚ β”‚ β”œβ”€β”€ OrderSagaOrchestrator.java # Saga coordinator +β”‚ β”‚ β”œβ”€β”€ OrderSagaRepository.java # Data access +β”‚ β”‚ β”œβ”€β”€ SagaRecoveryManager.java # Timeout handling +β”‚ β”‚ β”œβ”€β”€ events/ # Kafka event classes +β”‚ β”‚ └── handlers/ # Event handlers +β”‚ β”œβ”€β”€ payment/ # Payment processing +β”‚ └── notification/ # Email notifications +β”œβ”€β”€ k8s/ # Kubernetes deployments +β”‚ β”œβ”€β”€ helm-charts/ # Helm chart templates +β”‚ β”‚ β”œβ”€β”€ Chart.yaml # Chart metadata +β”‚ β”‚ β”œβ”€β”€ values.yaml # Configuration values +β”‚ β”‚ └── templates/ # K8s resource templates +β”‚ β”œβ”€β”€ blue-green-deploy.sh # Deployment automation +β”‚ β”œβ”€β”€ build-images.sh # Docker image builder +β”‚ └── README.md # K8s deployment guide +β”œβ”€β”€ learning-platform/ # Interactive learning platform +β”œβ”€β”€ docker-compose.yml # Local development setup +└── README.md # Main project documentation +``` + +## πŸš€ Quick Start Guide + +### Prerequisites +- Java 17+ +- Maven 3.6+ +- Docker & Docker Compose +- Kubernetes cluster (optional, for K8s deployment) +- Git + +### 1. Clone Repository +```bash +git clone https://github.com/PramithaMJ/fully-completed-microservices.git +cd fully-completed-microservices +``` + +### 2. Start Infrastructure (Local Development) +```bash +# Start databases, Kafka, Zipkin +docker-compose up -d + +# Wait for services to be ready (about 60 seconds) +docker-compose ps +``` + +### 3. Start Core Services (in order) +```bash +# 1. Config Server (Port 8888) +cd services/config-server +./mvnw spring-boot:run + +# 2. Discovery Service (Port 8761) +cd ../discovery +./mvnw spring-boot:run + +# 3. API Gateway (Port 8222) +cd ../gateway +./mvnw spring-boot:run +``` + +### 4. Start Business Services +```bash +# Start all business services (any order) +cd ../customer && ./mvnw spring-boot:run & +cd ../product && ./mvnw spring-boot:run & +cd ../order && ./mvnw spring-boot:run & +cd ../payment && ./mvnw spring-boot:run & +cd ../notification && ./mvnw spring-boot:run & +``` + +### 5. Verify Deployment +```bash +# Check service registry +curl http://localhost:8761 + +# Test API endpoints +curl http://localhost:8222/api/customers +curl http://localhost:8222/api/products +``` + +## πŸ§ͺ Testing Advanced Patterns + +### Circuit Breaker Testing +```bash +# Generate load to trigger circuit breaker +for i in {1..50}; do + curl -X GET "http://localhost:8222/api/customers/999" & +done + +# Check circuit breaker metrics +curl http://localhost:8222/actuator/prometheus | grep resilience4j +``` + +### Saga Pattern Testing +```bash +# 1. Create a customer +curl -X POST http://localhost:8222/api/customers \ + -H "Content-Type: application/json" \ + -d '{ + "firstname": "John", + "lastname": "Doe", + "email": "john@example.com", + "address": { + "street": "123 Main St", + "houseNumber": "123", + "zipCode": "12345" + } + }' + +# 2. Create a product +curl -X POST http://localhost:8222/api/products \ + -H "Content-Type: application/json" \ + -d '{ + "name": "MacBook Pro", + "description": "Apple MacBook Pro 16-inch", + "availableQuantity": 10, + "price": 2499.99 + }' + +# 3. Place order (triggers Saga) +curl -X POST http://localhost:8222/api/orders \ + -H "Content-Type: application/json" \ + -d '{ + "customerId": "CUSTOMER_ID_FROM_STEP_1", + "products": [ + { + "productId": PRODUCT_ID_FROM_STEP_2, + "quantity": 1 + } + ], + "paymentMethod": "CREDIT_CARD" + }' + +# 4. Check saga state in MongoDB +# Connect to MongoDB and check order_saga collection +``` + +## ☸️ Kubernetes Deployment + +### Prerequisites for K8s +```bash +# Install required tools +brew install kubectl helm + +# For local development +brew install kind + +# Create Kind cluster +kind create cluster --config=k8s/kind-config.yaml +``` + +### Deploy with Blue-Green Strategy +```bash +cd k8s + +# Build Docker images +./build-images.sh + +# Deploy with Helm (Blue-Green enabled) +helm install ecommerce-microservices ./helm-charts \ + --namespace ecommerce \ + --create-namespace \ + --set bluegreen.enabled=true \ + --set bluegreen.activeSlot=blue \ + --wait + +# Check deployment status +./blue-green-deploy.sh status +``` + +### Blue-Green Deployment Operations +```bash +# Deploy to inactive slot (Green) +./blue-green-deploy.sh deploy + +# Switch traffic to new deployment +./blue-green-deploy.sh switch + +# Complete deployment (with cleanup) +./blue-green-deploy.sh full-deploy + +# Rollback if needed +./blue-green-deploy.sh rollback + +# Canary deployment (10% traffic) +./blue-green-deploy.sh canary 10 + +# Promote canary to full +./blue-green-deploy.sh promote +``` + +## πŸ“Š Monitoring & Observability + +### Service Discovery Dashboard +- **URL**: http://localhost:8761 +- **Purpose**: View all registered services and health status + +### Distributed Tracing +- **Zipkin UI**: http://localhost:9411 +- **Features**: Request tracing across services, performance analysis + +### Database Management +- **PostgreSQL**: http://localhost:5050 (PgAdmin) +- **MongoDB**: http://localhost:8081 (Mongo Express) + +### Email Testing +- **MailDev**: http://localhost:1080 +- **Purpose**: View sent emails from notification service + +### Prometheus Metrics (K8s) +- **Circuit Breaker**: `resilience4j_circuitbreaker_*` +- **Saga Pattern**: `saga_*` +- **Application**: `http_server_requests_*` + +## 🎯 Learning Outcomes + +### Design Patterns Mastered +1. **Service Registry & Discovery** - Dynamic service location +2. **API Gateway** - Single entry point with cross-cutting concerns +3. **Database per Service** - Data isolation and independence +4. **Event-Driven Architecture** - Asynchronous communication +5. **Circuit Breaker** - Fault tolerance and resilience +6. **Saga Pattern** - Distributed transaction management +7. **Blue-Green Deployment** - Zero-downtime deployments + +### Technical Skills Developed +- **Spring Boot 3.2.5** - Modern Java development +- **Spring Cloud** - Microservices infrastructure +- **Kafka** - Event streaming and messaging +- **MongoDB & PostgreSQL** - Polyglot persistence +- **Docker** - Containerization +- **Kubernetes** - Container orchestration +- **Helm** - Package management +- **Resilience4j** - Circuit breaker implementation + +### Enterprise Best Practices +- **Configuration Management** - Externalized configuration +- **Service Mesh Patterns** - Service-to-service communication +- **Monitoring & Alerting** - Observability implementation +- **Security** - Network policies and secrets management +- **CI/CD** - Automated deployment pipelines + +## πŸ”§ Advanced Configuration + +### Circuit Breaker Settings +```yaml +# In values.yaml +order: + circuitBreaker: + enabled: true + failureRateThreshold: 30 # 30% failure rate + waitDurationInOpenState: 60000 # 60 seconds + slidingWindowSize: 20 # 20 calls window +``` + +### Saga Pattern Settings +```yaml +order: + saga: + enabled: true + retryAttempts: 3 # Max retry attempts + compensationTimeout: 30000 # 30 seconds timeout +``` + +### Scaling Configuration +```yaml +order: + autoscaling: + enabled: true + minReplicas: 3 + maxReplicas: 10 + targetCPUUtilizationPercentage: 70 +``` + +## πŸ› Troubleshooting + +### Common Issues + +#### 1. Service Registration Problems +```bash +# Check Eureka dashboard +curl http://localhost:8761 + +# Verify service configuration +curl http://localhost:8888/order-service/default +``` + +#### 2. Circuit Breaker Not Working +```bash +# Check circuit breaker metrics +curl http://localhost:8070/actuator/prometheus | grep resilience4j + +# Verify configuration +curl http://localhost:8070/actuator/circuitbreakers +``` + +#### 3. Saga State Issues +```bash +# Connect to MongoDB and check saga collection +docker exec -it mongodb mongo ecommerce --eval "db.order_saga.find().pretty()" + +# Check Kafka topics +docker exec -it kafka kafka-topics --list --bootstrap-server localhost:9092 +``` + +#### 4. Kubernetes Deployment Issues +```bash +# Check pod status +kubectl get pods -n ecommerce + +# Check service logs +kubectl logs -f deployment/order-service-blue -n ecommerce + +# Check ingress +kubectl describe ingress -n ecommerce +``` + +## πŸ“ˆ Performance Optimization + +### JVM Tuning +```yaml +order: + env: + - name: JAVA_OPTS + value: "-Xmx768m -XX:+UseG1GC -XX:G1HeapRegionSize=16m" +``` + +### Database Connection Pooling +```yaml +spring: + datasource: + hikari: + maximum-pool-size: 20 + minimum-idle: 5 + connection-timeout: 30000 +``` + +### Kafka Optimization +```yaml +spring: + kafka: + producer: + batch-size: 16384 + linger-ms: 10 + compression-type: snappy +``` + +## πŸ”’ Security Best Practices + +### Network Policies (K8s) +- Enabled by default in Helm chart +- Restricts inter-service communication +- Database access controls + +### Secrets Management +- All sensitive data in Kubernetes secrets +- Environment-specific configurations +- Encrypted at rest + +### Authentication & Authorization +- JWT tokens for service-to-service communication +- RBAC configuration for K8s resources +- API Gateway security filters + +## πŸš€ Production Readiness + +### Deployment Checklist +- [ ] Resource limits configured +- [ ] Health checks implemented +- [ ] Monitoring and alerting setup +- [ ] Security policies applied +- [ ] Backup strategy implemented +- [ ] Disaster recovery plan +- [ ] Load testing completed +- [ ] Documentation updated + +### Operational Procedures +- **Deployment**: Use blue-green strategy +- **Monitoring**: Prometheus + Grafana dashboards +- **Alerting**: Critical alerts for circuit breakers and saga failures +- **Backup**: Automated database backups +- **Scaling**: HPA based on CPU/memory metrics + +## πŸ‘¨β€πŸ’» Author & Support + +**Created by**: Pramitha Jayasooriya +**GitHub**: https://github.com/PramithaMJ/fully-completed-microservices +**Website**: https://pramithamj.live +**Support**: https://buymeacoffee.com/lpramithamm + +### Contributing +1. Fork the repository +2. Create your feature branch +3. Commit your changes +4. Push to the branch +5. Create a Pull Request + +### License +This project is licensed under the MIT License - see the LICENSE file for details. + +--- + +## πŸŽ‰ Conclusion + +This implementation provides a complete, production-ready microservices architecture with advanced patterns that are essential for enterprise applications. The combination of Circuit Breaker, Saga Pattern, and Blue-Green deployment ensures high availability, data consistency, and zero-downtime deployments. + +The project serves as both a learning platform and a reference implementation for building scalable, resilient distributed systems using modern Java and Kubernetes technologies. + +**Happy Learning and Coding! πŸš€** diff --git a/README.md b/README.md index fc76934..6887fa6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,47 @@ -# Fully Completed Microservices Project +# πŸ—οΈ Complete Microservices Architecture with Advanced Patterns + +[![Java](https://img.shields.io/badge/Java-17-orange)](https://www.oracle.com/java/) +[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.2.5-brightgreen)](https://spring.io/projects/spring-boot) +[![Spring Cloud](https://img.shields.io/badge/Spring%20Cloud-2023.0.1-blue)](https://spring.io/projects/spring-cloud) +[![Kubernetes](https://img.shields.io/badge/Kubernetes-1.28+-blue)](https://kubernetes.io/) +[![Docker](https://img.shields.io/badge/Docker-24.0+-blue)](https://www.docker.com/) +[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) + +> A comprehensive, production-ready microservices implementation featuring **Circuit Breaker Pattern**, **Saga Pattern**, and **Blue-Green Deployment** with Kubernetes orchestration. + +## 🎯 Project Overview + +This project demonstrates enterprise-grade microservices architecture patterns through a complete e-commerce platform implementation. It showcases advanced distributed systems patterns essential for building resilient, scalable applications. + +### πŸ† Key Features + +- βœ… **6 Production-Ready Microservices** +- βœ… **Circuit Breaker Pattern** with Resilience4j +- βœ… **Saga Pattern** for distributed transactions +- βœ… **Blue-Green Deployment** with Kubernetes +- βœ… **Event-Driven Architecture** with Kafka +- βœ… **Distributed Tracing** with Zipkin +- βœ… **Interactive Learning Platform** +- βœ… **Zero-Downtime Deployments** + +## πŸ—οΈ Advanced Patterns Implemented + +### πŸ›‘οΈ Circuit Breaker Pattern +- **Technology**: Resilience4j +- **Implementation**: All inter-service communications +- **Features**: Configurable thresholds, automatic recovery, fallback strategies +- **Monitoring**: Prometheus metrics integration + +### πŸ”„ Saga Pattern (Orchestration) +- **Coordinator**: OrderSagaOrchestrator +- **State Storage**: MongoDB +- **Event Bus**: Apache Kafka +- **Features**: Automatic compensation, timeout handling, retry mechanisms + +### πŸ”΅πŸŸ’ Blue-Green Deployment +- **Platform**: Kubernetes with Helm +- **Features**: Zero-downtime deployment, instant rollback, canary support +- **Automation**: Custom deployment scripts with health checks ## Overview diff --git a/k8s/README.md b/k8s/README.md new file mode 100644 index 0000000..e579575 --- /dev/null +++ b/k8s/README.md @@ -0,0 +1,513 @@ +# Kubernetes Deployment Guide for E-commerce Microservices + +## Overview + +This guide provides comprehensive instructions for deploying the e-commerce microservices platform on Kubernetes using Helm charts with advanced patterns including: + +- **Circuit Breaker Pattern** with Resilience4j +- **Saga Pattern** for distributed transactions +- **Blue-Green Deployment** for zero-downtime deployments +- **Auto-scaling** and **Health Monitoring** +- **Security** with Network Policies + +## Architecture + +### Microservices Components + +- **Discovery Service** (Eureka) - Service registration and discovery +- **Config Server** - Centralized configuration management +- **API Gateway** - Single entry point with routing and load balancing +- **Customer Service** - Customer management with circuit breakers +- **Product Service** - Product catalog with inventory management +- **Order Service** - Order processing with Saga orchestration +- **Payment Service** - Payment processing with circuit breakers +- **Notification Service** - Email and messaging notifications + +### Infrastructure Components + +- **MongoDB** - Document database for orders and saga state +- **PostgreSQL** - Relational database for customers and products +- **Apache Kafka** - Event streaming for saga pattern +- **Prometheus** - Metrics collection and monitoring +- **Grafana** - Metrics visualization + +## Prerequisites + +### Required Tools + +```bash +# Install kubectl +curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl" +chmod +x kubectl +sudo mv kubectl /usr/local/bin/ + +# Install Helm +brew install helm + +# Install jq for JSON parsing +brew install jq +``` + +### Kubernetes Cluster + +You need a Kubernetes cluster with: +- **Minimum 4 CPU cores and 8GB RAM** +- **LoadBalancer support** (for cloud providers) +- **Storage Class** for persistent volumes +- **Ingress Controller** (nginx recommended) + +#### Local Development with Kind + +```bash +# Install Kind +brew install kind + +# Create cluster with ingress support +cat <10%) +3. **ServiceDown** - Service unavailability +4. **HighMemoryUsage** - Memory usage >85% +5. **HighCPUUsage** - CPU usage >80% + +## Testing the Deployment + +### Health Checks + +```bash +# Check all services health +kubectl get pods -n ecommerce + +# Check specific service +kubectl describe pod -l app.kubernetes.io/component=order-service -n ecommerce + +# Check service logs +kubectl logs -f deployment/order-service-blue -n ecommerce +``` + +### API Testing + +```bash +# Get external IP +export GATEWAY_IP=$(kubectl get service api-gateway-blue -n ecommerce -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + +# Test API endpoints +curl -X GET "http://$GATEWAY_IP:8080/api/v1/customers" +curl -X GET "http://$GATEWAY_IP:8080/api/v1/products" + +# Test order creation (Saga Pattern) +curl -X POST "http://$GATEWAY_IP:8080/api/v1/orders" \ + -H "Content-Type: application/json" \ + -d '{ + "customerId": "1", + "products": [ + {"productId": 1, "quantity": 2} + ], + "paymentMethod": "CREDIT_CARD" + }' +``` + +### Circuit Breaker Testing + +```bash +# Generate load to trigger circuit breaker +for i in {1..100}; do + curl -X GET "http://$GATEWAY_IP:8080/api/v1/customers/999" & +done + +# Check circuit breaker metrics +curl -s "http://$GATEWAY_IP:8080/actuator/prometheus" | grep circuit +``` + +## Troubleshooting + +### Common Issues + +#### 1. Services Not Starting +```bash +# Check pod events +kubectl describe pod -n ecommerce + +# Check service logs +kubectl logs -n ecommerce --previous + +# Common causes: +# - Database connection issues +# - Configuration server unavailable +# - Resource constraints +``` + +#### 2. Circuit Breaker Not Working +```bash +# Verify circuit breaker configuration +kubectl get configmap order-service-config -n ecommerce -o yaml + +# Check metrics endpoint +curl "http://:8070/actuator/prometheus" | grep resilience4j +``` + +#### 3. Saga Pattern Issues +```bash +# Check Kafka connectivity +kubectl exec -it -n ecommerce -- kafka-topics.sh --list --bootstrap-server localhost:9092 + +# Check saga state in MongoDB +kubectl exec -it -n ecommerce -- mongo --eval "db.order_saga.find().pretty()" + +# Check order service logs for saga execution +kubectl logs -f deployment/order-service-blue -n ecommerce | grep -i saga +``` + +#### 4. Blue-Green Deployment Issues +```bash +# Check deployment status +./blue-green-deploy.sh status + +# Rollback if needed +./blue-green-deploy.sh rollback + +# Check ingress configuration +kubectl get ingress -n ecommerce -o yaml +``` + +### Performance Tuning + +#### 1. Resource Optimization +```yaml +# Adjust resource requests/limits in values.yaml +order: + resources: + requests: + cpu: 500m + memory: 512Mi + limits: + cpu: 1000m + memory: 1Gi +``` + +#### 2. JVM Tuning +```yaml +# Add JVM options as environment variables +order: + env: + - name: JAVA_OPTS + value: "-Xmx768m -XX:+UseG1GC -XX:G1HeapRegionSize=16m" +``` + +#### 3. Database Connection Pooling +```yaml +# Configure database connection pools +order: + env: + - name: SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE + value: "20" + - name: SPRING_DATASOURCE_HIKARI_MINIMUM_IDLE + value: "5" +``` + +## Security Best Practices + +### 1. Network Policies +Network policies are enabled by default and restrict inter-service communication. + +### 2. Secrets Management +All sensitive data is stored in Kubernetes secrets: +```bash +# View secrets (base64 encoded) +kubectl get secret ecommerce-microservices-secrets -n ecommerce -o yaml +``` + +### 3. RBAC Configuration +```yaml +# Create service account with minimal permissions +apiVersion: v1 +kind: ServiceAccount +metadata: + name: ecommerce-service-account + namespace: ecommerce +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + namespace: ecommerce + name: ecommerce-role +rules: +- apiGroups: [""] + resources: ["configmaps", "secrets"] + verbs: ["get", "list"] +``` + +## Backup and Disaster Recovery + +### Database Backups +```bash +# MongoDB backup +kubectl exec -n ecommerce -- mongodump --out /tmp/backup +kubectl cp ecommerce/:/tmp/backup ./mongodb-backup + +# PostgreSQL backup +kubectl exec -n ecommerce -- pg_dump -U ecommerce ecommerce > postgresql-backup.sql +``` + +### Configuration Backups +```bash +# Backup Helm values +helm get values ecommerce-microservices -n ecommerce > values-backup.yaml + +# Backup all Kubernetes resources +kubectl get all -n ecommerce -o yaml > kubernetes-backup.yaml +``` + +## Upgrading + +### Application Updates +```bash +# Update application version +helm upgrade ecommerce-microservices ./helm-charts \ + --namespace ecommerce \ + --set order.image.tag=v1.1.0 \ + --reuse-values + +# Blue-green upgrade +./blue-green-deploy.sh full-deploy +``` + +### Infrastructure Updates +```bash +# Update Helm dependencies +helm dependency update ./helm-charts + +# Upgrade with new dependencies +helm upgrade ecommerce-microservices ./helm-charts \ + --namespace ecommerce \ + --reuse-values +``` + +## Production Checklist + +- [ ] Resource limits configured appropriately +- [ ] Health checks configured and working +- [ ] Monitoring and alerting setup +- [ ] Network policies enabled +- [ ] Secrets properly managed +- [ ] Backup strategy implemented +- [ ] Disaster recovery plan documented +- [ ] Load testing completed +- [ ] Security scanning performed +- [ ] Documentation updated + +## Support and Troubleshooting + +For issues and support: +1. Check the troubleshooting section above +2. Review application logs: `kubectl logs -f deployment/ -n ecommerce` +3. Check cluster events: `kubectl get events -n ecommerce --sort-by='.lastTimestamp'` +4. Contact: **Pramitha Jayasooriya** - pramithajayasooriya@example.com + +--- + +**Author**: Pramitha Jayasooriya +**Version**: 1.0.0 +**Last Updated**: August 2025 diff --git a/k8s/blue-green-deploy.sh b/k8s/blue-green-deploy.sh new file mode 100755 index 0000000..bbed38f --- /dev/null +++ b/k8s/blue-green-deploy.sh @@ -0,0 +1,324 @@ +#!/bin/bash + +# Blue-Green Deployment Manager for E-commerce Microservices +# Author: Pramitha Jayasooriya +# Version: 1.0.0 + +set -e + +# Configuration +NAMESPACE=${NAMESPACE:-"ecommerce"} +RELEASE_NAME=${RELEASE_NAME:-"ecommerce-microservices"} +CHART_PATH=${CHART_PATH:-"./k8s/helm-charts"} +TIMEOUT=${TIMEOUT:-"600s"} + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Logging functions +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Function to get current active slot +get_active_slot() { + helm get values $RELEASE_NAME -n $NAMESPACE -o json | jq -r '.bluegreen.activeSlot // "blue"' +} + +# Function to get inactive slot +get_inactive_slot() { + local active_slot=$(get_active_slot) + if [ "$active_slot" = "blue" ]; then + echo "green" + else + echo "blue" + fi +} + +# Function to check if deployment is healthy +check_deployment_health() { + local slot=$1 + local services=("discovery-service" "config-server" "api-gateway" "customer-service" "product-service" "order-service" "payment-service" "notification-service") + + log_info "Checking health of $slot deployment..." + + for service in "${services[@]}"; do + local deployment_name="${service}-${slot}" + + # Check if deployment exists + if ! kubectl get deployment $deployment_name -n $NAMESPACE > /dev/null 2>&1; then + log_warning "Deployment $deployment_name not found, skipping health check" + continue + fi + + # Check deployment status + local ready_replicas=$(kubectl get deployment $deployment_name -n $NAMESPACE -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "0") + local desired_replicas=$(kubectl get deployment $deployment_name -n $NAMESPACE -o jsonpath='{.spec.replicas}') + + if [ "$ready_replicas" != "$desired_replicas" ]; then + log_error "Deployment $deployment_name is not ready ($ready_replicas/$desired_replicas)" + return 1 + fi + + log_info "βœ“ $deployment_name is healthy ($ready_replicas/$desired_replicas)" + done + + log_success "$slot deployment is healthy" + return 0 +} + +# Function to run health checks +run_health_checks() { + local slot=$1 + local gateway_service="api-gateway-${slot}" + + log_info "Running health checks for $slot deployment..." + + # Wait for gateway to be ready + kubectl wait --for=condition=available --timeout=$TIMEOUT deployment/api-gateway-$slot -n $NAMESPACE + + # Get gateway service endpoint + local gateway_url=$(kubectl get service $gateway_service -n $NAMESPACE -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || echo "") + + if [ -z "$gateway_url" ]; then + gateway_url=$(kubectl get service $gateway_service -n $NAMESPACE -o jsonpath='{.spec.clusterIP}') + log_warning "Using ClusterIP for health checks: $gateway_url" + fi + + # Health check endpoints + local health_endpoints=( + "/actuator/health" + "/api/v1/customers/health" + "/api/v1/products/health" + "/api/v1/orders/health" + "/api/v1/payments/health" + ) + + for endpoint in "${health_endpoints[@]}"; do + log_info "Checking endpoint: http://$gateway_url:8080$endpoint" + + # Use kubectl port-forward for local testing if needed + if ! curl -f -s "http://$gateway_url:8080$endpoint" > /dev/null; then + log_warning "Health check failed for $endpoint, but continuing..." + else + log_info "βœ“ Health check passed for $endpoint" + fi + done + + log_success "Health checks completed for $slot deployment" +} + +# Function to deploy to inactive slot +deploy_to_inactive_slot() { + local inactive_slot=$(get_inactive_slot) + + log_info "Deploying to inactive slot: $inactive_slot" + + # Update values file to deploy to inactive slot + helm upgrade $RELEASE_NAME $CHART_PATH \ + --namespace $NAMESPACE \ + --create-namespace \ + --set bluegreen.enabled=true \ + --set bluegreen.activeSlot=$inactive_slot \ + --timeout $TIMEOUT \ + --wait + + log_success "Deployed to $inactive_slot slot" + + # Wait for deployments to be ready + sleep 30 + + # Check health of new deployment + if ! check_deployment_health $inactive_slot; then + log_error "Health check failed for $inactive_slot deployment" + return 1 + fi + + # Run additional health checks + run_health_checks $inactive_slot + + log_success "Deployment to $inactive_slot slot completed successfully" +} + +# Function to switch traffic to new deployment +switch_traffic() { + local new_active_slot=$(get_inactive_slot) + local old_active_slot=$(get_active_slot) + + log_info "Switching traffic from $old_active_slot to $new_active_slot" + + # Update ingress to point to new deployment + helm upgrade $RELEASE_NAME $CHART_PATH \ + --namespace $NAMESPACE \ + --reuse-values \ + --set bluegreen.activeSlot=$new_active_slot \ + --timeout $TIMEOUT + + log_success "Traffic switched to $new_active_slot" + + # Wait a bit for traffic to stabilize + sleep 10 + + # Verify the switch worked + log_info "Verifying traffic switch..." + run_health_checks $new_active_slot + + log_success "Traffic successfully switched to $new_active_slot" +} + +# Function to rollback to previous deployment +rollback() { + local current_active=$(get_active_slot) + local rollback_to=$(get_inactive_slot) + + log_warning "Rolling back from $current_active to $rollback_to" + + # Switch traffic back + helm upgrade $RELEASE_NAME $CHART_PATH \ + --namespace $NAMESPACE \ + --reuse-values \ + --set bluegreen.activeSlot=$rollback_to \ + --timeout $TIMEOUT + + log_success "Rollback completed. Traffic switched to $rollback_to" +} + +# Function to cleanup old deployment +cleanup_old_deployment() { + local old_slot=$(get_inactive_slot) + + log_info "Cleaning up old deployment in $old_slot slot" + + # Scale down old deployments + local services=("discovery-service" "config-server" "api-gateway" "customer-service" "product-service" "order-service" "payment-service" "notification-service") + + for service in "${services[@]}"; do + local deployment_name="${service}-${old_slot}" + + if kubectl get deployment $deployment_name -n $NAMESPACE > /dev/null 2>&1; then + kubectl scale deployment $deployment_name --replicas=0 -n $NAMESPACE + log_info "Scaled down $deployment_name" + fi + done + + log_success "Old deployment cleanup completed" +} + +# Function to perform canary deployment +canary_deploy() { + local weight=${1:-10} + local inactive_slot=$(get_inactive_slot) + + log_info "Starting canary deployment to $inactive_slot slot with $weight% traffic" + + # Deploy to inactive slot first + deploy_to_inactive_slot + + # Update ingress canary weight + kubectl patch ingress ${RELEASE_NAME}-canary -n $NAMESPACE -p "{\"metadata\":{\"annotations\":{\"nginx.ingress.kubernetes.io/canary-weight\":\"$weight\"}}}" + + log_success "Canary deployment started with $weight% traffic to $inactive_slot" + log_info "Monitor metrics and run 'promote_canary' when ready" +} + +# Function to promote canary to full deployment +promote_canary() { + log_info "Promoting canary to full deployment" + + # Set canary weight to 100% + kubectl patch ingress ${RELEASE_NAME}-canary -n $NAMESPACE -p '{"metadata":{"annotations":{"nginx.ingress.kubernetes.io/canary-weight":"100"}}}' + + sleep 10 + + # Switch main traffic + switch_traffic + + # Disable canary + kubectl patch ingress ${RELEASE_NAME}-canary -n $NAMESPACE -p '{"metadata":{"annotations":{"nginx.ingress.kubernetes.io/canary-weight":"0"}}}' + + log_success "Canary promoted to full deployment" +} + +# Function to show deployment status +status() { + local active_slot=$(get_active_slot) + local inactive_slot=$(get_inactive_slot) + + echo "=== Blue-Green Deployment Status ===" + echo "Namespace: $NAMESPACE" + echo "Release: $RELEASE_NAME" + echo "Active Slot: $active_slot" + echo "Inactive Slot: $inactive_slot" + echo "" + + echo "=== Active Deployments ===" + kubectl get deployments -n $NAMESPACE -l deployment.kubernetes.io/slot=$active_slot + echo "" + + echo "=== Services ===" + kubectl get services -n $NAMESPACE + echo "" + + echo "=== Ingress ===" + kubectl get ingress -n $NAMESPACE +} + +# Main command dispatcher +case "${1:-}" in + "deploy") + deploy_to_inactive_slot + ;; + "switch") + switch_traffic + ;; + "rollback") + rollback + ;; + "cleanup") + cleanup_old_deployment + ;; + "full-deploy") + deploy_to_inactive_slot + switch_traffic + cleanup_old_deployment + ;; + "canary") + canary_deploy ${2:-10} + ;; + "promote") + promote_canary + ;; + "status") + status + ;; + *) + echo "Usage: $0 {deploy|switch|rollback|cleanup|full-deploy|canary [weight]|promote|status}" + echo "" + echo "Commands:" + echo " deploy - Deploy to inactive slot" + echo " switch - Switch traffic to inactive slot" + echo " rollback - Rollback to previous deployment" + echo " cleanup - Cleanup old deployment" + echo " full-deploy - Complete blue-green deployment (deploy + switch + cleanup)" + echo " canary - Start canary deployment with specified weight (default: 10%)" + echo " promote - Promote canary to full deployment" + echo " status - Show deployment status" + exit 1 + ;; +esac diff --git a/k8s/build-images.sh b/k8s/build-images.sh new file mode 100755 index 0000000..0dcc48b --- /dev/null +++ b/k8s/build-images.sh @@ -0,0 +1,325 @@ +#!/bin/bash + +# Docker Image Build Script for E-commerce Microservices +# Author: Pramitha Jayasooriya +# Version: 1.0.0 + +set -e + +# Configuration +DOCKER_REGISTRY=${DOCKER_REGISTRY:-""} +IMAGE_TAG=${IMAGE_TAG:-"latest"} +SERVICES_DIR="../services" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Logging functions +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Function to build Docker image +build_image() { + local service_name=$1 + local service_dir=$2 + local image_name="ecommerce/${service_name}" + + if [ ! -z "$DOCKER_REGISTRY" ]; then + image_name="${DOCKER_REGISTRY}/${image_name}" + fi + + log_info "Building image for ${service_name}..." + + # Check if Dockerfile exists + if [ ! -f "${service_dir}/Dockerfile" ]; then + log_warning "Dockerfile not found for ${service_name}, creating one..." + create_dockerfile "$service_dir" "$service_name" + fi + + # Build the Docker image + docker build -t "${image_name}:${IMAGE_TAG}" "$service_dir" + + if [ $? -eq 0 ]; then + log_success "Successfully built ${image_name}:${IMAGE_TAG}" + + # Tag as latest + docker tag "${image_name}:${IMAGE_TAG}" "${image_name}:latest" + + return 0 + else + log_error "Failed to build ${image_name}:${IMAGE_TAG}" + return 1 + fi +} + +# Function to create Dockerfile if not exists +create_dockerfile() { + local service_dir=$1 + local service_name=$2 + + cat > "${service_dir}/Dockerfile" << EOF +FROM openjdk:17-jre-slim + +# Install curl for health checks +RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* + +# Create app directory +WORKDIR /app + +# Copy the application jar +COPY target/*.jar app.jar + +# Expose port +EXPOSE 8080 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \\ + CMD curl -f http://localhost:8080/actuator/health || exit 1 + +# Run the application +ENTRYPOINT ["java", "-jar", "/app/app.jar"] +EOF + + log_info "Created Dockerfile for ${service_name}" +} + +# Function to push image to registry +push_image() { + local service_name=$1 + local image_name="ecommerce/${service_name}" + + if [ ! -z "$DOCKER_REGISTRY" ]; then + image_name="${DOCKER_REGISTRY}/${image_name}" + + log_info "Pushing ${image_name}:${IMAGE_TAG} to registry..." + docker push "${image_name}:${IMAGE_TAG}" + docker push "${image_name}:latest" + + if [ $? -eq 0 ]; then + log_success "Successfully pushed ${image_name}" + else + log_error "Failed to push ${image_name}" + return 1 + fi + else + log_warning "No registry specified, skipping push for ${service_name}" + fi +} + +# Function to build all services +build_all_services() { + local services=( + "discovery-service:discovery" + "config-server:config-server" + "api-gateway:gateway" + "customer-service:customer" + "product-service:product" + "order-service:order" + "payment-service:payment" + "notification-service:notification" + ) + + local failed_builds=() + + log_info "Building all microservices Docker images..." + log_info "Registry: ${DOCKER_REGISTRY:-'local'}" + log_info "Tag: ${IMAGE_TAG}" + + for service_info in "${services[@]}"; do + IFS=':' read -r service_name service_dir <<< "$service_info" + local full_service_dir="${SERVICES_DIR}/${service_dir}" + + if [ -d "$full_service_dir" ]; then + # Build Maven project first + log_info "Building Maven project for ${service_name}..." + (cd "$full_service_dir" && ./mvnw clean package -DskipTests) + + if [ $? -eq 0 ]; then + # Build Docker image + if build_image "$service_name" "$full_service_dir"; then + # Push to registry if specified + push_image "$service_name" + else + failed_builds+=("$service_name") + fi + else + log_error "Maven build failed for ${service_name}" + failed_builds+=("$service_name") + fi + else + log_error "Service directory not found: ${full_service_dir}" + failed_builds+=("$service_name") + fi + done + + # Summary + echo "" + echo "=== Build Summary ===" + if [ ${#failed_builds[@]} -eq 0 ]; then + log_success "All services built successfully!" + else + log_error "Failed to build the following services:" + for failed_service in "${failed_builds[@]}"; do + echo " - $failed_service" + done + return 1 + fi +} + +# Function to build single service +build_single_service() { + local service_name=$1 + + case $service_name in + "discovery-service"|"discovery") + build_service_by_name "discovery-service" "discovery" + ;; + "config-server"|"config") + build_service_by_name "config-server" "config-server" + ;; + "api-gateway"|"gateway") + build_service_by_name "api-gateway" "gateway" + ;; + "customer-service"|"customer") + build_service_by_name "customer-service" "customer" + ;; + "product-service"|"product") + build_service_by_name "product-service" "product" + ;; + "order-service"|"order") + build_service_by_name "order-service" "order" + ;; + "payment-service"|"payment") + build_service_by_name "payment-service" "payment" + ;; + "notification-service"|"notification") + build_service_by_name "notification-service" "notification" + ;; + *) + log_error "Unknown service: $service_name" + echo "Available services: discovery, config, gateway, customer, product, order, payment, notification" + return 1 + ;; + esac +} + +# Helper function to build service by name +build_service_by_name() { + local service_name=$1 + local service_dir=$2 + local full_service_dir="${SERVICES_DIR}/${service_dir}" + + if [ -d "$full_service_dir" ]; then + log_info "Building Maven project for ${service_name}..." + (cd "$full_service_dir" && ./mvnw clean package -DskipTests) + + if [ $? -eq 0 ]; then + if build_image "$service_name" "$full_service_dir"; then + push_image "$service_name" + fi + else + log_error "Maven build failed for ${service_name}" + return 1 + fi + else + log_error "Service directory not found: ${full_service_dir}" + return 1 + fi +} + +# Function to show usage +show_usage() { + echo "Usage: $0 [OPTIONS] [SERVICE]" + echo "" + echo "Build Docker images for E-commerce microservices" + echo "" + echo "Options:" + echo " -r, --registry REGISTRY Docker registry to push images to" + echo " -t, --tag TAG Image tag (default: latest)" + echo " -h, --help Show this help message" + echo "" + echo "Services:" + echo " all Build all services (default)" + echo " discovery Discovery Service (Eureka)" + echo " config Config Server" + echo " gateway API Gateway" + echo " customer Customer Service" + echo " product Product Service" + echo " order Order Service" + echo " payment Payment Service" + echo " notification Notification Service" + echo "" + echo "Examples:" + echo " $0 Build all services with default settings" + echo " $0 order Build only Order Service" + echo " $0 -r docker.io/myuser -t v1.0.0 all" + echo " $0 --registry=harbor.company.com --tag=prod customer" +} + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + -r|--registry) + DOCKER_REGISTRY="$2" + shift 2 + ;; + -t|--tag) + IMAGE_TAG="$2" + shift 2 + ;; + -h|--help) + show_usage + exit 0 + ;; + -*) + log_error "Unknown option: $1" + show_usage + exit 1 + ;; + *) + SERVICE_NAME="$1" + shift + ;; + esac +done + +# Main execution +log_info "E-commerce Microservices Docker Build Script" +log_info "============================================" + +# Check if Docker is running +if ! docker info > /dev/null 2>&1; then + log_error "Docker is not running. Please start Docker and try again." + exit 1 +fi + +# Build services +if [ -z "${SERVICE_NAME:-}" ] || [ "${SERVICE_NAME}" = "all" ]; then + build_all_services +else + build_single_service "$SERVICE_NAME" +fi + +log_success "Build process completed!" + +# Show built images +echo "" +echo "=== Built Images ===" +docker images | grep "ecommerce/" diff --git a/k8s/helm-charts/Chart.yaml b/k8s/helm-charts/Chart.yaml new file mode 100644 index 0000000..63efa05 --- /dev/null +++ b/k8s/helm-charts/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: ecommerce-microservices +description: A Helm chart for E-commerce Microservices with Blue-Green Deployment +type: application +version: 1.0.0 +appVersion: "1.0.0" + +maintainers: +- name: Pramitha Jayasooriya + email: pramithajayasooriya@example.com + +dependencies: + - name: kafka + version: 23.0.7 + repository: https://charts.bitnami.com/bitnami + condition: kafka.enabled + - name: mongodb + version: 13.15.1 + repository: https://charts.bitnami.com/bitnami + condition: mongodb.enabled + - name: postgresql + version: 12.6.6 + repository: https://charts.bitnami.com/bitnami + condition: postgresql.enabled diff --git a/k8s/helm-charts/templates/_helpers.tpl b/k8s/helm-charts/templates/_helpers.tpl new file mode 100644 index 0000000..f15199e --- /dev/null +++ b/k8s/helm-charts/templates/_helpers.tpl @@ -0,0 +1,100 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "ecommerce.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "ecommerce.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "ecommerce.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "ecommerce.labels" -}} +helm.sh/chart: {{ include "ecommerce.chart" . }} +{{ include "ecommerce.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "ecommerce.selectorLabels" -}} +app.kubernetes.io/name: {{ include "ecommerce.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Blue-Green deployment slot +*/}} +{{- define "ecommerce.slot" -}} +{{- if .Values.bluegreen.enabled }} +{{- .Values.bluegreen.activeSlot }} +{{- else }} +{{- "active" }} +{{- end }} +{{- end }} + +{{/* +Create service name with slot suffix +*/}} +{{- define "ecommerce.serviceName" -}} +{{- $serviceName := .serviceName -}} +{{- if .Values.bluegreen.enabled }} +{{- printf "%s-%s" $serviceName (include "ecommerce.slot" .) }} +{{- else }} +{{- $serviceName }} +{{- end }} +{{- end }} + +{{/* +Environment variables for microservices +*/}} +{{- define "ecommerce.envVars" -}} +- name: SPRING_PROFILES_ACTIVE + value: "kubernetes" +- name: EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE + value: "http://{{ include "ecommerce.serviceName" (dict "serviceName" "discovery-service" "Values" .Values) }}:8761/eureka" +- name: SPRING_CLOUD_CONFIG_URI + value: "http://{{ include "ecommerce.serviceName" (dict "serviceName" "config-server" "Values" .Values) }}:8888" +{{- if .Values.kafka.enabled }} +- name: SPRING_KAFKA_BOOTSTRAP_SERVERS + value: "{{ .Release.Name }}-kafka:9092" +{{- end }} +{{- if .Values.mongodb.enabled }} +- name: SPRING_DATA_MONGODB_URI + value: "mongodb://admin:admin123@{{ .Release.Name }}-mongodb:27017/ecommerce?authSource=admin" +{{- end }} +{{- if .Values.postgresql.enabled }} +- name: SPRING_DATASOURCE_URL + value: "jdbc:postgresql://{{ .Release.Name }}-postgresql:5432/ecommerce" +- name: SPRING_DATASOURCE_USERNAME + value: "ecommerce" +- name: SPRING_DATASOURCE_PASSWORD + value: "ecommerce123" +{{- end }} +{{- end }} diff --git a/k8s/helm-charts/templates/config-server-configmap.yaml b/k8s/helm-charts/templates/config-server-configmap.yaml new file mode 100644 index 0000000..7916a2b --- /dev/null +++ b/k8s/helm-charts/templates/config-server-configmap.yaml @@ -0,0 +1,63 @@ +{{- if .Values.configServer.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Values.configServer.name }}-config + labels: + {{- include "ecommerce.labels" . | nindent 4 }} + app.kubernetes.io/component: {{ .Values.configServer.name }} +data: + application.yml: | + server: + port: 8888 + + spring: + application: + name: config-server + profiles: + active: kubernetes + cloud: + config: + server: + git: + uri: https://github.com/PramithaMJ/microservices-config + clone-on-start: true + default-label: main + health: + repositories: + microservices-config: + label: main + name: microservices-config + profiles: default + + eureka: + client: + service-url: + defaultZone: http://{{ include "ecommerce.serviceName" (dict "serviceName" "discovery-service" "Values" .Values) }}:8761/eureka + register-with-eureka: true + fetch-registry: true + instance: + prefer-ip-address: true + hostname: {{ .Values.configServer.name }} + + management: + endpoints: + web: + exposure: + include: "*" + endpoint: + health: + show-details: always + health: + livenessstate: + enabled: true + readinessstate: + enabled: true + + bootstrap.yml: | + spring: + application: + name: config-server + profiles: + active: kubernetes +{{- end }} diff --git a/k8s/helm-charts/templates/discovery-service.yaml b/k8s/helm-charts/templates/discovery-service.yaml new file mode 100644 index 0000000..1f07dda --- /dev/null +++ b/k8s/helm-charts/templates/discovery-service.yaml @@ -0,0 +1,103 @@ +{{- if .Values.discovery.enabled }} +{{- $serviceName := .Values.discovery.name }} +{{- $slot := include "ecommerce.slot" . }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ $serviceName }}-{{ $slot }} + labels: + {{- include "ecommerce.labels" . | nindent 4 }} + app.kubernetes.io/component: {{ $serviceName }} + deployment.kubernetes.io/slot: {{ $slot }} +spec: + replicas: {{ .Values.discovery.replicaCount }} + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + selector: + matchLabels: + {{- include "ecommerce.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: {{ $serviceName }} + deployment.kubernetes.io/slot: {{ $slot }} + template: + metadata: + labels: + {{- include "ecommerce.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: {{ $serviceName }} + deployment.kubernetes.io/slot: {{ $slot }} + spec: + containers: + - name: {{ $serviceName }} + image: "{{ .Values.discovery.image.repository }}:{{ .Values.discovery.image.tag }}" + imagePullPolicy: {{ .Values.discovery.image.pullPolicy }} + ports: + - containerPort: {{ .Values.discovery.service.port }} + name: http + env: + {{- include "ecommerce.envVars" . | nindent 8 }} + - name: SERVER_PORT + value: "{{ .Values.discovery.service.port }}" + livenessProbe: + httpGet: + path: /actuator/health/liveness + port: http + initialDelaySeconds: {{ .Values.healthChecks.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.healthChecks.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.healthChecks.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.healthChecks.livenessProbe.failureThreshold }} + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: http + initialDelaySeconds: {{ .Values.healthChecks.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.healthChecks.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.healthChecks.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.healthChecks.readinessProbe.failureThreshold }} + resources: + {{- toYaml .Values.discovery.resources | nindent 10 }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "ecommerce.serviceName" (dict "serviceName" $serviceName "Values" .Values) }} + labels: + {{- include "ecommerce.labels" . | nindent 4 }} + app.kubernetes.io/component: {{ $serviceName }} +spec: + type: {{ .Values.discovery.service.type }} + ports: + - port: {{ .Values.discovery.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "ecommerce.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: {{ $serviceName }} + deployment.kubernetes.io/slot: {{ $slot }} +{{- if .Values.discovery.autoscaling.enabled }} +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ $serviceName }}-{{ $slot }} + labels: + {{- include "ecommerce.labels" . | nindent 4 }} + app.kubernetes.io/component: {{ $serviceName }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ $serviceName }}-{{ $slot }} + minReplicas: {{ .Values.discovery.autoscaling.minReplicas }} + maxReplicas: {{ .Values.discovery.autoscaling.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.discovery.autoscaling.targetCPUUtilizationPercentage }} +{{- end }} +{{- end }} diff --git a/k8s/helm-charts/templates/gateway-service.yaml b/k8s/helm-charts/templates/gateway-service.yaml new file mode 100644 index 0000000..1a62cce --- /dev/null +++ b/k8s/helm-charts/templates/gateway-service.yaml @@ -0,0 +1,105 @@ +{{- if .Values.gateway.enabled }} +{{- $serviceName := .Values.gateway.name }} +{{- $slot := include "ecommerce.slot" . }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ $serviceName }}-{{ $slot }} + labels: + {{- include "ecommerce.labels" . | nindent 4 }} + app.kubernetes.io/component: {{ $serviceName }} + deployment.kubernetes.io/slot: {{ $slot }} +spec: + replicas: {{ .Values.gateway.replicaCount }} + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + maxSurge: 2 + selector: + matchLabels: + {{- include "ecommerce.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: {{ $serviceName }} + deployment.kubernetes.io/slot: {{ $slot }} + template: + metadata: + labels: + {{- include "ecommerce.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: {{ $serviceName }} + deployment.kubernetes.io/slot: {{ $slot }} + spec: + containers: + - name: {{ $serviceName }} + image: "{{ .Values.gateway.image.repository }}:{{ .Values.gateway.image.tag }}" + imagePullPolicy: {{ .Values.gateway.image.pullPolicy }} + ports: + - containerPort: {{ .Values.gateway.service.targetPort }} + name: http + env: + {{- include "ecommerce.envVars" . | nindent 8 }} + - name: SERVER_PORT + value: "{{ .Values.gateway.service.targetPort }}" + - name: SPRING_CLOUD_GATEWAY_DISCOVERY_LOCATOR_ENABLED + value: "true" + livenessProbe: + httpGet: + path: /actuator/health/liveness + port: http + initialDelaySeconds: {{ .Values.healthChecks.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.healthChecks.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.healthChecks.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.healthChecks.livenessProbe.failureThreshold }} + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: http + initialDelaySeconds: {{ .Values.healthChecks.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.healthChecks.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.healthChecks.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.healthChecks.readinessProbe.failureThreshold }} + resources: + {{- toYaml .Values.gateway.resources | nindent 10 }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "ecommerce.serviceName" (dict "serviceName" $serviceName "Values" .Values) }} + labels: + {{- include "ecommerce.labels" . | nindent 4 }} + app.kubernetes.io/component: {{ $serviceName }} +spec: + type: {{ .Values.gateway.service.type }} + ports: + - port: {{ .Values.gateway.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "ecommerce.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: {{ $serviceName }} + deployment.kubernetes.io/slot: {{ $slot }} +{{- if .Values.gateway.autoscaling.enabled }} +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ $serviceName }}-{{ $slot }} + labels: + {{- include "ecommerce.labels" . | nindent 4 }} + app.kubernetes.io/component: {{ $serviceName }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ $serviceName }}-{{ $slot }} + minReplicas: {{ .Values.gateway.autoscaling.minReplicas }} + maxReplicas: {{ .Values.gateway.autoscaling.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.gateway.autoscaling.targetCPUUtilizationPercentage }} +{{- end }} +{{- end }} diff --git a/k8s/helm-charts/templates/ingress.yaml b/k8s/helm-charts/templates/ingress.yaml new file mode 100644 index 0000000..152ccaa --- /dev/null +++ b/k8s/helm-charts/templates/ingress.yaml @@ -0,0 +1,113 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "ecommerce.fullname" . -}} +{{- $svcPort := .Values.gateway.service.port -}} +{{- if and .Values.ingress.className (not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class")) }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "ecommerce.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + # Blue-Green deployment annotations + nginx.ingress.kubernetes.io/canary: "false" + nginx.ingress.kubernetes.io/canary-weight: "0" + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ include "ecommerce.serviceName" (dict "serviceName" $.Values.gateway.name "Values" $.Values) }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ include "ecommerce.serviceName" (dict "serviceName" $.Values.gateway.name "Values" $.Values) }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- if .Values.bluegreen.enabled }} +--- +# Blue-Green Canary Ingress for gradual traffic shifting +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }}-canary + labels: + {{- include "ecommerce.labels" . | nindent 4 }} + deployment.type: canary + annotations: + {{- with .Values.ingress.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + nginx.ingress.kubernetes.io/canary: "true" + nginx.ingress.kubernetes.io/canary-weight: "0" + nginx.ingress.kubernetes.io/canary-by-header: "X-Canary-Deploy" + nginx.ingress.kubernetes.io/canary-by-header-value: "always" +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $.Values.gateway.name }}-{{ if eq $.Values.bluegreen.activeSlot "blue" }}green{{ else }}blue{{ end }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $.Values.gateway.name }}-{{ if eq $.Values.bluegreen.activeSlot "blue" }}green{{ else }}blue{{ end }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} +{{- end }} diff --git a/k8s/helm-charts/templates/monitoring-configmap.yaml b/k8s/helm-charts/templates/monitoring-configmap.yaml new file mode 100644 index 0000000..3e28f87 --- /dev/null +++ b/k8s/helm-charts/templates/monitoring-configmap.yaml @@ -0,0 +1,205 @@ +{{- if .Values.monitoring.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: prometheus-config + labels: + {{- include "ecommerce.labels" . | nindent 4 }} + app.kubernetes.io/component: monitoring +data: + prometheus.yml: | + global: + scrape_interval: 15s + evaluation_interval: 15s + + rule_files: + - "circuit_breaker_rules.yml" + - "saga_pattern_rules.yml" + + scrape_configs: + # Discovery Service + - job_name: 'discovery-service' + kubernetes_sd_configs: + - role: endpoints + namespaces: + names: + - {{ .Release.Namespace }} + relabel_configs: + - source_labels: [__meta_kubernetes_service_name] + action: keep + regex: discovery-service.* + - source_labels: [__meta_kubernetes_endpoint_port_name] + action: keep + regex: http + scrape_interval: 30s + metrics_path: '/actuator/prometheus' + + # Config Server + - job_name: 'config-server' + kubernetes_sd_configs: + - role: endpoints + namespaces: + names: + - {{ .Release.Namespace }} + relabel_configs: + - source_labels: [__meta_kubernetes_service_name] + action: keep + regex: config-server.* + - source_labels: [__meta_kubernetes_endpoint_port_name] + action: keep + regex: http + scrape_interval: 30s + metrics_path: '/actuator/prometheus' + + # API Gateway + - job_name: 'api-gateway' + kubernetes_sd_configs: + - role: endpoints + namespaces: + names: + - {{ .Release.Namespace }} + relabel_configs: + - source_labels: [__meta_kubernetes_service_name] + action: keep + regex: api-gateway.* + - source_labels: [__meta_kubernetes_endpoint_port_name] + action: keep + regex: http + scrape_interval: 15s + metrics_path: '/actuator/prometheus' + + # Microservices + - job_name: 'microservices' + kubernetes_sd_configs: + - role: endpoints + namespaces: + names: + - {{ .Release.Namespace }} + relabel_configs: + - source_labels: [__meta_kubernetes_service_name] + action: keep + regex: '(customer|product|order|payment|notification)-service.*' + - source_labels: [__meta_kubernetes_endpoint_port_name] + action: keep + regex: http + scrape_interval: 15s + metrics_path: '/actuator/prometheus' + + # Kafka metrics + - job_name: 'kafka' + kubernetes_sd_configs: + - role: endpoints + namespaces: + names: + - {{ .Release.Namespace }} + relabel_configs: + - source_labels: [__meta_kubernetes_service_name] + action: keep + regex: '.*kafka.*' + - source_labels: [__meta_kubernetes_endpoint_port_name] + action: keep + regex: metrics + scrape_interval: 30s + + circuit_breaker_rules.yml: | + groups: + - name: circuit_breaker + rules: + - alert: CircuitBreakerOpen + expr: resilience4j_circuitbreaker_state{state="open"} == 1 + for: 1m + labels: + severity: critical + annotations: + summary: "Circuit breaker {{ $labels.name }} is open" + description: "Circuit breaker {{ $labels.name }} in service {{ $labels.job }} has been open for more than 1 minute" + + - alert: CircuitBreakerHighFailureRate + expr: rate(resilience4j_circuitbreaker_calls_total{kind="failed"}[5m]) / rate(resilience4j_circuitbreaker_calls_total[5m]) > 0.5 + for: 2m + labels: + severity: warning + annotations: + summary: "High failure rate detected for circuit breaker {{ $labels.name }}" + description: "Circuit breaker {{ $labels.name }} has a failure rate above 50% for the last 2 minutes" + + - alert: CircuitBreakerSlowCalls + expr: rate(resilience4j_circuitbreaker_slow_calls_total[5m]) / rate(resilience4j_circuitbreaker_calls_total[5m]) > 0.3 + for: 2m + labels: + severity: warning + annotations: + summary: "High slow call rate for circuit breaker {{ $labels.name }}" + description: "Circuit breaker {{ $labels.name }} has slow calls above 30% for the last 2 minutes" + + saga_pattern_rules.yml: | + groups: + - name: saga_pattern + rules: + - alert: SagaHighFailureRate + expr: rate(saga_failures_total[5m]) / rate(saga_started_total[5m]) > 0.1 + for: 2m + labels: + severity: critical + annotations: + summary: "High saga failure rate detected" + description: "Saga pattern has a failure rate above 10% for the last 2 minutes" + + - alert: SagaLongRunningTransactions + expr: histogram_quantile(0.95, rate(saga_duration_seconds_bucket[5m])) > 300 + for: 1m + labels: + severity: warning + annotations: + summary: "Long running saga transactions detected" + description: "95th percentile of saga duration is above 5 minutes" + + - alert: SagaCompensationRate + expr: rate(saga_compensations_total[5m]) > 0.05 + for: 1m + labels: + severity: warning + annotations: + summary: "High saga compensation rate" + description: "Saga compensations are occurring at a rate higher than expected" + + - alert: SagaPendingTransactions + expr: saga_pending_total > 100 + for: 5m + labels: + severity: warning + annotations: + summary: "High number of pending saga transactions" + description: "There are {{ $value }} pending saga transactions for more than 5 minutes" + + general_rules.yml: | + groups: + - name: microservices + rules: + - alert: ServiceDown + expr: up{job=~"(discovery-service|config-server|api-gateway|customer-service|product-service|order-service|payment-service|notification-service)"} == 0 + for: 1m + labels: + severity: critical + annotations: + summary: "Service {{ $labels.job }} is down" + description: "Service {{ $labels.job }} has been down for more than 1 minute" + + - alert: HighMemoryUsage + expr: (container_memory_usage_bytes / container_spec_memory_limit_bytes) * 100 > 85 + for: 5m + labels: + severity: warning + annotations: + summary: "High memory usage for {{ $labels.container }}" + description: "Container {{ $labels.container }} memory usage is above 85%" + + - alert: HighCPUUsage + expr: (rate(container_cpu_usage_seconds_total[5m]) / container_spec_cpu_quota) * 100 > 80 + for: 5m + labels: + severity: warning + annotations: + summary: "High CPU usage for {{ $labels.container }}" + description: "Container {{ $labels.container }} CPU usage is above 80%" +{{- end }} diff --git a/k8s/helm-charts/templates/network-policies.yaml b/k8s/helm-charts/templates/network-policies.yaml new file mode 100644 index 0000000..a486bbe --- /dev/null +++ b/k8s/helm-charts/templates/network-policies.yaml @@ -0,0 +1,283 @@ +{{- if .Values.security.networkPolicies.enabled }} +# Discovery Service Network Policy +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: discovery-service-netpol + labels: + {{- include "ecommerce.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/component: discovery-service + policyTypes: + - Ingress + - Egress + ingress: + - from: + - podSelector: + matchLabels: + app.kubernetes.io/name: {{ include "ecommerce.name" . }} + ports: + - protocol: TCP + port: 8761 + egress: + - {} # Allow all outbound traffic for service registration + +--- +# Config Server Network Policy +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: config-server-netpol + labels: + {{- include "ecommerce.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/component: config-server + policyTypes: + - Ingress + - Egress + ingress: + - from: + - podSelector: + matchLabels: + app.kubernetes.io/name: {{ include "ecommerce.name" . }} + ports: + - protocol: TCP + port: 8888 + egress: + - {} # Allow all outbound traffic for Git access + +--- +# API Gateway Network Policy +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: api-gateway-netpol + labels: + {{- include "ecommerce.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/component: api-gateway + policyTypes: + - Ingress + - Egress + ingress: + - {} # Allow all inbound traffic (external access) + egress: + - to: + - podSelector: + matchLabels: + app.kubernetes.io/name: {{ include "ecommerce.name" . }} + ports: + - protocol: TCP + port: 8761 # Discovery Service + - protocol: TCP + port: 8888 # Config Server + - protocol: TCP + port: 8090 # Customer Service + - protocol: TCP + port: 8050 # Product Service + - protocol: TCP + port: 8070 # Order Service + - protocol: TCP + port: 8060 # Payment Service + - protocol: TCP + port: 8040 # Notification Service + - {} # DNS resolution + +--- +# Microservices Network Policy +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: microservices-netpol + labels: + {{- include "ecommerce.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/component: customer-service + policyTypes: + - Ingress + - Egress + ingress: + - from: + - podSelector: + matchLabels: + app.kubernetes.io/component: api-gateway + - podSelector: + matchLabels: + app.kubernetes.io/component: order-service # For saga pattern + ports: + - protocol: TCP + port: 8090 + egress: + - to: + - podSelector: + matchLabels: + app.kubernetes.io/component: discovery-service + ports: + - protocol: TCP + port: 8761 + - to: + - podSelector: + matchLabels: + app.kubernetes.io/component: config-server + ports: + - protocol: TCP + port: 8888 + - to: # Database access + - podSelector: {} + ports: + - protocol: TCP + port: 5432 + - protocol: TCP + port: 27017 + - to: # Kafka access + - podSelector: {} + ports: + - protocol: TCP + port: 9092 + - {} # DNS resolution + +--- +# Order Service Network Policy (Saga Coordinator) +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: order-service-netpol + labels: + {{- include "ecommerce.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/component: order-service + policyTypes: + - Ingress + - Egress + ingress: + - from: + - podSelector: + matchLabels: + app.kubernetes.io/component: api-gateway + ports: + - protocol: TCP + port: 8070 + egress: + - to: + - podSelector: + matchLabels: + app.kubernetes.io/component: discovery-service + ports: + - protocol: TCP + port: 8761 + - to: + - podSelector: + matchLabels: + app.kubernetes.io/component: config-server + ports: + - protocol: TCP + port: 8888 + - to: # Communication with other services for saga pattern + - podSelector: + matchLabels: + app.kubernetes.io/component: customer-service + - podSelector: + matchLabels: + app.kubernetes.io/component: product-service + - podSelector: + matchLabels: + app.kubernetes.io/component: payment-service + - podSelector: + matchLabels: + app.kubernetes.io/component: notification-service + ports: + - protocol: TCP + port: 8090 + - protocol: TCP + port: 8050 + - protocol: TCP + port: 8060 + - protocol: TCP + port: 8040 + - to: # Database and Kafka access + - podSelector: {} + ports: + - protocol: TCP + port: 27017 + - protocol: TCP + port: 9092 + - {} # DNS resolution + +--- +# Database Network Policy +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: database-netpol + labels: + {{- include "ecommerce.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: mongodb + policyTypes: + - Ingress + ingress: + - from: + - podSelector: + matchLabels: + app.kubernetes.io/name: {{ include "ecommerce.name" . }} + ports: + - protocol: TCP + port: 27017 + +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: postgresql-netpol + labels: + {{- include "ecommerce.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: postgresql + policyTypes: + - Ingress + ingress: + - from: + - podSelector: + matchLabels: + app.kubernetes.io/name: {{ include "ecommerce.name" . }} + ports: + - protocol: TCP + port: 5432 + +--- +# Kafka Network Policy +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: kafka-netpol + labels: + {{- include "ecommerce.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: kafka + policyTypes: + - Ingress + ingress: + - from: + - podSelector: + matchLabels: + app.kubernetes.io/name: {{ include "ecommerce.name" . }} + ports: + - protocol: TCP + port: 9092 +{{- end }} diff --git a/k8s/helm-charts/templates/order-service.yaml b/k8s/helm-charts/templates/order-service.yaml new file mode 100644 index 0000000..6e20541 --- /dev/null +++ b/k8s/helm-charts/templates/order-service.yaml @@ -0,0 +1,117 @@ +{{- if .Values.order.enabled }} +{{- $serviceName := .Values.order.name }} +{{- $slot := include "ecommerce.slot" . }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ $serviceName }}-{{ $slot }} + labels: + {{- include "ecommerce.labels" . | nindent 4 }} + app.kubernetes.io/component: {{ $serviceName }} + deployment.kubernetes.io/slot: {{ $slot }} +spec: + replicas: {{ .Values.order.replicaCount }} + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + selector: + matchLabels: + {{- include "ecommerce.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: {{ $serviceName }} + deployment.kubernetes.io/slot: {{ $slot }} + template: + metadata: + labels: + {{- include "ecommerce.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: {{ $serviceName }} + deployment.kubernetes.io/slot: {{ $slot }} + spec: + containers: + - name: {{ $serviceName }} + image: "{{ .Values.order.image.repository }}:{{ .Values.order.image.tag }}" + imagePullPolicy: {{ .Values.order.image.pullPolicy }} + ports: + - containerPort: {{ .Values.order.service.port }} + name: http + env: + {{- include "ecommerce.envVars" . | nindent 8 }} + - name: SERVER_PORT + value: "{{ .Values.order.service.port }}" + {{- if .Values.order.circuitBreaker.enabled }} + - name: RESILIENCE4J_CIRCUITBREAKER_FAILURE_RATE_THRESHOLD + value: "{{ .Values.order.circuitBreaker.failureRateThreshold }}" + - name: RESILIENCE4J_CIRCUITBREAKER_WAIT_DURATION_IN_OPEN_STATE + value: "{{ .Values.order.circuitBreaker.waitDurationInOpenState }}" + - name: RESILIENCE4J_CIRCUITBREAKER_SLIDING_WINDOW_SIZE + value: "{{ .Values.order.circuitBreaker.slidingWindowSize }}" + {{- end }} + {{- if .Values.order.saga.enabled }} + - name: SAGA_RETRY_ATTEMPTS + value: "{{ .Values.order.saga.retryAttempts }}" + - name: SAGA_COMPENSATION_TIMEOUT + value: "{{ .Values.order.saga.compensationTimeout }}" + {{- end }} + livenessProbe: + httpGet: + path: /actuator/health/liveness + port: http + initialDelaySeconds: {{ .Values.healthChecks.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.healthChecks.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.healthChecks.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.healthChecks.livenessProbe.failureThreshold }} + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: http + initialDelaySeconds: {{ .Values.healthChecks.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.healthChecks.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.healthChecks.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.healthChecks.readinessProbe.failureThreshold }} + resources: + {{- toYaml .Values.order.resources | nindent 10 }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "ecommerce.serviceName" (dict "serviceName" $serviceName "Values" .Values) }} + labels: + {{- include "ecommerce.labels" . | nindent 4 }} + app.kubernetes.io/component: {{ $serviceName }} +spec: + type: {{ .Values.order.service.type }} + ports: + - port: {{ .Values.order.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "ecommerce.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: {{ $serviceName }} + deployment.kubernetes.io/slot: {{ $slot }} +{{- if .Values.order.autoscaling.enabled }} +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ $serviceName }}-{{ $slot }} + labels: + {{- include "ecommerce.labels" . | nindent 4 }} + app.kubernetes.io/component: {{ $serviceName }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ $serviceName }}-{{ $slot }} + minReplicas: {{ .Values.order.autoscaling.minReplicas }} + maxReplicas: {{ .Values.order.autoscaling.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.order.autoscaling.targetCPUUtilizationPercentage }} +{{- end }} +{{- end }} diff --git a/k8s/helm-charts/templates/secrets.yaml b/k8s/helm-charts/templates/secrets.yaml new file mode 100644 index 0000000..2c4c133 --- /dev/null +++ b/k8s/helm-charts/templates/secrets.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "ecommerce.fullname" . }}-secrets + labels: + {{- include "ecommerce.labels" . | nindent 4 }} +type: Opaque +data: + # Database passwords (base64 encoded) + mongodb-root-password: {{ "admin123" | b64enc | quote }} + mongodb-password: {{ "ecommerce123" | b64enc | quote }} + postgresql-password: {{ "admin123" | b64enc | quote }} + postgresql-user-password: {{ "ecommerce123" | b64enc | quote }} + + # JWT Secret for authentication + jwt-secret: {{ "mySecretKey123456789012345678901234567890" | b64enc | quote }} + + # Kafka credentials + kafka-username: {{ "kafka" | b64enc | quote }} + kafka-password: {{ "kafka123" | b64enc | quote }} + + # External API keys + payment-gateway-api-key: {{ "pg_test_key_123456789" | b64enc | quote }} + notification-email-password: {{ "email_password_123" | b64enc | quote }} + + # Circuit breaker secrets + circuit-breaker-secret: {{ "cb_secret_key_123" | b64enc | quote }} diff --git a/k8s/helm-charts/values.yaml b/k8s/helm-charts/values.yaml new file mode 100644 index 0000000..71d24a7 --- /dev/null +++ b/k8s/helm-charts/values.yaml @@ -0,0 +1,311 @@ +# Default values for ecommerce-microservices +# Blue-Green Deployment Configuration + +# Global settings +global: + imageRegistry: "" + imagePullSecrets: [] + storageClass: "" + +# Blue-Green Deployment Strategy +bluegreen: + enabled: true + activeSlot: "blue" + strategy: "manual" # manual or automatic + +# Service Discovery (Eureka) +discovery: + enabled: true + name: discovery-service + image: + repository: ecommerce/discovery-service + tag: "latest" + pullPolicy: IfNotPresent + replicaCount: 2 + service: + type: ClusterIP + port: 8761 + resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 250m + memory: 256Mi + autoscaling: + enabled: true + minReplicas: 2 + maxReplicas: 5 + targetCPUUtilizationPercentage: 70 + +# Config Server +configServer: + enabled: true + name: config-server + image: + repository: ecommerce/config-server + tag: "latest" + pullPolicy: IfNotPresent + replicaCount: 2 + service: + type: ClusterIP + port: 8888 + resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 250m + memory: 256Mi + +# API Gateway +gateway: + enabled: true + name: api-gateway + image: + repository: ecommerce/api-gateway + tag: "latest" + pullPolicy: IfNotPresent + replicaCount: 3 + service: + type: LoadBalancer + port: 8080 + targetPort: 8080 + resources: + limits: + cpu: 1000m + memory: 1Gi + requests: + cpu: 500m + memory: 512Mi + autoscaling: + enabled: true + minReplicas: 3 + maxReplicas: 10 + targetCPUUtilizationPercentage: 70 + +# Customer Service +customer: + enabled: true + name: customer-service + image: + repository: ecommerce/customer-service + tag: "latest" + pullPolicy: IfNotPresent + replicaCount: 3 + service: + type: ClusterIP + port: 8090 + resources: + limits: + cpu: 800m + memory: 1Gi + requests: + cpu: 400m + memory: 512Mi + autoscaling: + enabled: true + minReplicas: 3 + maxReplicas: 8 + targetCPUUtilizationPercentage: 70 + circuitBreaker: + enabled: true + failureRateThreshold: 50 + waitDurationInOpenState: 30000 + slidingWindowSize: 10 + +# Product Service +product: + enabled: true + name: product-service + image: + repository: ecommerce/product-service + tag: "latest" + pullPolicy: IfNotPresent + replicaCount: 3 + service: + type: ClusterIP + port: 8050 + resources: + limits: + cpu: 800m + memory: 1Gi + requests: + cpu: 400m + memory: 512Mi + autoscaling: + enabled: true + minReplicas: 3 + maxReplicas: 8 + targetCPUUtilizationPercentage: 70 + circuitBreaker: + enabled: true + failureRateThreshold: 50 + waitDurationInOpenState: 30000 + slidingWindowSize: 10 + +# Order Service (with Saga Pattern) +order: + enabled: true + name: order-service + image: + repository: ecommerce/order-service + tag: "latest" + pullPolicy: IfNotPresent + replicaCount: 3 + service: + type: ClusterIP + port: 8070 + resources: + limits: + cpu: 1000m + memory: 1Gi + requests: + cpu: 500m + memory: 512Mi + autoscaling: + enabled: true + minReplicas: 3 + maxReplicas: 10 + targetCPUUtilizationPercentage: 70 + circuitBreaker: + enabled: true + failureRateThreshold: 30 + waitDurationInOpenState: 60000 + slidingWindowSize: 20 + saga: + enabled: true + retryAttempts: 3 + compensationTimeout: 30000 + +# Payment Service +payment: + enabled: true + name: payment-service + image: + repository: ecommerce/payment-service + tag: "latest" + pullPolicy: IfNotPresent + replicaCount: 3 + service: + type: ClusterIP + port: 8060 + resources: + limits: + cpu: 800m + memory: 1Gi + requests: + cpu: 400m + memory: 512Mi + autoscaling: + enabled: true + minReplicas: 3 + maxReplicas: 8 + targetCPUUtilizationPercentage: 70 + circuitBreaker: + enabled: true + failureRateThreshold: 30 + waitDurationInOpenState: 60000 + slidingWindowSize: 20 + +# Notification Service +notification: + enabled: true + name: notification-service + image: + repository: ecommerce/notification-service + tag: "latest" + pullPolicy: IfNotPresent + replicaCount: 2 + service: + type: ClusterIP + port: 8040 + resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 250m + memory: 256Mi + autoscaling: + enabled: true + minReplicas: 2 + maxReplicas: 6 + targetCPUUtilizationPercentage: 70 + +# Database configurations +mongodb: + enabled: true + auth: + enabled: true + rootUser: admin + rootPassword: admin123 + persistence: + enabled: true + size: 10Gi + +postgresql: + enabled: true + auth: + postgresPassword: admin123 + username: ecommerce + password: ecommerce123 + database: ecommerce + persistence: + enabled: true + size: 10Gi + +# Kafka configuration +kafka: + enabled: true + persistence: + enabled: true + size: 10Gi + zookeeper: + persistence: + enabled: true + size: 5Gi + +# Monitoring +monitoring: + enabled: true + prometheus: + enabled: true + grafana: + enabled: true + +# Health checks +healthChecks: + livenessProbe: + initialDelaySeconds: 90 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 5 + readinessProbe: + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + +# Security +security: + enabled: true + networkPolicies: + enabled: true + podSecurityPolicy: + enabled: true + +# Ingress +ingress: + enabled: true + className: "nginx" + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + nginx.ingress.kubernetes.io/ssl-redirect: "false" + nginx.ingress.kubernetes.io/proxy-body-size: "50m" + hosts: + - host: ecommerce-api.local + paths: + - path: / + pathType: Prefix + tls: [] diff --git a/learning-platform/README.md b/learning-platform/README.md new file mode 100644 index 0000000..bc115a5 --- /dev/null +++ b/learning-platform/README.md @@ -0,0 +1,491 @@ +# Microservices Architecture Learning Platform + +An interactive web-based learning platform that explains microservices architecture through a complete e-commerce implementation using Spring Boot and Java, featuring advanced patterns like Saga orchestration, Circuit Breakers, and Kubernetes deployment strategies. + +## πŸš€ Features + +### Interactive Learning Experience +- **Visual Architecture Diagram**: Click on any service to see detailed information +- **Step-by-Step Explanations**: Understand how each component works +- **Hands-On Testing**: Real API examples and testing scenarios +- **Progress Tracking**: Track your learning progress through sections +- **GitHub Integration**: Direct access to complete source code +- **Responsive Design**: Works on desktop, tablet, and mobile devices +- **Circuit Breaker Demonstrations**: Interactive circuit breaker pattern visualization +- **Saga Pattern Examples**: Learn distributed transaction management +- **Deployment Scenarios**: Kubernetes and Helm chart explanations + +### Complete Source Code Access +- **GitHub Repository**: Direct links to the complete implementation +- **Author Information**: Learn about the creator and get support +- **Quick Actions**: Fork, download, or report issues directly from the platform +- **Live Repository Stats**: See the latest information about the codebase + +### Comprehensive Coverage +- **8 Microservices**: Customer, Product, Order, Payment, Notification, Config Server, Discovery, and Gateway +- **Advanced Patterns**: Saga orchestration, Circuit Breakers, Event-Driven Architecture, Service Registry, API Gateway +- **12+ Technologies**: Spring Boot, Spring Cloud, PostgreSQL, MongoDB, Kafka, Docker, Zipkin, Kubernetes, Helm, Resilience4j, and more +- **Deployment Strategies**: Blue-green deployment, rolling updates, canary deployments + +### Real-World Implementation +- **Business Problem**: Based on actual e-commerce requirements +- **Complete Solution**: From customer management to payment processing +- **Production-Ready**: Includes monitoring, tracing, resilience patterns, and Kubernetes deployment +- **Enterprise-Grade**: Saga patterns for distributed transactions, circuit breakers for fault tolerance + +## πŸ“ File Structure + +``` +learning-platform/ +β”œβ”€β”€ index.html # Main HTML file with all content +β”œβ”€β”€ styles.css # Complete CSS styling +β”œβ”€β”€ script.js # Interactive JavaScript functionality +└── README.md # This documentation +``` + +## πŸ› οΈ How to Use + +### 1. Open the Learning Platform +Simply open `index.html` in your web browser: +```bash +open index.html +``` +Or use a local server: +```bash +# Using Python +python -m http.server 8000 + +# Using Node.js +npx serve . + +# Then visit http://localhost:8000 +``` + +### 2. Navigate Through Sections +- **Overview**: Understand the business problem and solution +- **Architecture**: Interactive diagram of all services +- **Repository**: Access complete source code and GitHub integration +- **Services**: Deep dive into each microservice +- **Patterns**: Learn microservices design patterns including Saga and Circuit Breaker +- **Deployment**: Kubernetes, Helm charts, and blue-green deployment strategies +- **Hands-On**: Step-by-step implementation guide + +### 3. Interactive Features +- **Click service boxes** to see detailed information +- **Copy code examples** with one click +- **Follow hands-on tutorials** for practical learning +- **Track your progress** with the built-in progress bar + +## 🎯 Learning Objectives + +After completing this platform, you will understand: + +1. **Microservices Architecture Principles** + - Service decomposition strategies + - Inter-service communication + - Data management patterns + +2. **Spring Cloud Ecosystem** + - Config Server for centralized configuration + - Eureka for service discovery + - Gateway for API routing + +3. **Event-Driven Architecture** + - Kafka for asynchronous messaging + - Event sourcing patterns + - Notification systems + - Saga orchestration patterns + +4. **Resilience Patterns** + - Circuit breakers with Resilience4j + - Saga pattern for distributed transactions + - Compensation and recovery strategies + - Fault tolerance mechanisms + +5. **Production Concerns** + - Distributed tracing with Zipkin + - Database per service pattern + - Monitoring and observability + +6. **Kubernetes Deployment** + - Container orchestration strategies + - Helm charts for package management + - Blue-green deployment processes + - Service mesh considerations + +7. **Hands-On Implementation** + - Setting up the complete system + - Testing API endpoints + - Viewing distributed traces + - Deploying to Kubernetes + +## πŸ”§ Technical Implementation + +### Technologies Explained + +#### Core Services +- **Config Server (Port 8888)**: Centralized configuration management +- **Discovery Service (Port 8761)**: Service registry using Netflix Eureka +- **API Gateway (Port 8222)**: Single entry point with Spring Cloud Gateway + +#### Business Services +- **Customer Service**: MongoDB for flexible customer profiles with validation circuit breakers +- **Product Service**: PostgreSQL for structured product data +- **Order Service**: Orchestrates order workflow with Saga patterns and multiple circuit breakers +- **Payment Service**: Handles secure payment processing with compensation logic +- **Notification Service**: Event-driven email notifications + +#### Infrastructure +- **PostgreSQL**: Relational database for transactional data +- **MongoDB**: Document database for flexible schemas +- **Apache Kafka**: Event streaming platform +- **Zipkin**: Distributed tracing system +- **Docker Compose**: Container orchestration +- **Kubernetes**: Production container orchestration +- **Helm**: Kubernetes package manager +- **Resilience4j**: Circuit breakers and resilience patterns + +### Design Patterns Demonstrated + +1. **Service Registry & Discovery** + - Implementation: Netflix Eureka + - Benefits: Dynamic service location, load balancing, health monitoring + +2. **API Gateway** + - Implementation: Spring Cloud Gateway + - Benefits: Single entry point, cross-cutting concerns, request routing + +3. **Database per Service** + - Implementation: PostgreSQL + MongoDB + - Benefits: Data isolation, technology diversity, independent scaling + +4. **Event-Driven Architecture** + - Implementation: Apache Kafka + - Benefits: Loose coupling, asynchronous processing, scalability + +5. **Externalized Configuration** + - Implementation: Spring Cloud Config + - Benefits: Environment-specific configs, runtime updates, security + +6. **Distributed Tracing** + - Implementation: Zipkin + - Benefits: Performance monitoring, debugging, dependency visualization + +7. **Saga Orchestration Pattern** + - Implementation: OrderSagaOrchestrator with compensation logic + - Benefits: Distributed transaction management, data consistency, failure recovery + +8. **Circuit Breaker Pattern** + - Implementation: Resilience4j with specialized saga circuit breakers + - Benefits: Fault tolerance, graceful degradation, system stability + +9. **Blue-Green Deployment** + - Implementation: Kubernetes deployment strategies + - Benefits: Zero-downtime deployments, quick rollback, risk mitigation + +10. **Package Management** + - Implementation: Helm charts for Kubernetes + - Benefits: Templated deployments, version management, configuration management + +## πŸ§ͺ Testing Scenarios + +The platform includes practical testing scenarios: + +### 1. Create Customer +```bash +curl -X POST http://localhost:8222/api/customers \ + -H "Content-Type: application/json" \ + -d '{ + "firstname": "John", + "lastname": "Doe", + "email": "john.doe@example.com", + "address": { + "street": "123 Main St", + "houseNumber": "123", + "zipCode": "12345" + } + }' +``` + +### 2. Add Product +```bash +curl -X POST http://localhost:8222/api/products \ + -H "Content-Type: application/json" \ + -d '{ + "name": "MacBook Pro", + "description": "Apple MacBook Pro 16-inch", + "availableQuantity": 10, + "price": 2499.99 + }' +``` + +### 3. Simulate Saga Compensation +```bash +# Trigger a failed order to see saga compensation in action +curl -X POST http://localhost:8222/api/orders \ + -H "Content-Type: application/json" \ + -d '{ + "customerId": "invalid_customer_id", + "products": [ + { + "productId": "product_id_here", + "quantity": 999 + } + ], + "paymentMethod": "PAYPAL" + }' +``` + +### 4. Circuit Breaker Testing +```bash +# Test circuit breaker resilience by making multiple rapid requests +for i in {1..10}; do + curl -X GET http://localhost:8222/api/customers/invalid_id + sleep 0.1 +done +``` + +## πŸ“Š Monitoring Dashboards + +Access these URLs once your microservices are running: + +- **Eureka Discovery**: http://localhost:8761 - Service registry dashboard +- **PostgreSQL Admin**: http://localhost:5050 - Database administration +- **MongoDB Admin**: http://localhost:8081 - MongoDB management +- **Zipkin Tracing**: http://localhost:9411 - Distributed tracing visualization +- **Email Testing**: http://localhost:1080 - Email notification testing +- **Circuit Breaker Metrics**: Available through Spring Boot Actuator endpoints + +## 🐳 Kubernetes Deployment + +### Prerequisites for Kubernetes +- Kubernetes cluster (local with minikube or cloud provider) +- kubectl CLI tool +- Helm 3.x +- Docker registry access + +### Deploy with Helm + +1. **Add Helm Repository** (if using external charts): + ```bash + helm repo add microservices-chart ./helm-charts + helm repo update + ``` + +2. **Deploy Infrastructure**: + ```bash + helm install postgres stable/postgresql + helm install mongodb stable/mongodb + helm install kafka strimzi/strimzi-kafka-operator + ``` + +3. **Deploy Microservices**: + ```bash + # Deploy each service with Helm + helm install config-server ./helm-charts/config-server + helm install discovery-service ./helm-charts/discovery + helm install api-gateway ./helm-charts/gateway + + # Deploy business services + helm install customer-service ./helm-charts/customer + helm install product-service ./helm-charts/product + helm install order-service ./helm-charts/order + helm install payment-service ./helm-charts/payment + helm install notification-service ./helm-charts/notification + ``` + +### Blue-Green Deployment + +1. **Prepare Green Environment**: + ```bash + kubectl apply -f k8s/green-deployment.yaml + ``` + +2. **Switch Traffic**: + ```bash + kubectl patch service api-gateway -p '{"spec":{"selector":{"version":"green"}}}' + ``` + +3. **Rollback if Needed**: + ```bash + kubectl patch service api-gateway -p '{"spec":{"selector":{"version":"blue"}}}' + ``` + +## πŸš€ Getting Started with the Actual System + +### Prerequisites +- Java 17+ +- Maven 3.6+ +- Docker & Docker Compose +- IDE (IntelliJ IDEA or VS Code) +- kubectl (for Kubernetes deployment) +- Helm 3.x (for package management) + +### Local Development Setup + +1. **Start Infrastructure**: + ```bash + docker-compose up -d + ``` + +2. **Start Core Services** (in order): + ```bash + # Config Server first + cd services/config-server && mvn spring-boot:run + + # Discovery Service + cd services/discovery && mvn spring-boot:run + + # Gateway + cd services/gateway && mvn spring-boot:run + ``` + +3. **Start Business Services**: + ```bash + # Customer Service + cd services/customer && mvn spring-boot:run + + # Product Service + cd services/product && mvn spring-boot:run + + # Order Service (with Saga orchestration) + cd services/order && mvn spring-boot:run + + # Payment Service + cd services/payment && mvn spring-boot:run + + # Notification Service + cd services/notification && mvn spring-boot:run + ``` + +### Production Kubernetes Setup + +1. **Build Container Images**: + ```bash + # Build all service images + docker build -t pramithamj/config-server:latest services/config-server/ + docker build -t pramithamj/discovery:latest services/discovery/ + docker build -t pramithamj/gateway:latest services/gateway/ + docker build -t pramithamj/customer:latest services/customer/ + docker build -t pramithamj/product:latest services/product/ + docker build -t pramithamj/order:latest services/order/ + docker build -t pramithamj/payment:latest services/payment/ + docker build -t pramithamj/notification:latest services/notification/ + ``` + +2. **Deploy to Kubernetes**: + ```bash + # Apply namespace + kubectl create namespace microservices + + # Deploy infrastructure + kubectl apply -f k8s/infrastructure/ -n microservices + + # Deploy services + kubectl apply -f k8s/services/ -n microservices + + # Or use Helm + helm install microservices-stack ./helm-charts/ -n microservices + ``` + +## πŸ’‘ Learning Tips + +1. **Start with the Overview** to understand the business context +2. **Explore the Architecture** interactively before diving into code +3. **Read each Service section** to understand responsibilities +4. **Study the Design Patterns** to grasp architectural concepts including Saga and Circuit Breaker patterns +5. **Learn the Deployment section** to understand Kubernetes and Helm deployment strategies +6. **Follow the Hands-On guide** to see everything in action +7. **Use the monitoring tools** to observe system behavior +8. **Test circuit breakers** by simulating failures +9. **Observe saga compensation** during failed transactions +10. **Practice blue-green deployment** in a safe environment + +## 🎨 Customization + +### Adding New Content +- Edit `index.html` to add new sections +- Update `serviceDetails` in `script.js` for new services +- Modify `styles.css` for visual customizations + +### Extending Functionality +- Add new interactive features in `script.js` +- Create additional testing scenarios +- Integrate with actual running services for live data + +## πŸ“± Mobile Experience + +The platform is fully responsive and works on: +- Desktop computers +- Tablets +- Mobile phones + +Navigation is optimized for touch interfaces with collapsible menus and touch-friendly buttons. + +## πŸŽ“ Educational Value + +This platform serves as: +- **University coursework** for distributed systems and microservices architecture +- **Professional training** for enterprise microservices adoption +- **Self-study resource** for developers learning modern patterns +- **Architecture reference** for teams implementing saga patterns and circuit breakers +- **Interview preparation** for system design and microservices questions +- **DevOps training** for Kubernetes and Helm deployment strategies +- **Resilience engineering** education for fault-tolerant system design + +## πŸ“š Advanced Topics Covered + +### Saga Pattern Implementation +- **Orchestration vs Choreography**: Learn when to use each approach +- **Compensation Logic**: Understand rollback mechanisms for distributed transactions +- **Saga Recovery**: Handle partial failures and system resilience + +### Circuit Breaker Patterns +- **Failure Thresholds**: Configure appropriate failure rates for different scenarios +- **Fallback Strategies**: Implement graceful degradation when services fail +- **Recovery Mechanisms**: Automatic and manual circuit breaker recovery + +### Kubernetes Deployment Strategies +- **Blue-Green Deployments**: Zero-downtime deployment strategies +- **Rolling Updates**: Gradual service updates with minimal disruption +- **Canary Releases**: Risk-mitigated feature rollouts + +### Observability and Monitoring +- **Distributed Tracing**: Track requests across multiple services +- **Circuit Breaker Metrics**: Monitor system resilience patterns +- **Saga Transaction Monitoring**: Observe distributed transaction flows + +## πŸ“ž Support + +For questions about the microservices implementation: +1. Review the business needs in `resources/business needs.txt` +2. Check the curriculum in `resources/curriculum.txt` +3. Study the patterns in `resources/distributed patterns.txt` +4. Explore the source code in the `services/` directory + +## πŸ”„ Updates + +This learning platform includes the latest features: + +### Recently Added +- **Saga Orchestration Patterns**: Complete implementation with compensation logic +- **Circuit Breaker Integration**: Five specialized circuit breakers for different scenarios +- **Kubernetes Deployment Guide**: Step-by-step production deployment instructions +- **Helm Charts Documentation**: Package management for Kubernetes deployments +- **Blue-Green Deployment**: Zero-downtime deployment strategies +- **Interactive Circuit Breaker Demo**: Visual learning tool for resilience patterns + +### Upcoming Features +- Additional microservices patterns (CQRS, Event Sourcing) +- Advanced monitoring examples with Prometheus and Grafana +- Performance optimization techniques and load testing +- Security implementation details (OAuth2, JWT, API Security) +- Service mesh implementation with Istio +- Cloud-native deployment strategies (AWS EKS, Google GKE, Azure AKS) +- GitOps workflows with ArgoCD + +--- + +**Happy Learning! πŸš€** + +Master modern microservices architecture with advanced patterns, resilience strategies, and production-ready Kubernetes deployment through this comprehensive, interactive learning experience. diff --git a/learning-platform/index.html b/learning-platform/index.html new file mode 100644 index 0000000..15d0749 --- /dev/null +++ b/learning-platform/index.html @@ -0,0 +1,1577 @@ + + + + + + Microservices Architecture Learning Platform + + + + + + + + + +
+
+

Master Microservices Architecture

+

Learn through a complete e-commerce implementation with Spring Boot & Java

+
+
+

8

+

Microservices

+
+
+

9

+

Design Patterns

+
+
+

12

+

Technologies

+
+
+

5

+

Circuit Breakers

+
+
+ + + +
+ +
+
+
+ + +
+
+

Business Problem & Solution

+
+
+
+

The Challenge

+
    +
  • E-commerce business with no digital solutions
  • +
  • Manual product catalog management
  • +
  • Customer data scattered across systems
  • +
  • Payment processing inefficiencies
  • +
  • No automated notifications
  • +
  • Scalability concerns for growth
  • +
+
+
+
+
+

The Solution

+
    +
  • Product Service: Centralized catalog with unique codes
  • +
  • Customer Service: Complete customer profiles
  • +
  • Order Service: Automated order processing
  • +
  • Payment Service: Secure payment handling
  • +
  • Notification Service: Automated email confirmations
  • +
  • Gateway & Discovery: Scalable infrastructure
  • +
+
+
+
+
+
+ + +
+
+

Interactive Architecture Diagram

+
+
+

Client Layer

+
+ + Web Client +
+
+ +
+

Gateway Layer

+
+ + API Gateway + Port: 8222 +
+
+ +
+

Core Services

+
+ + Config Server + Port: 8888 +
+
+ + Discovery Service + Port: 8761 +
+
+ +
+

Business Services

+
+ + Customer Service + MongoDB +
+
+ + Product Service + PostgreSQL +
+
+ + Order Service + PostgreSQL +
+
+ + Payment Service + PostgreSQL +
+
+ + Notification Service + MongoDB +
+
+ +
+

Infrastructure

+
+
+ + PostgreSQL +
+
+ + MongoDB +
+
+ + Kafka +
+
+ + Zipkin +
+
+
+
+
+
+ + +
+
+

Microservices Deep Dive

+ + + + + +
+ +
+
+
+
+
+

Config Server

+

Port: 8888

+

Technology: Spring Cloud Config

+

Database: File System

+
+
+
+
+
Purpose & Functionality
+

The Config Server acts as a centralized configuration management system for all microservices. It eliminates the need to rebuild and redeploy services when configuration changes.

+ +
Key Features:
+
    +
  • Centralized configuration for all services
  • +
  • Environment-specific configurations (dev, staging, prod)
  • +
  • Dynamic configuration refresh without restart
  • +
  • Version control integration with Git
  • +
  • Security for sensitive configurations
  • +
+ +
How it Works:
+
    +
  1. Stores all service configurations in a central location
  2. +
  3. Services request their configuration on startup
  4. +
  5. Provides environment-specific overrides
  6. +
  7. Enables runtime configuration updates via refresh endpoints
  8. +
+
+
+
+
+
+ + +
+
+
+
+
+

Discovery Service

+

Port: 8761

+

Technology: Netflix Eureka

+

Database: In-Memory Registry

+
+
+
+
+
Service Registry & Discovery
+

The Discovery Service maintains a registry of all available microservice instances, enabling dynamic service-to-service communication without hardcoded URLs.

+ +
Key Features:
+
    +
  • Automatic service registration
  • +
  • Health check monitoring
  • +
  • Load balancing support
  • +
  • Failover and resilience
  • +
  • Web dashboard for monitoring
  • +
+ +
Service Registration Flow:
+
    +
  1. Services register themselves on startup
  2. +
  3. Send periodic heartbeats to maintain registration
  4. +
  5. Other services discover available instances
  6. +
  7. Automatic deregistration on shutdown
  8. +
+
+
+
+
+
+ + +
+
+
+
+
+

API Gateway

+

Port: 8222

+

Technology: Spring Cloud Gateway

+

Features: Routing, Load Balancing, Tracing

+
+
+
+
+
Single Entry Point
+

The API Gateway serves as the single entry point for all client requests, routing them to appropriate microservices while providing cross-cutting concerns.

+ +
Key Responsibilities:
+
    +
  • Request routing to appropriate services
  • +
  • Load balancing across service instances
  • +
  • Authentication and authorization
  • +
  • Request/response transformation
  • +
  • Rate limiting and throttling
  • +
  • Distributed tracing integration
  • +
+ +
Routing Example:
+
+ + /api/customers/** β†’ Customer Service
+ /api/products/** β†’ Product Service
+ /api/orders/** β†’ Order Service
+ /api/payments/** β†’ Payment Service +
+
+
+
+
+
+
+ + +
+
+
+
+
+

Customer Service

+

Database: MongoDB

+

Technology: Spring Data MongoDB

+

Pattern: Document Store

+
+
+
+
+
Customer Profile Management
+

Manages all customer-related data and operations, storing flexible customer profiles in MongoDB for scalability and schema evolution.

+ +
Data Model:
+
    +
  • Personal Info: First name, Last name, Email
  • +
  • Address: Street, City, Zipcode
  • +
  • Metadata: Creation date, Update history
  • +
+ +
API Endpoints:
+
+
+ POST /api/customers - Create customer +
+
+ GET /api/customers/{id} - Get customer details +
+
+ PUT /api/customers/{id} - Update customer +
+
+ GET /api/customers/exists/{id} - Check existence +
+
+
+
+
+
+
+ + +
+
+
+
+
+

Order Service

+

Database: PostgreSQL

+

Technology: Spring Data JPA

+

Integration: OpenFeign, Kafka

+
+
+
+
+
Order Processing Hub
+

Orchestrates the complete order lifecycle, integrating with multiple services to validate customers, check product availability, process payments, and trigger notifications.

+ +
Order Processing Flow:
+
    +
  1. Validation: Check customer exists via Customer Service
  2. +
  3. Inventory: Validate products and quantities via Product Service
  4. +
  5. Order Creation: Create order with PENDING status
  6. +
  7. Payment: Process payment via Payment Service
  8. +
  9. Confirmation: Send confirmation via Kafka to Notification Service
  10. +
  11. Status Update: Update order status based on payment result
  12. +
+ +
Service Dependencies:
+
    +
  • CustomerClient: Customer validation
  • +
  • ProductClient: Product verification
  • +
  • PaymentClient: Payment processing
  • +
  • Kafka Producer: Event notifications
  • +
+
+
+
+
+
+ + +
+
+
+
+
+

Notification Service

+

Database: MongoDB

+

Technology: Kafka, Thymeleaf

+

Integration: Java Mail Sender

+
+
+
+
+
Event-Driven Notifications
+

Handles all notification requirements using event-driven architecture. Listens to Kafka events and sends appropriate email notifications to customers.

+ +
Notification Types:
+
    +
  • Order Confirmation: When order is successfully created
  • +
  • Payment Success: When payment is processed successfully
  • +
  • Payment Failure: When payment processing fails
  • +
  • Order Updates: Status changes and updates
  • +
+ +
Event Processing Flow:
+
    +
  1. Listen to Kafka topics (order-topic, payment-topic)
  2. +
  3. Parse incoming event messages
  4. +
  5. Generate HTML email using Thymeleaf templates
  6. +
  7. Send email via configured SMTP server
  8. +
  9. Store notification history in MongoDB
  10. +
+ +
Email Templates:
+
    +
  • order-confirmation.html
  • +
  • payment-confirmation.html
  • +
  • payment-failed.html
  • +
+
+
+
+
+
+
+
+
+ + +
+
+

Microservices Design Patterns

+
+ +
+
+ +
+

Service Registry & Discovery

+

Implementation: Netflix Eureka

+

Services automatically register themselves and discover other services dynamically.

+
+
Benefits:
+
    +
  • Dynamic service location
  • +
  • Load balancing
  • +
  • Health monitoring
  • +
  • Fault tolerance
  • +
+
+
+ + +
+
+ +
+

API Gateway

+

Implementation: Spring Cloud Gateway

+

Single entry point for all client requests with routing and cross-cutting concerns.

+
+
Benefits:
+
    +
  • Simplified client interactions
  • +
  • Centralized security
  • +
  • Request transformation
  • +
  • Rate limiting
  • +
+
+
+ + +
+
+ +
+

Database per Service

+

Implementation: PostgreSQL + MongoDB

+

Each service owns its data and database, ensuring loose coupling.

+
+
Benefits:
+
    +
  • Data isolation
  • +
  • Technology diversity
  • +
  • Independent scaling
  • +
  • Fault isolation
  • +
+
+
+ + +
+
+ +
+

Event-Driven Architecture

+

Implementation: Apache Kafka

+

Services communicate through events for loose coupling and resilience.

+
+
Benefits:
+
    +
  • Loose coupling
  • +
  • Asynchronous processing
  • +
  • Event sourcing
  • +
  • Scalability
  • +
+
+
+ + +
+
+ +
+

Externalized Configuration

+

Implementation: Spring Cloud Config

+

Configuration management separated from application code for flexibility.

+
+
Benefits:
+
    +
  • Environment-specific configs
  • +
  • Runtime updates
  • +
  • Version control
  • +
  • Security separation
  • +
+
+
+ + +
+
+ +
+

Distributed Tracing

+

Implementation: Zipkin

+

Track requests across multiple services for debugging and monitoring.

+
+
Benefits:
+
    +
  • Performance monitoring
  • +
  • Debugging assistance
  • +
  • Service dependencies
  • +
  • Bottleneck identification
  • +
+
+
+ + +
+
+ +
+

Circuit Breaker

+

Implementation: Resilience4j

+

Prevents cascading failures by monitoring service calls and failing fast when downstream services are unavailable.

+
+
Benefits:
+
    +
  • Fault tolerance
  • +
  • Automatic recovery
  • +
  • Resource protection
  • +
  • Cascading failure prevention
  • +
+
+
+
Saga-Specific Circuit Breakers:
+
    +
  • Saga Orchestration: 40% failure threshold
  • +
  • Saga Compensation: 25% failure threshold
  • +
  • Saga Recovery: 60% failure threshold
  • +
  • Customer Validation: 35% failure threshold
  • +
  • Inventory Reservation: 45% failure threshold
  • +
+
+
+ + +
+
+ +
+

Saga Pattern

+

Implementation: Choreography + Orchestration

+

Manages distributed transactions across multiple services with compensation logic for rollback scenarios.

+
+
Benefits:
+
    +
  • Distributed transaction management
  • +
  • Automatic compensation
  • +
  • Eventual consistency
  • +
  • Resilience to failures
  • +
+
+
+
Saga Flow:
+
    +
  1. Customer Validation: Verify customer exists
  2. +
  3. Inventory Reservation: Reserve products
  4. +
  5. Payment Processing: Process payment
  6. +
  7. Order Completion: Finalize order
  8. +
+

Recovery: Automatic timeout handling and retry mechanisms with exponential backoff

+
+
+
+
+
+ + +
+
+

Hands-On Learning

+ + +
+

Getting Started

+ + +
+
+
Clone the Repository First
+

Before following the setup steps, clone the complete source code from GitHub:

+
+ git clone https://github.com/PramithaMJ/fully-completed-microservices.git +cd fully-completed-microservices +
+

+ Repository: + + https://github.com/PramithaMJ/fully-completed-microservices + +

+
+
+ +
+
+
1
+
+
Prerequisites
+
    +
  • Java 17+
  • +
  • Maven 3.6+
  • +
  • Docker & Docker Compose
  • +
  • IDE (IntelliJ IDEA or VS Code)
  • +
+
+
+ +
+
2
+
+
Start Infrastructure
+
+ docker-compose up -d +
+

This starts PostgreSQL, MongoDB, Kafka, Zipkin, and admin tools.

+
+
+ +
+
3
+
+
Start Core Services
+
+
+ 1. Config Server (Port 8888) +
+
+ 2. Discovery Service (Port 8761) +
+
+ 3. API Gateway (Port 8222) +
+
+
+
+ +
+
4
+
+
Start Business Services
+

Start Customer, Product, Order, Payment, and Notification services in any order.

+
+
+
+
+ + +
+

Testing Scenarios

+
+ + +
+
+
+
Create a Customer
+
+
API Request:
+
curl -X POST http://localhost:8222/api/customers \
+  -H "Content-Type: application/json" \
+  -d '{
+    "firstname": "John",
+    "lastname": "Doe",
+    "email": "john.doe@example.com",
+    "address": {
+      "street": "123 Main St",
+      "houseNumber": "123",
+      "zipCode": "12345"
+    }
+  }'
+
+
+
What Happens:
+
    +
  1. Request hits API Gateway on port 8222
  2. +
  3. Gateway routes to Customer Service via Eureka discovery
  4. +
  5. Customer data stored in MongoDB
  6. +
  7. Response includes generated customer ID
  8. +
  9. Trace information sent to Zipkin
  10. +
+
+
+
+ +
+
+
Add a Product
+
+
API Request:
+
curl -X POST http://localhost:8222/api/products \
+  -H "Content-Type: application/json" \
+  -d '{
+    "name": "MacBook Pro",
+    "description": "Apple MacBook Pro 16-inch",
+    "availableQuantity": 10,
+    "price": 2499.99
+  }'
+
+
+
What Happens:
+
    +
  1. Request routed through Gateway to Product Service
  2. +
  3. Product stored in PostgreSQL database
  4. +
  5. Auto-generated product ID returned
  6. +
  7. Inventory tracking initialized
  8. +
+
+
+
+ +
+
+
Place an Order
+
+
API Request:
+
curl -X POST http://localhost:8222/api/orders \
+  -H "Content-Type: application/json" \
+  -d '{
+    "customerId": "customer_id_here",
+    "products": [
+      {
+        "productId": "product_id_here",
+        "quantity": 1
+      }
+    ],
+    "paymentMethod": "PAYPAL"
+  }'
+
+
+
Complete Order Flow:
+
    +
  1. Validation: Order Service validates customer exists
  2. +
  3. Inventory: Checks product availability
  4. +
  5. Order Creation: Creates order with PENDING status
  6. +
  7. Payment: Processes payment via Payment Service
  8. +
  9. Kafka Event: Publishes order confirmation event
  10. +
  11. Email: Notification Service sends confirmation email
  12. +
  13. Status Update: Order status updated to PAID
  14. +
+
+
+
+ +
+
+
View Distributed Tracing
+
+
Zipkin Dashboard:
+

Open Zipkin UI

+ +
What You'll See:
+
    +
  • Complete request trace across all services
  • +
  • Service call duration and timing
  • +
  • Service dependencies visualization
  • +
  • Performance bottlenecks identification
  • +
  • Error propagation tracking
  • +
+ +
Trace Components:
+
+
Gateway
+
β†’
+
Order Service
+
β†’
+
Customer Service
+
β†’
+
Product Service
+
β†’
+
Payment Service
+
+
+
+
+
+
+
+ + +
+

Monitoring Dashboards

+
+
+
Eureka Discovery
+

View all registered services and their health status

+ Open Dashboard +
+
+
PostgreSQL Admin
+

Manage PostgreSQL databases for Order, Payment, and Product services

+ Open PgAdmin +
+
+
MongoDB Admin
+

View MongoDB collections for Customer and Notification services

+ Open Mongo Express +
+
+
Email Testing
+

View sent emails and test notification functionality

+ Open MailDev +
+
+
+
+
+ + +
+
+
+
+

+ + Complete Source Code Available +

+

+ Access the complete, production-ready microservices implementation with detailed documentation, + configuration files, and step-by-step commit history. +

+ +
+
+
+
    +
  • Complete Spring Boot 3.2.5 implementation
  • +
  • Docker Compose configuration
  • +
  • Detailed README documentation
  • +
  • Step-by-step commit history
  • +
+
+
+
    +
  • Production-ready configurations
  • +
  • Comprehensive testing examples
  • +
  • MIT License - Free to use
  • +
  • Active community support
  • +
+
+
+
+
+ +
+
+
+ +

Repository

+
+
+
fully-completed-microservices
+

Spring Boot Microservices Architecture

+
+ + Java + + + Production Ready + + + Educational + +
+
+ +
+
+
+ + + +
+
+ + +
+
+

Microservices Deep Dive

+ + + + + +
+ +
+
+
+
+
+

Config Server

+

Port: 8888

+

Technology: Spring Cloud Config

+

Database: File System

+
+
+
+
+
Purpose & Functionality
+

The Config Server acts as a centralized configuration management system for all microservices. It eliminates the need to rebuild and redeploy services when configuration changes.

+ +
Key Features:
+
    +
  • Centralized configuration for all services
  • +
  • Environment-specific configurations (dev, staging, prod)
  • +
  • Dynamic configuration refresh without restart
  • +
  • Version control integration with Git
  • +
  • Security for sensitive configurations
  • +
+ +
How it Works:
+
    +
  1. Stores all service configurations in a central location
  2. +
  3. Services request their configuration on startup
  4. +
  5. Provides environment-specific overrides
  6. +
  7. Enables runtime configuration updates via refresh endpoints
  8. +
+
+
+
+
+
+ + +
+
+
+
+
+

Discovery Service

+

Port: 8761

+

Technology: Netflix Eureka

+

Database: In-Memory Registry

+
+
+
+
+
Service Registry & Discovery
+

The Discovery Service maintains a registry of all available microservice instances, enabling dynamic service-to-service communication without hardcoded URLs.

+ +
Key Features:
+
    +
  • Automatic service registration
  • +
  • Health check monitoring
  • +
  • Load balancing support
  • +
  • Failover and resilience
  • +
  • Web dashboard for monitoring
  • +
+ +
Service Registration Flow:
+
    +
  1. Services register themselves on startup
  2. +
  3. Send periodic heartbeats to maintain registration
  4. +
  5. Other services discover available instances
  6. +
  7. Automatic deregistration on shutdown
  8. +
+
+
+
+
+
+ + +
+
+
+
+
+

API Gateway

+

Port: 8222

+

Technology: Spring Cloud Gateway

+

Features: Routing, Load Balancing, Tracing

+
+
+
+
+
Single Entry Point
+

The API Gateway serves as the single entry point for all client requests, routing them to appropriate microservices while providing cross-cutting concerns.

+ +
Key Responsibilities:
+
    +
  • Request routing to appropriate services
  • +
  • Load balancing across service instances
  • +
  • Authentication and authorization
  • +
  • Request/response transformation
  • +
  • Rate limiting and throttling
  • +
  • Distributed tracing integration
  • +
+ +
Routing Example:
+
+ + /api/customers/** β†’ Customer Service
+ /api/products/** β†’ Product Service
+ /api/orders/** β†’ Order Service
+ /api/payments/** β†’ Payment Service +
+
+
+
+
+
+
+ + +
+
+
+
+
+

Customer Service

+

Database: MongoDB

+

Technology: Spring Data MongoDB

+

Pattern: Document Store

+
+
+
+
+
Customer Profile Management
+

Manages all customer-related data and operations, storing flexible customer profiles in MongoDB for scalability and schema evolution.

+ +
Data Model:
+
    +
  • Personal Info: First name, Last name, Email
  • +
  • Address: Street, City, Zipcode
  • +
  • Metadata: Creation date, Update history
  • +
+ +
API Endpoints:
+
+
+ POST /api/customers - Create customer +
+
+ GET /api/customers/{id} - Get customer details +
+
+ PUT /api/customers/{id} - Update customer +
+
+ GET /api/customers/exists/{id} - Check existence +
+
+
+
+
+
+
+ + +
+
+
+
+
+

Order Service

+

Database: PostgreSQL

+

Technology: Spring Data JPA

+

Integration: OpenFeign, Kafka

+
+
+
+
+
Order Processing Hub
+

Orchestrates the complete order lifecycle, integrating with multiple services to validate customers, check product availability, process payments, and trigger notifications.

+ +
Order Processing Flow:
+
    +
  1. Validation: Check customer exists via Customer Service
  2. +
  3. Inventory: Validate products and quantities via Product Service
  4. +
  5. Order Creation: Create order with PENDING status
  6. +
  7. Payment: Process payment via Payment Service
  8. +
  9. Confirmation: Send confirmation via Kafka to Notification Service
  10. +
  11. Status Update: Update order status based on payment result
  12. +
+ +
Service Dependencies:
+
    +
  • CustomerClient: Customer validation
  • +
  • ProductClient: Product verification
  • +
  • PaymentClient: Payment processing
  • +
  • Kafka Producer: Event notifications
  • +
+
+
+
+
+
+ + +
+
+
+
+
+

Notification Service

+

Database: MongoDB

+

Technology: Kafka, Thymeleaf

+

Integration: Java Mail Sender

+
+
+
+
+
Event-Driven Notifications
+

Handles all notification requirements using event-driven architecture. Listens to Kafka events and sends appropriate email notifications to customers.

+ +
Notification Types:
+
    +
  • Order Confirmation: When order is successfully created
  • +
  • Payment Success: When payment is processed successfully
  • +
  • Payment Failure: When payment processing fails
  • +
  • Order Updates: Status changes and updates
  • +
+ +
Event Processing Flow:
+
    +
  1. Listen to Kafka topics (order-topic, payment-topic)
  2. +
  3. Parse incoming event messages
  4. +
  5. Generate HTML email using Thymeleaf templates
  6. +
  7. Send email via configured SMTP server
  8. +
  9. Store notification history in MongoDB
  10. +
+ +
Email Templates:
+
    +
  • order-confirmation.html
  • +
  • payment-confirmation.html
  • +
  • payment-failed.html
  • +
+
+
+
+
+
+
+
+
+ + +
+
+

Enterprise Deployment with Kubernetes

+ + +
+
+
+
+ +
+

Kubernetes Native

+

Complete Kubernetes deployment manifests with Helm charts for production-ready deployment.

+
    +
  • Deployment configurations for all services
  • +
  • Service discovery and load balancing
  • +
  • ConfigMaps and Secrets management
  • +
  • Ingress controllers and networking
  • +
+
+
+
+
+
+ +
+

Blue-Green Deployment

+

Zero-downtime deployment strategy with automated rollback capabilities for production environments.

+
    +
  • Automated blue-green switching
  • +
  • Health checks and readiness probes
  • +
  • Instant rollback on failure
  • +
  • Traffic splitting for gradual rollouts
  • +
+
+
+
+ + +
+

Helm Chart Deployment

+
+
+
+
Deploy the entire microservices stack:
+
+ # Install the complete microservices platform +helm install ecommerce-platform ./k8s/helm-charts \ + --namespace microservices \ + --create-namespace + +# Verify deployment +kubectl get pods -n microservices + +# Check services +kubectl get svc -n microservices +
+
+
+
+
+
Helm Features:
+
    +
  • Parameterized deployments
  • +
  • Environment-specific values
  • +
  • Dependency management
  • +
  • Release versioning
  • +
  • Easy upgrades & rollbacks
  • +
+
+
+
+
+ + +
+

Blue-Green Deployment Process

+
+
+
1
+
Prepare Green Environment
+

Deploy new version to green environment while blue serves production traffic

+
+ ./k8s/blue-green-deploy.sh prepare-green v2.0.0 +
+
+ +
+
2
+
Health Check & Validation
+

Automated health checks ensure green environment is ready for production

+
+ ./k8s/blue-green-deploy.sh validate-green +
+
+ +
+
3
+
Switch Traffic
+

Instantly switch production traffic from blue to green environment

+
+ ./k8s/blue-green-deploy.sh switch-to-green +
+
+ +
+
4
+
Rollback if Needed
+

Instant rollback to blue environment if issues are detected

+
+ ./k8s/blue-green-deploy.sh rollback-to-blue +
+
+
+
+ + +
+

Production-Ready Kubernetes Features

+
+
+
+
Security
+
    +
  • Network policies for isolation
  • +
  • RBAC (Role-Based Access Control)
  • +
  • Secrets management
  • +
  • Pod security contexts
  • +
+
+
+
+
+
Monitoring & Observability
+
    +
  • Prometheus metrics collection
  • +
  • Grafana dashboards
  • +
  • Distributed tracing with Jaeger
  • +
  • Centralized logging with ELK stack
  • +
+
+
+
+
+
Scalability
+
    +
  • Horizontal Pod Autoscaling (HPA)
  • +
  • Vertical Pod Autoscaling (VPA)
  • +
  • Resource quotas and limits
  • +
  • Load balancing with ingress
  • +
+
+
+
+
+ + +
+

Quick Start with Kubernetes

+
+
Prerequisites:
+
    +
  • Docker Desktop with Kubernetes enabled
  • +
  • kubectl CLI tool
  • +
  • Helm 3.x installed
  • +
+
+ +
+ # 1. Build and push Docker images +./k8s/build-images.sh + +# 2. Deploy with Helm +helm install microservices ./k8s/helm-charts \ + --namespace microservices \ + --create-namespace \ + --set global.environment=production + +# 3. Access the application +kubectl port-forward svc/gateway-service 8222:8222 -n microservices + +# 4. Monitor deployment +kubectl get all -n microservices +kubectl logs -f deployment/gateway-service -n microservices +
+
+
+
+ + + + + + + + + + + + diff --git a/learning-platform/script.js b/learning-platform/script.js new file mode 100644 index 0000000..100d468 --- /dev/null +++ b/learning-platform/script.js @@ -0,0 +1,1133 @@ +// Learning Platform Interactive JavaScript + +document.addEventListener('DOMContentLoaded', function() { + // Initialize the application + initializeApp(); +}); + +function initializeApp() { + // Setup navigation + setupSmoothScrolling(); + + // Setup interactive architecture diagram + setupArchitectureDiagram(); + + // Setup service modals + setupServiceModals(); + + // Setup copy code functionality + setupCodeCopying(); + + // Setup progress tracking + setupProgressTracking(); + + // Setup dynamic content loading + setupDynamicContent(); + + console.log('πŸš€ Microservices Learning Platform Initialized'); +} + +// Smooth scrolling navigation +function setupSmoothScrolling() { + const navLinks = document.querySelectorAll('a[href^="#"]'); + + navLinks.forEach(link => { + link.addEventListener('click', function(e) { + e.preventDefault(); + + const targetId = this.getAttribute('href').substring(1); + const targetElement = document.getElementById(targetId); + + if (targetElement) { + const headerOffset = 80; + const elementPosition = targetElement.getBoundingClientRect().top; + const offsetPosition = elementPosition + window.pageYOffset - headerOffset; + + window.scrollTo({ + top: offsetPosition, + behavior: 'smooth' + }); + + // Update active navigation + updateActiveNavigation(targetId); + } + }); + }); +} + +// Update active navigation item +function updateActiveNavigation(activeId) { + const navLinks = document.querySelectorAll('.navbar-nav .nav-link'); + navLinks.forEach(link => { + link.classList.remove('active'); + if (link.getAttribute('href') === `#${activeId}`) { + link.classList.add('active'); + } + }); +} + +// Interactive Architecture Diagram +function setupArchitectureDiagram() { + const serviceBoxes = document.querySelectorAll('.service-box, .infra-box'); + + serviceBoxes.forEach(box => { + box.addEventListener('click', function() { + const serviceName = this.getAttribute('data-service'); + showServiceDetails(serviceName); + }); + + // Add hover effects + box.addEventListener('mouseenter', function() { + this.style.transform = 'translateY(-10px) scale(1.05)'; + showServiceTooltip(this); + }); + + box.addEventListener('mouseleave', function() { + this.style.transform = 'translateY(0) scale(1)'; + hideServiceTooltip(); + }); + }); +} + +// Service details data +const serviceDetails = { + config: { + title: 'Config Server', + icon: 'fas fa-cog', + port: '8888', + technology: 'Spring Cloud Config', + description: 'Centralized configuration management for all microservices', + features: [ + 'Environment-specific configurations', + 'Dynamic configuration refresh', + 'Git integration for version control', + 'Encrypted sensitive properties', + 'Configuration validation' + ], + endpoints: [ + { method: 'GET', path: '/{application}/{profile}', description: 'Get configuration' }, + { method: 'POST', path: '/refresh', description: 'Refresh configuration' } + ] + }, + discovery: { + title: 'Discovery Service', + icon: 'fas fa-search', + port: '8761', + technology: 'Netflix Eureka', + description: 'Service registry and discovery for dynamic service location', + features: [ + 'Automatic service registration', + 'Health check monitoring', + 'Load balancing support', + 'Failover capabilities', + 'Service instance management' + ], + endpoints: [ + { method: 'GET', path: '/eureka/apps', description: 'List all services' }, + { method: 'GET', path: '/eureka/apps/{service}', description: 'Get service instances' } + ] + }, + gateway: { + title: 'API Gateway', + icon: 'fas fa-door-open', + port: '8222', + technology: 'Spring Cloud Gateway', + description: 'Single entry point for all client requests with routing and filtering', + features: [ + 'Dynamic request routing', + 'Load balancing', + 'Authentication & authorization', + 'Rate limiting', + 'Request/response transformation', + 'Distributed tracing integration' + ], + routes: [ + { path: '/api/customers/**', target: 'Customer Service' }, + { path: '/api/products/**', target: 'Product Service' }, + { path: '/api/orders/**', target: 'Order Service' }, + { path: '/api/payments/**', target: 'Payment Service' } + ] + }, + customer: { + title: 'Customer Service', + icon: 'fas fa-users', + database: 'MongoDB', + technology: 'Spring Data MongoDB', + description: 'Manages customer profiles and related operations', + dataModel: { + fields: ['firstname', 'lastname', 'email', 'address'], + collections: ['customers'] + }, + apis: [ + { method: 'POST', path: '/api/customers', description: 'Create customer' }, + { method: 'GET', path: '/api/customers/{id}', description: 'Get customer details' }, + { method: 'PUT', path: '/api/customers/{id}', description: 'Update customer' }, + { method: 'GET', path: '/api/customers/exists/{id}', description: 'Check existence' } + ] + }, + product: { + title: 'Product Service', + icon: 'fas fa-box', + database: 'PostgreSQL', + technology: 'Spring Data JPA', + description: 'Manages product catalog and inventory', + features: [ + 'Product catalog management', + 'Inventory tracking', + 'Price management', + 'Product categorization' + ] + }, + order: { + title: 'Order Service', + icon: 'fas fa-shopping-cart', + database: 'PostgreSQL', + technology: 'Spring Data JPA + OpenFeign + Kafka', + description: 'Orchestrates order processing workflow', + workflow: [ + 'Validate customer via Customer Service', + 'Check product availability via Product Service', + 'Create order with PENDING status', + 'Process payment via Payment Service', + 'Publish order event to Kafka', + 'Update order status based on payment result' + ] + }, + payment: { + title: 'Payment Service', + icon: 'fas fa-credit-card', + database: 'PostgreSQL', + technology: 'Spring Data JPA', + description: 'Handles payment processing and transactions' + }, + notification: { + title: 'Notification Service', + icon: 'fas fa-envelope', + database: 'MongoDB', + technology: 'Kafka Consumer + Java Mail + Thymeleaf', + description: 'Event-driven email notification system', + events: [ + 'Order confirmation', + 'Payment success', + 'Payment failure', + 'Order status updates' + ] + }, + postgres: { + title: 'PostgreSQL', + icon: 'fas fa-database', + port: '5432', + description: 'Relational database for Order, Payment, and Product services', + features: ['ACID compliance', 'Complex queries', 'Transactions', 'Relationships'] + }, + mongodb: { + title: 'MongoDB', + icon: 'fas fa-leaf', + port: '27017', + description: 'Document database for Customer and Notification services', + features: ['Schema flexibility', 'Horizontal scaling', 'JSON documents', 'Rich queries'] + }, + kafka: { + title: 'Apache Kafka', + icon: 'fas fa-stream', + port: '9092', + description: 'Distributed streaming platform for event-driven communication', + topics: ['order-topic', 'payment-topic', 'notification-topic'] + }, + zipkin: { + title: 'Zipkin', + icon: 'fas fa-route', + port: '9411', + description: 'Distributed tracing system for monitoring microservices', + features: ['Request tracing', 'Performance monitoring', 'Service dependencies', 'Error tracking'] + } +}; + +// Show service details modal +function showServiceDetails(serviceName) { + const service = serviceDetails[serviceName]; + if (!service) return; + + const modal = document.getElementById('serviceModal'); + const title = document.getElementById('serviceModalTitle'); + const body = document.getElementById('serviceModalBody'); + + title.innerHTML = ` ${service.title}`; + body.innerHTML = generateServiceModalContent(service); + + const bsModal = new bootstrap.Modal(modal); + bsModal.show(); +} + +// Generate service modal content +function generateServiceModalContent(service) { + let content = ` +
+
+
+
Overview
+

${service.description}

+
+
+
+ `; + + if (service.port) content += `

Port: ${service.port}

`; + if (service.database) content += `

Database: ${service.database}

`; + if (service.technology) content += `

Technology: ${service.technology}

`; + + content += ` +
+
+
+ `; + + if (service.features) { + content += ` +
+
Key Features
+
    + ${service.features.map(feature => `
  • ${feature}
  • `).join('')} +
+
+ `; + } + + if (service.workflow) { + content += ` +
+
Workflow
+
    + ${service.workflow.map(step => `
  1. ${step}
  2. `).join('')} +
+
+ `; + } + + if (service.apis) { + content += ` +
+
API Endpoints
+
+ ${service.apis.map(api => ` +
+ ${api.method} + ${api.path} + ${api.description} +
+ `).join('')} +
+
+ `; + } + + if (service.routes) { + content += ` +
+
Routes
+
+ ${service.routes.map(route => ` +
+ ${route.path} β†’ ${route.target} +
+ `).join('')} +
+
+ `; + } + + if (service.topics) { + content += ` +
+
Kafka Topics
+
+ ${service.topics.map(topic => `${topic}`).join(' ')} +
+
+ `; + } + + content += `
`; + return content; +} + +// Tooltip functionality +let tooltip = null; + +function showServiceTooltip(element) { + const serviceName = element.getAttribute('data-service'); + const service = serviceDetails[serviceName]; + if (!service) return; + + // Remove existing tooltip + hideServiceTooltip(); + + // Create tooltip + tooltip = document.createElement('div'); + tooltip.className = 'service-tooltip'; + tooltip.innerHTML = ` +
${service.title}
+
${service.description}
+ ${service.port ? `
Port: ${service.port}
` : ''} + `; + + document.body.appendChild(tooltip); + + // Position tooltip + const rect = element.getBoundingClientRect(); + tooltip.style.left = rect.left + rect.width / 2 - tooltip.offsetWidth / 2 + 'px'; + tooltip.style.top = rect.top - tooltip.offsetHeight - 10 + 'px'; + + // Show tooltip + setTimeout(() => tooltip.classList.add('show'), 10); +} + +function hideServiceTooltip() { + if (tooltip) { + tooltip.remove(); + tooltip = null; + } +} + +// Setup service modals +function setupServiceModals() { + // Modal styles + const modalStyles = ` + + `; + + document.head.insertAdjacentHTML('beforeend', modalStyles); +} + +// Code copying functionality +function setupCodeCopying() { + const codeBlocks = document.querySelectorAll('pre code, .code-block code'); + + codeBlocks.forEach(block => { + const container = block.closest('pre, .code-block'); + if (container && !container.querySelector('.copy-btn')) { + const copyBtn = document.createElement('button'); + copyBtn.className = 'copy-btn'; + copyBtn.innerHTML = ''; + copyBtn.title = 'Copy to clipboard'; + + copyBtn.addEventListener('click', () => { + navigator.clipboard.writeText(block.textContent).then(() => { + copyBtn.innerHTML = ''; + copyBtn.style.background = '#28a745'; + + setTimeout(() => { + copyBtn.innerHTML = ''; + copyBtn.style.background = '#667eea'; + }, 2000); + }); + }); + + container.style.position = 'relative'; + container.appendChild(copyBtn); + } + }); + + // Copy button styles + const copyStyles = ` + + `; + + document.head.insertAdjacentHTML('beforeend', copyStyles); +} + +// Progress tracking +function setupProgressTracking() { + const sections = document.querySelectorAll('section[id]'); + let currentSection = ''; + + window.addEventListener('scroll', () => { + const scrollPosition = window.pageYOffset + 100; + + sections.forEach(section => { + const sectionTop = section.offsetTop; + const sectionBottom = sectionTop + section.offsetHeight; + + if (scrollPosition >= sectionTop && scrollPosition < sectionBottom) { + if (currentSection !== section.id) { + currentSection = section.id; + updateActiveNavigation(currentSection); + updateProgressBar(section.id); + } + } + }); + }); + + // Create progress bar + const progressBar = document.createElement('div'); + progressBar.className = 'learning-progress'; + progressBar.innerHTML = ` +
+
+
+
Learning Progress
+ `; + document.body.appendChild(progressBar); + + // Progress bar styles + const progressStyles = ` + + `; + document.head.insertAdjacentHTML('beforeend', progressStyles); +} + +function updateProgressBar(sectionId) { + const sections = ['home', 'overview', 'architecture', 'repository', 'services', 'patterns', 'hands-on']; + const currentIndex = sections.indexOf(sectionId); + const progress = currentIndex >= 0 ? ((currentIndex + 1) / sections.length) * 100 : 0; + + const progressFill = document.querySelector('.progress-fill'); + if (progressFill) { + progressFill.style.width = progress + '%'; + } +} + +// Dynamic content loading +function setupDynamicContent() { + // Service status checking + const statusIndicators = document.querySelectorAll('.service-status'); + statusIndicators.forEach(indicator => { + checkServiceStatus(indicator); + }); + + // Auto-refresh service statuses every 30 seconds + setInterval(() => { + statusIndicators.forEach(indicator => { + checkServiceStatus(indicator); + }); + }, 30000); +} + +function checkServiceStatus(indicator) { + const serviceName = indicator.getAttribute('data-service'); + const url = getServiceHealthUrl(serviceName); + + if (!url) return; + + fetch(url) + .then(response => { + if (response.ok) { + indicator.className = 'service-status online'; + indicator.textContent = 'Online'; + } else { + indicator.className = 'service-status offline'; + indicator.textContent = 'Offline'; + } + }) + .catch(() => { + indicator.className = 'service-status offline'; + indicator.textContent = 'Offline'; + }); +} + +function getServiceHealthUrl(serviceName) { + const healthUrls = { + 'config': 'http://localhost:8888/actuator/health', + 'discovery': 'http://localhost:8761/actuator/health', + 'gateway': 'http://localhost:8222/actuator/health', + 'zipkin': 'http://localhost:9411/health' + }; + + return healthUrls[serviceName]; +} + +// Keyboard shortcuts +document.addEventListener('keydown', function(e) { + // Press 'h' to go home + if (e.key === 'h' && !e.ctrlKey && !e.metaKey) { + document.getElementById('home').scrollIntoView({ behavior: 'smooth' }); + } + + // Press 'a' to go to architecture + if (e.key === 'a' && !e.ctrlKey && !e.metaKey) { + document.getElementById('architecture').scrollIntoView({ behavior: 'smooth' }); + } + + // Press 's' to go to services + if (e.key === 's' && !e.ctrlKey && !e.metaKey) { + document.getElementById('services').scrollIntoView({ behavior: 'smooth' }); + } + + // Press 'p' to go to patterns + if (e.key === 'p' && !e.ctrlKey && !e.metaKey) { + document.getElementById('patterns').scrollIntoView({ behavior: 'smooth' }); + } +}); + +// Print functionality +function printLearningGuide() { + window.print(); +} + +// Export learning progress +function exportProgress() { + const progress = { + timestamp: new Date().toISOString(), + sectionsVisited: getSectionsVisited(), + currentSection: getCurrentSection(), + timeSpent: getTimeSpent() + }; + + const blob = new Blob([JSON.stringify(progress, null, 2)], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'microservices-learning-progress.json'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); +} + +function getSectionsVisited() { + return JSON.parse(localStorage.getItem('sectionsVisited') || '[]'); +} + +function getCurrentSection() { + return document.querySelector('section:target')?.id || 'home'; +} + +function getTimeSpent() { + return parseInt(localStorage.getItem('timeSpent') || '0'); +} + +// Initialize time tracking +let startTime = Date.now(); +setInterval(() => { + const timeSpent = parseInt(localStorage.getItem('timeSpent') || '0'); + localStorage.setItem('timeSpent', (timeSpent + 60).toString()); +}, 60000); + +// Add utility functions for external use +window.MicroservicesLearningPlatform = { + showServiceDetails, + exportProgress, + printLearningGuide, + getCurrentSection, + getSectionsVisited +}; + +console.log('πŸŽ“ Welcome to the Microservices Learning Platform!'); +console.log('Press "h" for home, "a" for architecture, "s" for services, "p" for patterns'); +console.log('Click on any service box to see detailed information'); + +// Kubernetes Deployment Interactive Features +function setupDeploymentInteractions() { + // Setup blue-green deployment visualization + setupBlueGreenVisualization(); + + // Setup Helm command examples + setupHelmCommands(); + + // Setup deployment step animations + setupDeploymentAnimations(); + + // Setup circuit breaker demonstration + setupCircuitBreakerDemo(); + + console.log('🚒 Deployment interactions initialized'); +} + +// Blue-Green Deployment Visualization +function setupBlueGreenVisualization() { + const deploymentSteps = document.querySelectorAll('.deployment-step'); + + deploymentSteps.forEach((step, index) => { + step.addEventListener('click', function() { + // Reset all steps + deploymentSteps.forEach(s => s.classList.remove('active', 'completed')); + + // Mark current and previous steps as completed + for (let i = 0; i <= index; i++) { + deploymentSteps[i].classList.add('completed'); + } + + // Mark current step as active + this.classList.add('active'); + + // Show deployment status + showDeploymentStatus(index); + }); + }); +} + +function showDeploymentStatus(step) { + const statusMessages = [ + 'πŸ”„ Preparing green environment... Building and deploying new version.', + 'βœ… Running health checks... Validating green environment readiness.', + 'πŸ”€ Switching traffic... Routing production traffic to green environment.', + 'πŸ”™ Rollback ready... Blue environment available for instant rollback.' + ]; + + // Create or update status display + let statusDiv = document.querySelector('.deployment-status'); + if (!statusDiv) { + statusDiv = document.createElement('div'); + statusDiv.className = 'deployment-status alert alert-info mt-3'; + document.querySelector('.blue-green-section').appendChild(statusDiv); + } + + statusDiv.innerHTML = ` +
+ + Step ${step + 1}: ${statusMessages[step]} +
+ `; + + // Remove spinner after 2 seconds + setTimeout(() => { + statusDiv.querySelector('.spinner-border')?.remove(); + }, 2000); +} + +// Helm Command Examples +function setupHelmCommands() { + const helmSection = document.querySelector('.helm-section'); + if (!helmSection) return; + + // Create interactive Helm command builder + const commandBuilder = document.createElement('div'); + commandBuilder.className = 'helm-command-builder mt-3 p-3 bg-light border rounded'; + commandBuilder.innerHTML = ` +
Interactive Helm Command Builder
+
+
+ + +
+
+ + +
+
+
+ helm install ecommerce-platform ./k8s/helm-charts --namespace microservices --create-namespace --set global.environment=production +
+ + `; + + helmSection.appendChild(commandBuilder); + + // Update command on change + const inputs = commandBuilder.querySelectorAll('select'); + inputs.forEach(input => { + input.addEventListener('change', updateHelmCommand); + }); +} + +function updateHelmCommand() { + const namespace = document.getElementById('namespace').value; + const environment = document.getElementById('environment').value; + const command = `helm install ecommerce-platform ./k8s/helm-charts --namespace ${namespace} --create-namespace --set global.environment=${environment}`; + document.getElementById('helmCommand').textContent = command; +} + +function copyHelmCommand() { + const command = document.getElementById('helmCommand').textContent; + copyToClipboard(command); + + // Show feedback + const btn = event.target.closest('button'); + const originalText = btn.innerHTML; + btn.innerHTML = ' Copied!'; + btn.classList.add('btn-success'); + btn.classList.remove('btn-primary'); + + setTimeout(() => { + btn.innerHTML = originalText; + btn.classList.remove('btn-success'); + btn.classList.add('btn-primary'); + }, 2000); +} + +// Deployment Step Animations +function setupDeploymentAnimations() { + // Intersection Observer for step animations + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.style.animationDelay = entry.target.dataset.delay + 'ms'; + entry.target.classList.add('animate-in'); + } + }); + }); + + document.querySelectorAll('.deployment-step').forEach((step, index) => { + step.dataset.delay = index * 200; + observer.observe(step); + }); +} + +// Circuit Breaker Demonstration +function setupCircuitBreakerDemo() { + const circuitBreakerCard = document.querySelector('.pattern-card:has(.fa-plug-circle-xmark)'); + if (!circuitBreakerCard) return; + + // Add interactive demo button + const demoSection = document.createElement('div'); + demoSection.className = 'circuit-breaker-demo mt-3'; + demoSection.innerHTML = ` +
+ + + +
+
+ `; + + circuitBreakerCard.appendChild(demoSection); +} + +function simulateCircuitBreaker(type) { + const statusDiv = document.querySelector('.demo-status'); + const states = ['CLOSED', 'OPEN', 'HALF_OPEN']; + let currentState = 0; + + const typeNames = { + 'saga-orchestration': 'Saga Orchestration', + 'saga-compensation': 'Saga Compensation', + 'saga-recovery': 'Saga Recovery' + }; + + const interval = setInterval(() => { + const state = states[currentState]; + const color = state === 'CLOSED' ? 'success' : state === 'OPEN' ? 'danger' : 'warning'; + + statusDiv.innerHTML = ` +
+ ${typeNames[type]} Circuit Breaker: + ${state} + ${getCircuitBreakerMessage(state)} +
+ `; + + currentState = (currentState + 1) % states.length; + + if (currentState === 0) { + clearInterval(interval); + setTimeout(() => { + statusDiv.innerHTML = ''; + }, 3000); + } + }, 2000); +} + +function getCircuitBreakerMessage(state) { + switch (state) { + case 'CLOSED': return 'All requests passing through normally'; + case 'OPEN': return 'Failing fast, requests blocked'; + case 'HALF_OPEN': return 'Testing with limited requests'; + default: return ''; + } +} + +// Saga Pattern Visualization +function setupSagaVisualization() { + const sagaCard = document.querySelector('.pattern-card:has(.fa-project-diagram)'); + if (!sagaCard) return; + + const visualizer = document.createElement('div'); + visualizer.className = 'saga-visualizer mt-3'; + visualizer.innerHTML = ` +
+ +
+
+ `; + + sagaCard.appendChild(visualizer); +} + +function runSagaDemo() { + const stepsContainer = document.querySelector('.saga-steps'); + const steps = [ + { name: 'Customer Validation', status: 'running', duration: 1000 }, + { name: 'Inventory Reservation', status: 'pending', duration: 1500 }, + { name: 'Payment Processing', status: 'pending', duration: 2000 }, + { name: 'Order Completion', status: 'pending', duration: 1000 } + ]; + + let currentStep = 0; + + function updateDisplay() { + stepsContainer.innerHTML = steps.map((step, index) => { + let statusClass = 'secondary'; + let icon = 'fas fa-clock'; + + if (index < currentStep) { + statusClass = 'success'; + icon = 'fas fa-check'; + } else if (index === currentStep) { + statusClass = 'primary'; + icon = 'fas fa-spinner fa-spin'; + } + + return ` +
+ + + + ${step.name} +
+ `; + }).join(''); + } + + updateDisplay(); + + const stepInterval = setInterval(() => { + if (currentStep < steps.length) { + currentStep++; + updateDisplay(); + + if (currentStep >= steps.length) { + clearInterval(stepInterval); + setTimeout(() => { + stepsContainer.innerHTML = '
βœ… Saga completed successfully!
'; + setTimeout(() => stepsContainer.innerHTML = '', 3000); + }, 1000); + } + } + }, 2000); +} + +// Update the main initialization function +const originalInitializeApp = initializeApp; +initializeApp = function() { + originalInitializeApp(); + + // Initialize deployment features + setupDeploymentInteractions(); + setupSagaVisualization(); + + console.log('🎯 Enhanced with Kubernetes deployment features'); +}; + +// Add CSS animations for deployment steps +const deploymentCSS = ` +.deployment-step { + opacity: 0; + transform: translateY(20px); + transition: all 0.6s ease; +} + +.deployment-step.animate-in { + opacity: 1; + transform: translateY(0); +} + +.deployment-step.active { + border-top-width: 6px; + box-shadow: 0 15px 35px rgba(0,0,0,0.2); +} + +.deployment-step.completed { + background: linear-gradient(135deg, #f8f9fa, #e9ecef); +} + +.deployment-step.completed .step-number { + background: #28a745 !important; +} +`; + +// Inject CSS +const style = document.createElement('style'); +style.textContent = deploymentCSS; +document.head.appendChild(style); + +console.log('πŸš€ Kubernetes & Circuit Breaker features loaded!'); +console.log('Click deployment steps to see the blue-green process in action'); +console.log('Try the circuit breaker demos to see resilience patterns'); diff --git a/learning-platform/styles.css b/learning-platform/styles.css new file mode 100644 index 0000000..2df50ef --- /dev/null +++ b/learning-platform/styles.css @@ -0,0 +1,1172 @@ +/* Reset and Base Styles */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + line-height: 1.6; + color: #333; +} + +/* Hero Section */ +.hero-section { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + min-height: 100vh; + display: flex; + align-items: center; + padding-top: 80px; +} + +.hero-title { + font-size: 3.5rem; + font-weight: 700; + margin-bottom: 1rem; + text-shadow: 0 2px 4px rgba(0,0,0,0.3); +} + +.hero-subtitle { + font-size: 1.3rem; + margin-bottom: 2rem; + opacity: 0.9; +} + +.hero-stats { + display: flex; + justify-content: center; + gap: 3rem; + margin: 2rem 0; +} + +.stat { + text-align: center; +} + +.stat h3 { + font-size: 2.5rem; + font-weight: 700; + color: #ffd700; +} + +.stat p { + font-size: 1rem; + text-transform: uppercase; + letter-spacing: 1px; +} + +/* GitHub Integration Styles */ +.hero-buttons { + margin: 2rem 0; +} + +.github-info { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + border-radius: 15px; + padding: 1.5rem; + margin: 2rem auto; + max-width: 600px; +} + +.repo-stats { + text-align: center; +} + +.repo-link { + color: #ffd700; + text-decoration: none; + font-size: 1.1rem; + font-weight: 600; + display: inline-block; + margin-bottom: 0.5rem; + transition: all 0.3s ease; +} + +.repo-link:hover { + color: #ffffff; + transform: scale(1.05); +} + +.repo-link i { + margin-right: 0.5rem; + font-size: 1.3rem; +} + +.author-info { + margin-top: 1rem; + font-size: 0.95rem; +} + +.author-link { + color: #e2e8f0; + text-decoration: none; + margin-left: 1rem; + transition: color 0.3s ease; +} + +.author-link:hover { + color: #ffd700; +} + +.author-link i { + margin-right: 0.25rem; +} + +/* GitHub Clone Section */ +.github-clone-section { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + border-radius: 15px; + padding: 2rem; + color: white; + border: none !important; +} + +.github-clone-section .alert-info { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); + color: white; +} + +.github-clone-section h5 { + color: #ffd700; + margin-bottom: 1rem; +} + +.github-clone-section .code-block { + background: rgba(0, 0, 0, 0.3); + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.github-clone-section .text-primary { + color: #ffd700 !important; +} + +/* Sections */ +.section { + padding: 80px 0; +} + +.section-title { + text-align: center; + font-size: 2.5rem; + font-weight: 700; + margin-bottom: 3rem; + color: #2c3e50; +} + +.section-title::after { + content: ''; + display: block; + width: 80px; + height: 4px; + background: linear-gradient(90deg, #667eea, #764ba2); + margin: 1rem auto; +} + +/* Business Problem & Solution */ +.business-problem, .business-solution { + background: #f8f9fa; + padding: 2rem; + border-radius: 10px; + box-shadow: 0 5px 15px rgba(0,0,0,0.1); + height: 100%; +} + +.business-problem { + border-left: 5px solid #e74c3c; +} + +.business-solution { + border-left: 5px solid #27ae60; +} + +.problem-list, .solution-list { + list-style: none; + padding: 0; +} + +.problem-list li, .solution-list li { + padding: 0.5rem 0; + position: relative; + padding-left: 1.5rem; +} + +.problem-list li::before { + content: '⚠️'; + position: absolute; + left: 0; +} + +.solution-list li::before { + content: 'βœ…'; + position: absolute; + left: 0; +} + +/* Architecture Diagram */ +.architecture-diagram { + background: #f8f9fa; + padding: 2rem; + border-radius: 15px; + margin: 2rem 0; +} + +.service-layer { + margin: 2rem 0; + text-align: center; +} + +.service-layer h4 { + margin-bottom: 1rem; + color: #2c3e50; + font-weight: 600; +} + +.service-box { + display: inline-block; + background: white; + padding: 1.5rem; + margin: 0.5rem; + border-radius: 10px; + box-shadow: 0 5px 15px rgba(0,0,0,0.1); + cursor: pointer; + transition: all 0.3s ease; + min-width: 150px; + position: relative; +} + +.service-box:hover { + transform: translateY(-5px); + box-shadow: 0 10px 25px rgba(0,0,0,0.15); +} + +.service-box i { + font-size: 2rem; + display: block; + margin-bottom: 0.5rem; +} + +.service-box span { + display: block; + font-weight: 600; + margin-bottom: 0.5rem; +} + +.service-box small { + color: #666; + font-size: 0.85rem; +} + +/* Service Box Colors */ +.client-box { border-top: 4px solid #3498db; } +.client-box i { color: #3498db; } + +.gateway-box { border-top: 4px solid #27ae60; } +.gateway-box i { color: #27ae60; } + +.config-box { border-top: 4px solid #f39c12; } +.config-box i { color: #f39c12; } + +.discovery-box { border-top: 4px solid #17a2b8; } +.discovery-box i { color: #17a2b8; } + +.business-box { border-top: 4px solid #6f42c1; } +.business-box i { color: #6f42c1; } + +.infra-box { border-top: 4px solid #dc3545; } +.infra-box i { color: #dc3545; } + +.infrastructure-grid { + display: flex; + justify-content: center; + flex-wrap: wrap; + gap: 1rem; +} + +.infra-box { + background: white; + padding: 1rem; + border-radius: 8px; + text-align: center; + min-width: 120px; + box-shadow: 0 3px 10px rgba(0,0,0,0.1); +} + +/* Service Detail Tabs */ +.nav-pills .nav-link { + color: #6c757d; + background: transparent; + border: 2px solid #dee2e6; + margin: 0 0.25rem; + border-radius: 25px; + padding: 0.75rem 1.5rem; + transition: all 0.3s ease; +} + +.nav-pills .nav-link:hover { + background: #f8f9fa; + color: #495057; +} + +.nav-pills .nav-link.active { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + border-color: transparent; +} + +/* Service Detail Cards */ +.service-detail { + background: white; + padding: 2rem; + border-radius: 15px; + box-shadow: 0 5px 20px rgba(0,0,0,0.1); + margin-top: 2rem; +} + +.service-info { + background: #f8f9fa; + padding: 1.5rem; + border-radius: 10px; + border-left: 5px solid #667eea; +} + +.service-info h4 { + margin-bottom: 1rem; +} + +.service-info p { + margin-bottom: 0.5rem; + font-weight: 500; +} + +.service-description h5 { + color: #2c3e50; + margin-bottom: 1rem; + font-weight: 600; +} + +.service-description h6 { + color: #495057; + margin: 1.5rem 0 0.5rem 0; + font-weight: 600; +} + +/* API Endpoints */ +.api-endpoints { + background: #f8f9fa; + padding: 1rem; + border-radius: 8px; + margin: 1rem 0; +} + +.endpoint { + display: flex; + align-items: center; + margin: 0.5rem 0; + padding: 0.5rem; + background: white; + border-radius: 5px; +} + +.method { + padding: 0.25rem 0.75rem; + border-radius: 4px; + color: white; + font-weight: 600; + font-size: 0.85rem; + margin-right: 1rem; + min-width: 60px; + text-align: center; +} + +.method.get { background: #28a745; } +.method.post { background: #007bff; } +.method.put { background: #ffc107; color: #333; } +.method.delete { background: #dc3545; } + +.code-example { + background: #f8f9fa; + border-radius: 10px; + padding: 1.5rem; + border-left: 4px solid #007bff; +} + +.code-example h6 { + color: #495057; + margin-bottom: 1rem; + font-weight: 600; +} + +.code-block { + background: #2d3748; + border-radius: 8px; + padding: 1.5rem; + margin: 1rem 0; +} + +.code-block code { + color: #e2e8f0; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + line-height: 1.8; + font-size: 0.9rem; +} + +/* Design Patterns */ +.pattern-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + gap: 2rem; + margin: 2rem 0; +} + +.pattern-card { + background: white; + padding: 2rem; + border-radius: 15px; + box-shadow: 0 5px 20px rgba(0,0,0,0.1); + transition: transform 0.3s ease; + border-top: 5px solid #667eea; +} + +.pattern-card:hover { + transform: translateY(-5px); +} + +.pattern-icon { + text-align: center; + margin-bottom: 1rem; +} + +.pattern-icon i { + font-size: 3rem; + color: #667eea; +} + +.pattern-card h4 { + text-align: center; + color: #2c3e50; + margin-bottom: 1rem; +} + +.pattern-benefits h6 { + color: #495057; + margin: 1rem 0 0.5rem 0; +} + +.pattern-benefits ul { + list-style-type: none; + padding: 0; +} + +.pattern-benefits li { + padding: 0.25rem 0; + position: relative; + padding-left: 1.5rem; +} + +.pattern-benefits li::before { + content: 'β–Έ'; + position: absolute; + left: 0; + color: #667eea; + font-weight: bold; +} + +/* Hands-On Section */ +.setup-steps { + margin: 2rem 0; +} + +.step { + display: flex; + align-items: flex-start; + margin: 2rem 0; + background: white; + padding: 1.5rem; + border-radius: 10px; + box-shadow: 0 3px 15px rgba(0,0,0,0.1); +} + +.step-number { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + width: 40px; + height: 40px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-weight: bold; + margin-right: 1.5rem; + flex-shrink: 0; +} + +.step-content { + flex: 1; +} + +.step-content h5 { + color: #2c3e50; + margin-bottom: 1rem; +} + +.code-block { + background: #2d3748; + border-radius: 8px; + padding: 1.5rem; + margin: 1rem 0; +} + +.service-startup { + margin: 1rem 0; +} + +.startup-order { + display: flex; + align-items: center; + margin: 0.5rem 0; + padding: 0.5rem; + background: #f8f9fa; + border-radius: 5px; +} + +.order-number { + background: #667eea; + color: white; + width: 25px; + height: 25px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.85rem; + font-weight: bold; + margin-right: 1rem; +} + +/* Testing Scenarios */ +.scenario-content { + padding: 1.5rem; +} + +.curl-example { + margin: 1rem 0; +} + +.curl-example pre { + background: #2d3748; + color: #e2e8f0; + padding: 1rem; + border-radius: 8px; + font-size: 0.9rem; + overflow-x: auto; +} + +.expected-result { + background: #e8f5e8; + padding: 1rem; + border-radius: 8px; + border-left: 5px solid #28a745; + margin: 1rem 0; +} + +.trace-flow { + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + margin: 1rem 0; +} + +.trace-step { + background: #667eea; + color: white; + padding: 0.5rem 1rem; + border-radius: 25px; + margin: 0.25rem; + font-size: 0.9rem; +} + +.trace-arrow { + color: #667eea; + font-size: 1.5rem; + margin: 0 0.5rem; +} + +/* Monitoring Dashboards */ +.dashboard-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 1.5rem; + margin: 2rem 0; +} + +.dashboard-card { + background: white; + padding: 1.5rem; + border-radius: 10px; + box-shadow: 0 5px 15px rgba(0,0,0,0.1); + text-align: center; + border-top: 4px solid #667eea; +} + +.dashboard-card h5 { + color: #2c3e50; + margin-bottom: 1rem; +} + +.dashboard-card p { + color: #666; + margin-bottom: 1.5rem; +} + +/* Footer */ +.footer { + background: #2c3e50 !important; +} + +.tech-stack { + margin-top: 0.5rem; +} + +.tech-stack .badge { + margin: 0.25rem; + padding: 0.5rem 0.75rem; + font-size: 0.85rem; +} + +/* Kubernetes Deployment Section Styles */ +.deployment-feature { + background: white; + border-radius: 15px; + padding: 2rem; + box-shadow: 0 8px 25px rgba(0,0,0,0.1); + transition: all 0.3s ease; + height: 100%; + border-left: 4px solid #007bff; +} + +.deployment-feature:hover { + transform: translateY(-5px); + box-shadow: 0 15px 35px rgba(0,0,0,0.15); +} + +.feature-icon { + width: 60px; + height: 60px; + background: linear-gradient(135deg, #007bff, #0056b3); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 1.5rem; +} + +.feature-icon i { + font-size: 1.5rem; + color: white; +} + +.feature-list { + list-style: none; + padding: 0; +} + +.feature-list li { + padding: 0.5rem 0; + padding-left: 1.5rem; + position: relative; +} + +.feature-list li::before { + content: 'βœ“'; + position: absolute; + left: 0; + color: #28a745; + font-weight: bold; +} + +/* Helm Section Styles */ +.helm-section { + background: #f8f9fa; + border-radius: 15px; + padding: 2rem; +} + +.helm-features h6 { + color: #495057; + margin-bottom: 1rem; + font-weight: 600; +} + +.helm-features ul { + list-style-type: none; + padding: 0; +} + +.helm-features li { + padding: 0.5rem 0; + padding-left: 1.5rem; + position: relative; + color: #6c757d; +} + +.helm-features li::before { + content: '⚑'; + position: absolute; + left: 0; + color: #007bff; +} + +/* Blue-Green Deployment Flow */ +.deployment-flow { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 1.5rem; + margin: 2rem 0; +} + +.deployment-step { + background: white; + border-radius: 15px; + padding: 1.5rem; + text-align: center; + box-shadow: 0 5px 15px rgba(0,0,0,0.1); + transition: all 0.3s ease; + border-top: 4px solid transparent; +} + +.deployment-step:nth-child(1) { border-top-color: #007bff; } +.deployment-step:nth-child(2) { border-top-color: #28a745; } +.deployment-step:nth-child(3) { border-top-color: #ffc107; } +.deployment-step:nth-child(4) { border-top-color: #dc3545; } + +.deployment-step:hover { + transform: translateY(-5px); + box-shadow: 0 10px 25px rgba(0,0,0,0.15); +} + +.step-number { + width: 40px; + height: 40px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1rem auto; + color: white; + font-weight: bold; + font-size: 1.2rem; +} + +.code-snippet { + background: #f8f9fa; + border-radius: 8px; + padding: 0.75rem; + margin-top: 1rem; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; +} + +.code-snippet code { + color: #e83e8c; + font-size: 0.9rem; +} + +/* Kubernetes Features Grid */ +.k8s-features { + margin: 2rem 0; +} + +.feature-card { + background: white; + border-radius: 15px; + padding: 2rem; + height: 100%; + box-shadow: 0 5px 15px rgba(0,0,0,0.1); + transition: all 0.3s ease; + border-bottom: 4px solid #007bff; +} + +.feature-card:hover { + transform: translateY(-3px); + box-shadow: 0 10px 25px rgba(0,0,0,0.15); +} + +.feature-card h5 { + color: #495057; + margin-bottom: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.feature-card h5 i { + color: #007bff; +} + +.feature-card ul { + list-style: none; + padding: 0; +} + +.feature-card li { + padding: 0.5rem 0; + padding-left: 1.5rem; + position: relative; + color: #6c757d; +} + +.feature-card li::before { + content: 'β–Ά'; + position: absolute; + left: 0; + color: #007bff; + font-size: 0.8rem; +} + +/* Quick Start Kubernetes Section */ +.quick-start-k8s { + background: linear-gradient(135deg, #28a745, #20c997); + border-radius: 15px; + padding: 2rem; + color: white; + margin: 2rem 0; +} + +.quick-start-k8s h3 { + color: white; + margin-bottom: 1.5rem; +} + +.quick-start-k8s .alert-info { + background: rgba(255, 255, 255, 0.15); + border: 1px solid rgba(255, 255, 255, 0.3); + color: white; +} + +.quick-start-k8s .code-block { + background: rgba(0, 0, 0, 0.3); + border-radius: 10px; + padding: 1.5rem; + margin-top: 1rem; +} + +.quick-start-k8s .code-block code { + color: #fff; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + line-height: 1.8; +} + +/* Enhanced Pattern Cards for Circuit Breakers and Saga */ +.pattern-card .implementation-details { + margin-top: 1.5rem; + padding-top: 1rem; + border-top: 1px solid #dee2e6; +} + +.pattern-card .implementation-details h6 { + color: #495057; + margin-bottom: 0.75rem; + font-weight: 600; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.pattern-card .implementation-details h6::before { + content: 'βš™οΈ'; + font-size: 1rem; +} + +.pattern-card .implementation-details ul { + list-style: none; + padding: 0; +} + +.pattern-card .implementation-details li { + padding: 0.25rem 0; + padding-left: 1.5rem; + position: relative; + color: #6c757d; + font-size: 0.9rem; +} + +.pattern-card .implementation-details li::before { + content: 'πŸ”§'; + position: absolute; + left: 0; + font-size: 0.8rem; +} + +.pattern-card .implementation-details ol { + padding-left: 1rem; +} + +.pattern-card .implementation-details ol li { + padding-left: 0.5rem; + list-style: decimal; +} + +.pattern-card .implementation-details ol li::before { + content: ''; +} + +/* Circuit Breaker Specific Styling */ +.pattern-card:has(.fa-plug-circle-xmark) { + border-left: 4px solid #dc3545; +} + +.pattern-card:has(.fa-plug-circle-xmark) .pattern-icon { + background: linear-gradient(135deg, #dc3545, #c82333); +} + +/* Saga Pattern Specific Styling */ +.pattern-card:has(.fa-project-diagram) { + border-left: 4px solid #6f42c1; +} + +.pattern-card:has(.fa-project-diagram) .pattern-icon { + background: linear-gradient(135deg, #6f42c1, #5a32a3); +} + +/* Responsive Design for Deployment Section */ +@media (max-width: 768px) { + .deployment-flow { + grid-template-columns: 1fr; + } + + .hero-stats { + grid-template-columns: repeat(2, 1fr); + gap: 1rem; + } + + .hero-stats .stat { + margin-bottom: 1rem; + } + + .feature-card { + margin-bottom: 1rem; + } +} + +/* Animation for Circuit Breaker Icons */ +.pattern-card .fa-plug-circle-xmark { + animation: pulse 2s infinite; +} + +@keyframes pulse { + 0% { transform: scale(1); } + 50% { transform: scale(1.1); } + 100% { transform: scale(1); } +} + +/* Animation for Saga Flow Icons */ +.pattern-card .fa-project-diagram { + animation: rotate 3s linear infinite; +} + +@keyframes rotate { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Enhanced Code Blocks */ +.code-example { + background: #f8f9fa; + border-radius: 10px; + padding: 1.5rem; + border-left: 4px solid #007bff; +} + +.code-example h6 { + color: #495057; + margin-bottom: 1rem; + font-weight: 600; +} + +.code-block { + background: #2d3748; + border-radius: 8px; + padding: 1.5rem; + margin: 1rem 0; +} + +.code-block code { + color: #e2e8f0; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + line-height: 1.8; + font-size: 0.9rem; +} + +/* Animations */ +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.service-box, .pattern-card, .step, .dashboard-card { + animation: fadeInUp 0.6s ease forwards; +} + +/* Scrollbar Styling */ +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + background: #f1f1f1; +} + +::-webkit-scrollbar-thumb { + background: #667eea; + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: #5a67d8; +} + +/* Loading Animation */ +.loading { + display: inline-block; + width: 20px; + height: 20px; + border: 3px solid #f3f3f3; + border-top: 3px solid #667eea; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* GitHub Repository Section */ +.bg-primary { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; +} + +.github-card { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + border-radius: 20px; + padding: 2rem; + border: 1px solid rgba(255, 255, 255, 0.2); + transition: transform 0.3s ease; +} + +.github-card:hover { + transform: translateY(-5px); +} + +.github-card-header { + text-align: center; + margin-bottom: 1.5rem; +} + +.github-icon { + font-size: 4rem; + margin-bottom: 1rem; + color: #ffd700; +} + +.github-card-header h4 { + color: white; + margin: 0; +} + +.github-card-body { + text-align: center; + margin-bottom: 1.5rem; +} + +.github-card-body h5 { + color: white; + font-weight: 600; + margin-bottom: 0.5rem; +} + +.github-card-body p { + color: rgba(255, 255, 255, 0.8); + margin-bottom: 1rem; +} + +.github-stats { + display: flex; + justify-content: center; + flex-wrap: wrap; + gap: 0.5rem; +} + +.github-card-footer .btn-light { + background: rgba(255, 255, 255, 0.9); + border: none; + font-weight: 600; + transition: all 0.3s ease; +} + +.github-card-footer .btn-light:hover { + background: white; + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(0,0,0,0.2); +} + +.repo-features { + background: rgba(255, 255, 255, 0.1); + border-radius: 15px; + padding: 2rem; + margin: 2rem 0; +} + +.repo-features ul li { + padding: 0.5rem 0; + font-size: 1rem; +} + +.quick-actions { + text-align: center; + background: rgba(255, 255, 255, 0.1); + border-radius: 15px; + padding: 2rem; +} + +.action-buttons { + display: flex; + justify-content: center; + flex-wrap: wrap; + gap: 1rem; +} + +.action-buttons .btn { + padding: 0.75rem 1.5rem; + font-weight: 600; + border-radius: 25px; + transition: all 0.3s ease; +} + +.action-buttons .btn:hover { + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(0,0,0,0.2); +} + +.action-buttons .btn-warning { + background: #ffd700; + border-color: #ffd700; + color: #333; +} + +.action-buttons .btn-warning:hover { + background: #ffed4e; + border-color: #ffed4e; +} diff --git a/services/config-server/pom.xml b/services/config-server/pom.xml index 041052f..3a48768 100644 --- a/services/config-server/pom.xml +++ b/services/config-server/pom.xml @@ -8,7 +8,7 @@ 3.2.5 - com.alibou + com.pramithamj config-server 0.0.1-SNAPSHOT config-server diff --git a/services/config-server/src/main/java/com/alibou/configserver/ConfigServerApplication.java b/services/config-server/src/main/java/com/pramithamj/configserver/ConfigServerApplication.java similarity index 91% rename from services/config-server/src/main/java/com/alibou/configserver/ConfigServerApplication.java rename to services/config-server/src/main/java/com/pramithamj/configserver/ConfigServerApplication.java index 11fb339..a69e4f4 100644 --- a/services/config-server/src/main/java/com/alibou/configserver/ConfigServerApplication.java +++ b/services/config-server/src/main/java/com/pramithamj/configserver/ConfigServerApplication.java @@ -1,4 +1,4 @@ -package com.alibou.configserver; +package com.pramithamj.configserver; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/services/config-server/src/main/resources/configurations/notification-service.yml b/services/config-server/src/main/resources/configurations/notification-service.yml index 046fdd3..ac7ef97 100644 --- a/services/config-server/src/main/resources/configurations/notification-service.yml +++ b/services/config-server/src/main/resources/configurations/notification-service.yml @@ -18,7 +18,7 @@ spring: value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer properties: spring.json.trusted.packages: '*' - spring.json.type.mapping: orderConfirmation:com.alibou.ecommerce.kafka.order.OrderConfirmation,paymentConfirmation:com.alibou.ecommerce.kafka.payment.PaymentConfirmation + spring.json.type.mapping: orderConfirmation:com.pramithamj.ecommerce.kafka.order.OrderConfirmation,paymentConfirmation:com.pramithamj.ecommerce.kafka.payment.PaymentConfirmation mail: host: localhost port: 1025 diff --git a/services/config-server/src/main/resources/configurations/order-service.yml b/services/config-server/src/main/resources/configurations/order-service.yml index 4f46cc7..151035a 100644 --- a/services/config-server/src/main/resources/configurations/order-service.yml +++ b/services/config-server/src/main/resources/configurations/order-service.yml @@ -17,7 +17,7 @@ spring: key-serializer: org.apache.kafka.common.serialization.StringSerializer value-serializer: org.springframework.kafka.support.serializer.JsonSerializer properties: - spring.json.type.mapping: orderConfirmation:com.alibou.ecommerce.kafka.OrderConfirmation + spring.json.type.mapping: orderConfirmation:com.pramithamj.ecommerce.kafka.OrderConfirmation application: config: diff --git a/services/config-server/src/main/resources/configurations/payment-service.yml b/services/config-server/src/main/resources/configurations/payment-service.yml index 72fa982..4e70ac8 100644 --- a/services/config-server/src/main/resources/configurations/payment-service.yml +++ b/services/config-server/src/main/resources/configurations/payment-service.yml @@ -18,7 +18,7 @@ spring: key-serializer: org.apache.kafka.common.serialization.StringSerializer value-serializer: org.springframework.kafka.support.serializer.JsonSerializer properties: - spring.json.type.mapping: paymentConfirmation:com.alibou.ecommerce.notification.PaymentNotificationRequest + spring.json.type.mapping: paymentConfirmation:com.pramithamj.ecommerce.notification.PaymentNotificationRequest application: config: product-url: http://localhost:8222/api/v1/products diff --git a/services/config-server/src/test/java/com/alibou/configserver/ConfigServerApplicationTests.java b/services/config-server/src/test/java/com/pramithamj/configserver/ConfigServerApplicationTests.java similarity index 83% rename from services/config-server/src/test/java/com/alibou/configserver/ConfigServerApplicationTests.java rename to services/config-server/src/test/java/com/pramithamj/configserver/ConfigServerApplicationTests.java index fa84786..85b3efb 100644 --- a/services/config-server/src/test/java/com/alibou/configserver/ConfigServerApplicationTests.java +++ b/services/config-server/src/test/java/com/pramithamj/configserver/ConfigServerApplicationTests.java @@ -1,4 +1,4 @@ -package com.alibou.configserver; +package com.pramithamj.configserver; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; diff --git a/services/customer/pom.xml b/services/customer/pom.xml index 9b8657a..e990cfd 100644 --- a/services/customer/pom.xml +++ b/services/customer/pom.xml @@ -8,7 +8,7 @@ 3.2.5 - com.alibou + com.pramithamj customer 0.0.1-SNAPSHOT customer diff --git a/services/customer/src/main/java/com/alibou/ecommerce/CustomerApplication.java b/services/customer/src/main/java/com/pramithamj/ecommerce/CustomerApplication.java similarity index 89% rename from services/customer/src/main/java/com/alibou/ecommerce/CustomerApplication.java rename to services/customer/src/main/java/com/pramithamj/ecommerce/CustomerApplication.java index 937ecc4..9248cfe 100644 --- a/services/customer/src/main/java/com/alibou/ecommerce/CustomerApplication.java +++ b/services/customer/src/main/java/com/pramithamj/ecommerce/CustomerApplication.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce; +package com.pramithamj.ecommerce; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/services/customer/src/main/java/com/alibou/ecommerce/customer/Address.java b/services/customer/src/main/java/com/pramithamj/ecommerce/customer/Address.java similarity index 90% rename from services/customer/src/main/java/com/alibou/ecommerce/customer/Address.java rename to services/customer/src/main/java/com/pramithamj/ecommerce/customer/Address.java index 1da667b..cf27036 100644 --- a/services/customer/src/main/java/com/alibou/ecommerce/customer/Address.java +++ b/services/customer/src/main/java/com/pramithamj/ecommerce/customer/Address.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.customer; +package com.pramithamj.ecommerce.customer; import lombok.AllArgsConstructor; diff --git a/services/customer/src/main/java/com/alibou/ecommerce/customer/Customer.java b/services/customer/src/main/java/com/pramithamj/ecommerce/customer/Customer.java similarity index 92% rename from services/customer/src/main/java/com/alibou/ecommerce/customer/Customer.java rename to services/customer/src/main/java/com/pramithamj/ecommerce/customer/Customer.java index 3f9db56..b3df6d0 100644 --- a/services/customer/src/main/java/com/alibou/ecommerce/customer/Customer.java +++ b/services/customer/src/main/java/com/pramithamj/ecommerce/customer/Customer.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.customer; +package com.pramithamj.ecommerce.customer; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerController.java b/services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerController.java similarity index 97% rename from services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerController.java rename to services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerController.java index 0f33712..05a6a6b 100644 --- a/services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerController.java +++ b/services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerController.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.customer; +package com.pramithamj.ecommerce.customer; import java.util.List; diff --git a/services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerMapper.java b/services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerMapper.java similarity index 94% rename from services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerMapper.java rename to services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerMapper.java index 1d16f6d..74c6bf7 100644 --- a/services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerMapper.java +++ b/services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerMapper.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.customer; +package com.pramithamj.ecommerce.customer; import org.springframework.stereotype.Component; diff --git a/services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerRepository.java b/services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerRepository.java similarity index 78% rename from services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerRepository.java rename to services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerRepository.java index 7ebc36e..e9e6c55 100644 --- a/services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerRepository.java +++ b/services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerRepository.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.customer; +package com.pramithamj.ecommerce.customer; import org.springframework.data.mongodb.repository.MongoRepository; diff --git a/services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerRequest.java b/services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerRequest.java similarity index 91% rename from services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerRequest.java rename to services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerRequest.java index 3965a36..9b8ba15 100644 --- a/services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerRequest.java +++ b/services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerRequest.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.customer; +package com.pramithamj.ecommerce.customer; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotNull; diff --git a/services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerResponse.java b/services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerResponse.java similarity index 75% rename from services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerResponse.java rename to services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerResponse.java index daba51f..2b28e3b 100644 --- a/services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerResponse.java +++ b/services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerResponse.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.customer; +package com.pramithamj.ecommerce.customer; public record CustomerResponse( String id, diff --git a/services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerService.java b/services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerService.java similarity index 94% rename from services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerService.java rename to services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerService.java index 04a0ce7..975ab4f 100644 --- a/services/customer/src/main/java/com/alibou/ecommerce/customer/CustomerService.java +++ b/services/customer/src/main/java/com/pramithamj/ecommerce/customer/CustomerService.java @@ -1,6 +1,6 @@ -package com.alibou.ecommerce.customer; +package com.pramithamj.ecommerce.customer; -import com.alibou.ecommerce.exception.CustomerNotFoundException; +import com.pramithamj.ecommerce.exception.CustomerNotFoundException; import lombok.RequiredArgsConstructor; import org.apache.commons.lang.StringUtils; import org.springframework.stereotype.Service; diff --git a/services/customer/src/main/java/com/alibou/ecommerce/exception/CustomerNotFoundException.java b/services/customer/src/main/java/com/pramithamj/ecommerce/exception/CustomerNotFoundException.java similarity index 81% rename from services/customer/src/main/java/com/alibou/ecommerce/exception/CustomerNotFoundException.java rename to services/customer/src/main/java/com/pramithamj/ecommerce/exception/CustomerNotFoundException.java index f840e33..8ddb4e1 100644 --- a/services/customer/src/main/java/com/alibou/ecommerce/exception/CustomerNotFoundException.java +++ b/services/customer/src/main/java/com/pramithamj/ecommerce/exception/CustomerNotFoundException.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.exception; +package com.pramithamj.ecommerce.exception; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/services/customer/src/main/java/com/alibou/ecommerce/handler/ErrorResponse.java b/services/customer/src/main/java/com/pramithamj/ecommerce/handler/ErrorResponse.java similarity index 72% rename from services/customer/src/main/java/com/alibou/ecommerce/handler/ErrorResponse.java rename to services/customer/src/main/java/com/pramithamj/ecommerce/handler/ErrorResponse.java index d15146e..fe4c7be 100644 --- a/services/customer/src/main/java/com/alibou/ecommerce/handler/ErrorResponse.java +++ b/services/customer/src/main/java/com/pramithamj/ecommerce/handler/ErrorResponse.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.handler; +package com.pramithamj.ecommerce.handler; import java.util.Map; import java.util.Set; diff --git a/services/customer/src/main/java/com/alibou/ecommerce/handler/GlobalExceptionHandler.java b/services/customer/src/main/java/com/pramithamj/ecommerce/handler/GlobalExceptionHandler.java similarity index 92% rename from services/customer/src/main/java/com/alibou/ecommerce/handler/GlobalExceptionHandler.java rename to services/customer/src/main/java/com/pramithamj/ecommerce/handler/GlobalExceptionHandler.java index 307c46e..bd2f061 100644 --- a/services/customer/src/main/java/com/alibou/ecommerce/handler/GlobalExceptionHandler.java +++ b/services/customer/src/main/java/com/pramithamj/ecommerce/handler/GlobalExceptionHandler.java @@ -1,6 +1,6 @@ -package com.alibou.ecommerce.handler; +package com.pramithamj.ecommerce.handler; -import com.alibou.ecommerce.exception.CustomerNotFoundException; +import com.pramithamj.ecommerce.exception.CustomerNotFoundException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.FieldError; diff --git a/services/customer/src/test/java/com/alibou/ecommerce/CustomerApplicationTests.java b/services/customer/src/test/java/com/pramithamj/ecommerce/CustomerApplicationTests.java similarity index 84% rename from services/customer/src/test/java/com/alibou/ecommerce/CustomerApplicationTests.java rename to services/customer/src/test/java/com/pramithamj/ecommerce/CustomerApplicationTests.java index 5a7fe9a..b4d7ff9 100644 --- a/services/customer/src/test/java/com/alibou/ecommerce/CustomerApplicationTests.java +++ b/services/customer/src/test/java/com/pramithamj/ecommerce/CustomerApplicationTests.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce; +package com.pramithamj.ecommerce; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; diff --git a/services/discovery/pom.xml b/services/discovery/pom.xml index f0e1a82..47620a2 100644 --- a/services/discovery/pom.xml +++ b/services/discovery/pom.xml @@ -8,7 +8,7 @@ 3.2.5 - com.alibou + com.pramithamj discovery 0.0.1-SNAPSHOT discovery diff --git a/services/discovery/src/main/java/com/alibou/discovery/DiscoveryApplication.java b/services/discovery/src/main/java/com/pramithamj/discovery/DiscoveryApplication.java similarity index 91% rename from services/discovery/src/main/java/com/alibou/discovery/DiscoveryApplication.java rename to services/discovery/src/main/java/com/pramithamj/discovery/DiscoveryApplication.java index a003fce..dd178be 100644 --- a/services/discovery/src/main/java/com/alibou/discovery/DiscoveryApplication.java +++ b/services/discovery/src/main/java/com/pramithamj/discovery/DiscoveryApplication.java @@ -1,4 +1,4 @@ -package com.alibou.discovery; +package com.pramithamj.discovery; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/services/discovery/src/test/java/com/alibou/discovery/DiscoveryApplicationTests.java b/services/discovery/src/test/java/com/pramithamj/discovery/DiscoveryApplicationTests.java similarity index 84% rename from services/discovery/src/test/java/com/alibou/discovery/DiscoveryApplicationTests.java rename to services/discovery/src/test/java/com/pramithamj/discovery/DiscoveryApplicationTests.java index 7db88e4..332fa80 100644 --- a/services/discovery/src/test/java/com/alibou/discovery/DiscoveryApplicationTests.java +++ b/services/discovery/src/test/java/com/pramithamj/discovery/DiscoveryApplicationTests.java @@ -1,4 +1,4 @@ -package com.alibou.discovery; +package com.pramithamj.discovery; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; diff --git a/services/gateway/pom.xml b/services/gateway/pom.xml index 4f92b62..c29b3d7 100644 --- a/services/gateway/pom.xml +++ b/services/gateway/pom.xml @@ -8,7 +8,7 @@ 3.2.5 - com.alibou + com.pramithamj gateway 0.0.1-SNAPSHOT gateway diff --git a/services/gateway/src/main/java/com/alibou/gateway/GatewayApplication.java b/services/gateway/src/main/java/com/pramithamj/gateway/GatewayApplication.java similarity index 89% rename from services/gateway/src/main/java/com/alibou/gateway/GatewayApplication.java rename to services/gateway/src/main/java/com/pramithamj/gateway/GatewayApplication.java index e0ad3bd..ff0c0b6 100644 --- a/services/gateway/src/main/java/com/alibou/gateway/GatewayApplication.java +++ b/services/gateway/src/main/java/com/pramithamj/gateway/GatewayApplication.java @@ -1,4 +1,4 @@ -package com.alibou.gateway; +package com.pramithamj.gateway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/services/gateway/src/test/java/com/alibou/gateway/GatewayApplicationTests.java b/services/gateway/src/test/java/com/pramithamj/gateway/GatewayApplicationTests.java similarity index 85% rename from services/gateway/src/test/java/com/alibou/gateway/GatewayApplicationTests.java rename to services/gateway/src/test/java/com/pramithamj/gateway/GatewayApplicationTests.java index 00d9745..eee21ef 100644 --- a/services/gateway/src/test/java/com/alibou/gateway/GatewayApplicationTests.java +++ b/services/gateway/src/test/java/com/pramithamj/gateway/GatewayApplicationTests.java @@ -1,4 +1,4 @@ -package com.alibou.gateway; +package com.pramithamj.gateway; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; diff --git a/services/notification/pom.xml b/services/notification/pom.xml index 4c10e92..93e0f0d 100644 --- a/services/notification/pom.xml +++ b/services/notification/pom.xml @@ -8,7 +8,7 @@ 3.2.5 - com.alibou + com.pramithamj notification 0.0.1-SNAPSHOT notification diff --git a/services/notification/src/main/java/com/alibou/ecommerce/NotificationApplication.java b/services/notification/src/main/java/com/pramithamj/ecommerce/NotificationApplication.java similarity index 89% rename from services/notification/src/main/java/com/alibou/ecommerce/NotificationApplication.java rename to services/notification/src/main/java/com/pramithamj/ecommerce/NotificationApplication.java index 4b4295b..8d3202b 100644 --- a/services/notification/src/main/java/com/alibou/ecommerce/NotificationApplication.java +++ b/services/notification/src/main/java/com/pramithamj/ecommerce/NotificationApplication.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce; +package com.pramithamj.ecommerce; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/services/notification/src/main/java/com/alibou/ecommerce/email/EmailService.java b/services/notification/src/main/java/com/pramithamj/ecommerce/email/EmailService.java similarity index 90% rename from services/notification/src/main/java/com/alibou/ecommerce/email/EmailService.java rename to services/notification/src/main/java/com/pramithamj/ecommerce/email/EmailService.java index d42efa2..9a1b246 100644 --- a/services/notification/src/main/java/com/alibou/ecommerce/email/EmailService.java +++ b/services/notification/src/main/java/com/pramithamj/ecommerce/email/EmailService.java @@ -1,6 +1,6 @@ -package com.alibou.ecommerce.email; +package com.pramithamj.ecommerce.email; -import com.alibou.ecommerce.kafka.order.Product; +import com.pramithamj.ecommerce.kafka.order.Product; import jakarta.mail.MessagingException; import jakarta.mail.internet.MimeMessage; import lombok.RequiredArgsConstructor; @@ -17,8 +17,8 @@ import java.util.List; import java.util.Map; -import static com.alibou.ecommerce.email.EmailTemplates.ORDER_CONFIRMATION; -import static com.alibou.ecommerce.email.EmailTemplates.PAYMENT_CONFIRMATION; +import static com.pramithamj.ecommerce.email.EmailTemplates.ORDER_CONFIRMATION; +import static com.pramithamj.ecommerce.email.EmailTemplates.PAYMENT_CONFIRMATION; import static java.nio.charset.StandardCharsets.UTF_8; @Service @@ -39,7 +39,7 @@ public void sendPaymentSuccessEmail( MimeMessage mimeMessage = mailSender.createMimeMessage(); MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED, UTF_8.name()); - messageHelper.setFrom("contact@aliboucoding.com"); + messageHelper.setFrom("contact@pramithamj.com"); final String templateName = PAYMENT_CONFIRMATION.getTemplate(); @@ -76,7 +76,7 @@ public void sendOrderConfirmationEmail( MimeMessage mimeMessage = mailSender.createMimeMessage(); MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED, UTF_8.name()); - messageHelper.setFrom("contact@aliboucoding.com"); + messageHelper.setFrom("contact@pramithamj.com"); final String templateName = ORDER_CONFIRMATION.getTemplate(); diff --git a/services/notification/src/main/java/com/alibou/ecommerce/email/EmailTemplates.java b/services/notification/src/main/java/com/pramithamj/ecommerce/email/EmailTemplates.java similarity index 91% rename from services/notification/src/main/java/com/alibou/ecommerce/email/EmailTemplates.java rename to services/notification/src/main/java/com/pramithamj/ecommerce/email/EmailTemplates.java index 0d74079..374dc7f 100644 --- a/services/notification/src/main/java/com/alibou/ecommerce/email/EmailTemplates.java +++ b/services/notification/src/main/java/com/pramithamj/ecommerce/email/EmailTemplates.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.email; +package com.pramithamj.ecommerce.email; import lombok.Getter; diff --git a/services/notification/src/main/java/com/alibou/ecommerce/kafka/NotificationsConsumer.java b/services/notification/src/main/java/com/pramithamj/ecommerce/kafka/NotificationsConsumer.java similarity index 81% rename from services/notification/src/main/java/com/alibou/ecommerce/kafka/NotificationsConsumer.java rename to services/notification/src/main/java/com/pramithamj/ecommerce/kafka/NotificationsConsumer.java index 0413045..5a8f8df 100644 --- a/services/notification/src/main/java/com/alibou/ecommerce/kafka/NotificationsConsumer.java +++ b/services/notification/src/main/java/com/pramithamj/ecommerce/kafka/NotificationsConsumer.java @@ -1,10 +1,10 @@ -package com.alibou.ecommerce.kafka; +package com.pramithamj.ecommerce.kafka; -import com.alibou.ecommerce.email.EmailService; -import com.alibou.ecommerce.kafka.order.OrderConfirmation; -import com.alibou.ecommerce.kafka.payment.PaymentConfirmation; -import com.alibou.ecommerce.notification.Notification; -import com.alibou.ecommerce.notification.NotificationRepository; +import com.pramithamj.ecommerce.email.EmailService; +import com.pramithamj.ecommerce.kafka.order.OrderConfirmation; +import com.pramithamj.ecommerce.kafka.payment.PaymentConfirmation; +import com.pramithamj.ecommerce.notification.Notification; +import com.pramithamj.ecommerce.notification.NotificationRepository; import jakarta.mail.MessagingException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -13,8 +13,8 @@ import java.time.LocalDateTime; -import static com.alibou.ecommerce.notification.NotificationType.ORDER_CONFIRMATION; -import static com.alibou.ecommerce.notification.NotificationType.PAYMENT_CONFIRMATION; +import static com.pramithamj.ecommerce.notification.NotificationType.ORDER_CONFIRMATION; +import static com.pramithamj.ecommerce.notification.NotificationType.PAYMENT_CONFIRMATION; import static java.lang.String.format; @Service diff --git a/services/notification/src/main/java/com/alibou/ecommerce/kafka/order/Customer.java b/services/notification/src/main/java/com/pramithamj/ecommerce/kafka/order/Customer.java similarity index 69% rename from services/notification/src/main/java/com/alibou/ecommerce/kafka/order/Customer.java rename to services/notification/src/main/java/com/pramithamj/ecommerce/kafka/order/Customer.java index 8bb1b8c..8963f9a 100644 --- a/services/notification/src/main/java/com/alibou/ecommerce/kafka/order/Customer.java +++ b/services/notification/src/main/java/com/pramithamj/ecommerce/kafka/order/Customer.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.kafka.order; +package com.pramithamj.ecommerce.kafka.order; public record Customer( String id, diff --git a/services/notification/src/main/java/com/alibou/ecommerce/kafka/order/OrderConfirmation.java b/services/notification/src/main/java/com/pramithamj/ecommerce/kafka/order/OrderConfirmation.java similarity index 70% rename from services/notification/src/main/java/com/alibou/ecommerce/kafka/order/OrderConfirmation.java rename to services/notification/src/main/java/com/pramithamj/ecommerce/kafka/order/OrderConfirmation.java index 523bf75..e342db7 100644 --- a/services/notification/src/main/java/com/alibou/ecommerce/kafka/order/OrderConfirmation.java +++ b/services/notification/src/main/java/com/pramithamj/ecommerce/kafka/order/OrderConfirmation.java @@ -1,6 +1,6 @@ -package com.alibou.ecommerce.kafka.order; +package com.pramithamj.ecommerce.kafka.order; -import com.alibou.ecommerce.kafka.payment.PaymentMethod; +import com.pramithamj.ecommerce.kafka.payment.PaymentMethod; import java.math.BigDecimal; import java.util.List; diff --git a/services/notification/src/main/java/com/alibou/ecommerce/kafka/order/Product.java b/services/notification/src/main/java/com/pramithamj/ecommerce/kafka/order/Product.java similarity index 80% rename from services/notification/src/main/java/com/alibou/ecommerce/kafka/order/Product.java rename to services/notification/src/main/java/com/pramithamj/ecommerce/kafka/order/Product.java index 7d7525b..b3ad7dd 100644 --- a/services/notification/src/main/java/com/alibou/ecommerce/kafka/order/Product.java +++ b/services/notification/src/main/java/com/pramithamj/ecommerce/kafka/order/Product.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.kafka.order; +package com.pramithamj.ecommerce.kafka.order; import java.math.BigDecimal; diff --git a/services/notification/src/main/java/com/alibou/ecommerce/kafka/payment/PaymentConfirmation.java b/services/notification/src/main/java/com/pramithamj/ecommerce/kafka/payment/PaymentConfirmation.java similarity index 84% rename from services/notification/src/main/java/com/alibou/ecommerce/kafka/payment/PaymentConfirmation.java rename to services/notification/src/main/java/com/pramithamj/ecommerce/kafka/payment/PaymentConfirmation.java index 1855d7b..81b87c2 100644 --- a/services/notification/src/main/java/com/alibou/ecommerce/kafka/payment/PaymentConfirmation.java +++ b/services/notification/src/main/java/com/pramithamj/ecommerce/kafka/payment/PaymentConfirmation.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.kafka.payment; +package com.pramithamj.ecommerce.kafka.payment; import java.math.BigDecimal; diff --git a/services/notification/src/main/java/com/alibou/ecommerce/kafka/payment/PaymentMethod.java b/services/notification/src/main/java/com/pramithamj/ecommerce/kafka/payment/PaymentMethod.java similarity index 65% rename from services/notification/src/main/java/com/alibou/ecommerce/kafka/payment/PaymentMethod.java rename to services/notification/src/main/java/com/pramithamj/ecommerce/kafka/payment/PaymentMethod.java index 4abffea..9a94f40 100644 --- a/services/notification/src/main/java/com/alibou/ecommerce/kafka/payment/PaymentMethod.java +++ b/services/notification/src/main/java/com/pramithamj/ecommerce/kafka/payment/PaymentMethod.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.kafka.payment; +package com.pramithamj.ecommerce.kafka.payment; public enum PaymentMethod { diff --git a/services/notification/src/main/java/com/alibou/ecommerce/notification/Notification.java b/services/notification/src/main/java/com/pramithamj/ecommerce/notification/Notification.java similarity index 77% rename from services/notification/src/main/java/com/alibou/ecommerce/notification/Notification.java rename to services/notification/src/main/java/com/pramithamj/ecommerce/notification/Notification.java index c1cbc09..13e24e0 100644 --- a/services/notification/src/main/java/com/alibou/ecommerce/notification/Notification.java +++ b/services/notification/src/main/java/com/pramithamj/ecommerce/notification/Notification.java @@ -1,7 +1,7 @@ -package com.alibou.ecommerce.notification; +package com.pramithamj.ecommerce.notification; -import com.alibou.ecommerce.kafka.order.OrderConfirmation; -import com.alibou.ecommerce.kafka.payment.PaymentConfirmation; +import com.pramithamj.ecommerce.kafka.order.OrderConfirmation; +import com.pramithamj.ecommerce.kafka.payment.PaymentConfirmation; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; diff --git a/services/notification/src/main/java/com/alibou/ecommerce/notification/NotificationRepository.java b/services/notification/src/main/java/com/pramithamj/ecommerce/notification/NotificationRepository.java similarity index 77% rename from services/notification/src/main/java/com/alibou/ecommerce/notification/NotificationRepository.java rename to services/notification/src/main/java/com/pramithamj/ecommerce/notification/NotificationRepository.java index c560f6a..45ad5c3 100644 --- a/services/notification/src/main/java/com/alibou/ecommerce/notification/NotificationRepository.java +++ b/services/notification/src/main/java/com/pramithamj/ecommerce/notification/NotificationRepository.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.notification; +package com.pramithamj.ecommerce.notification; import org.springframework.data.mongodb.repository.MongoRepository; diff --git a/services/notification/src/main/java/com/alibou/ecommerce/notification/NotificationType.java b/services/notification/src/main/java/com/pramithamj/ecommerce/notification/NotificationType.java similarity index 63% rename from services/notification/src/main/java/com/alibou/ecommerce/notification/NotificationType.java rename to services/notification/src/main/java/com/pramithamj/ecommerce/notification/NotificationType.java index 0b1d546..c009978 100644 --- a/services/notification/src/main/java/com/alibou/ecommerce/notification/NotificationType.java +++ b/services/notification/src/main/java/com/pramithamj/ecommerce/notification/NotificationType.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.notification; +package com.pramithamj.ecommerce.notification; public enum NotificationType { ORDER_CONFIRMATION, diff --git a/services/notification/src/main/resources/templates/order-confirmation.html b/services/notification/src/main/resources/templates/order-confirmation.html index 72f6e3b..f49ea0f 100644 --- a/services/notification/src/main/resources/templates/order-confirmation.html +++ b/services/notification/src/main/resources/templates/order-confirmation.html @@ -80,7 +80,7 @@

Order Details

diff --git a/services/notification/src/test/java/com/alibou/ecommerce/NotificationApplicationTests.java b/services/notification/src/test/java/com/pramithamj/ecommerce/NotificationApplicationTests.java similarity index 84% rename from services/notification/src/test/java/com/alibou/ecommerce/NotificationApplicationTests.java rename to services/notification/src/test/java/com/pramithamj/ecommerce/NotificationApplicationTests.java index e0cd1be..e9ae97c 100644 --- a/services/notification/src/test/java/com/alibou/ecommerce/NotificationApplicationTests.java +++ b/services/notification/src/test/java/com/pramithamj/ecommerce/NotificationApplicationTests.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce; +package com.pramithamj.ecommerce; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; diff --git a/services/order/pom.xml b/services/order/pom.xml index 8700840..39de938 100644 --- a/services/order/pom.xml +++ b/services/order/pom.xml @@ -8,7 +8,7 @@ 3.2.5 - com.alibou + com.pramithamj order 0.0.1-SNAPSHOT order @@ -46,6 +46,26 @@ org.springframework.cloud spring-cloud-starter-netflix-eureka-client + + + + org.springframework.cloud + spring-cloud-starter-circuitbreaker-resilience4j + + + io.github.resilience4j + resilience4j-spring-boot3 + + + io.github.resilience4j + resilience4j-micrometer + + + + + org.springframework.boot + spring-boot-starter-data-mongodb + org.springframework.boot diff --git a/services/order/src/main/java/com/alibou/ecommerce/OrderApplication.java b/services/order/src/main/java/com/pramithamj/ecommerce/OrderApplication.java similarity index 93% rename from services/order/src/main/java/com/alibou/ecommerce/OrderApplication.java rename to services/order/src/main/java/com/pramithamj/ecommerce/OrderApplication.java index b6891e1..547b2c2 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/OrderApplication.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/OrderApplication.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce; +package com.pramithamj.ecommerce; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/services/order/src/main/java/com/pramithamj/ecommerce/config/CircuitBreakerConfiguration.java b/services/order/src/main/java/com/pramithamj/ecommerce/config/CircuitBreakerConfiguration.java new file mode 100644 index 0000000..361af1e --- /dev/null +++ b/services/order/src/main/java/com/pramithamj/ecommerce/config/CircuitBreakerConfiguration.java @@ -0,0 +1,141 @@ +package com.pramithamj.ecommerce.config; + +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; +import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.Duration; + +@Configuration +public class CircuitBreakerConfiguration { + + @Bean + public CircuitBreakerRegistry circuitBreakerRegistry() { + return CircuitBreakerRegistry.ofDefaults(); + } + + @Bean + public CircuitBreaker customerServiceCircuitBreaker(CircuitBreakerRegistry registry) { + CircuitBreakerConfig config = CircuitBreakerConfig.custom() + .failureRateThreshold(50) + .waitDurationInOpenState(Duration.ofMillis(30000)) + .slidingWindowSize(10) + .minimumNumberOfCalls(5) + .slowCallRateThreshold(50) + .slowCallDurationThreshold(Duration.ofSeconds(2)) + .build(); + + return registry.circuitBreaker("customer-service", config); + } + + @Bean + public CircuitBreaker productServiceCircuitBreaker(CircuitBreakerRegistry registry) { + CircuitBreakerConfig config = CircuitBreakerConfig.custom() + .failureRateThreshold(50) + .waitDurationInOpenState(Duration.ofMillis(30000)) + .slidingWindowSize(10) + .minimumNumberOfCalls(5) + .slowCallRateThreshold(50) + .slowCallDurationThreshold(Duration.ofSeconds(2)) + .build(); + + return registry.circuitBreaker("product-service", config); + } + + @Bean + public CircuitBreaker paymentServiceCircuitBreaker(CircuitBreakerRegistry registry) { + CircuitBreakerConfig config = CircuitBreakerConfig.custom() + .failureRateThreshold(30) + .waitDurationInOpenState(Duration.ofMillis(60000)) + .slidingWindowSize(20) + .minimumNumberOfCalls(10) + .slowCallRateThreshold(40) + .slowCallDurationThreshold(Duration.ofSeconds(3)) + .build(); + + return registry.circuitBreaker("payment-service", config); + } + + // Saga-specific Circuit Breakers + @Bean + public CircuitBreaker sagaOrchestrationCircuitBreaker(CircuitBreakerRegistry registry) { + CircuitBreakerConfig config = CircuitBreakerConfig.custom() + .failureRateThreshold(40) + .waitDurationInOpenState(Duration.ofMillis(45000)) + .slidingWindowSize(15) + .minimumNumberOfCalls(8) + .slowCallRateThreshold(45) + .slowCallDurationThreshold(Duration.ofSeconds(5)) + .enableAutomaticTransitionFromOpenToHalfOpen() + .permittedNumberOfCallsInHalfOpenState(5) + .build(); + + return registry.circuitBreaker("saga-orchestration", config); + } + + @Bean + public CircuitBreaker sagaCompensationCircuitBreaker(CircuitBreakerRegistry registry) { + CircuitBreakerConfig config = CircuitBreakerConfig.custom() + .failureRateThreshold(25) + .waitDurationInOpenState(Duration.ofMillis(30000)) + .slidingWindowSize(12) + .minimumNumberOfCalls(6) + .slowCallRateThreshold(35) + .slowCallDurationThreshold(Duration.ofSeconds(4)) + .enableAutomaticTransitionFromOpenToHalfOpen() + .permittedNumberOfCallsInHalfOpenState(3) + .build(); + + return registry.circuitBreaker("saga-compensation", config); + } + + @Bean + public CircuitBreaker sagaRecoveryCircuitBreaker(CircuitBreakerRegistry registry) { + CircuitBreakerConfig config = CircuitBreakerConfig.custom() + .failureRateThreshold(60) + .waitDurationInOpenState(Duration.ofMillis(90000)) + .slidingWindowSize(8) + .minimumNumberOfCalls(4) + .slowCallRateThreshold(55) + .slowCallDurationThreshold(Duration.ofSeconds(6)) + .enableAutomaticTransitionFromOpenToHalfOpen() + .permittedNumberOfCallsInHalfOpenState(2) + .build(); + + return registry.circuitBreaker("saga-recovery", config); + } + + @Bean + public CircuitBreaker inventoryReservationCircuitBreaker(CircuitBreakerRegistry registry) { + CircuitBreakerConfig config = CircuitBreakerConfig.custom() + .failureRateThreshold(45) + .waitDurationInOpenState(Duration.ofMillis(35000)) + .slidingWindowSize(12) + .minimumNumberOfCalls(7) + .slowCallRateThreshold(40) + .slowCallDurationThreshold(Duration.ofSeconds(3)) + .enableAutomaticTransitionFromOpenToHalfOpen() + .permittedNumberOfCallsInHalfOpenState(4) + .build(); + + return registry.circuitBreaker("inventory-reservation", config); + } + + @Bean + public CircuitBreaker customerValidationCircuitBreaker(CircuitBreakerRegistry registry) { + CircuitBreakerConfig config = CircuitBreakerConfig.custom() + .failureRateThreshold(35) + .waitDurationInOpenState(Duration.ofMillis(25000)) + .slidingWindowSize(10) + .minimumNumberOfCalls(5) + .slowCallRateThreshold(30) + .slowCallDurationThreshold(Duration.ofSeconds(2)) + .enableAutomaticTransitionFromOpenToHalfOpen() + .permittedNumberOfCallsInHalfOpenState(3) + .build(); + + return registry.circuitBreaker("customer-validation", config); + } +} diff --git a/services/order/src/main/java/com/alibou/ecommerce/config/KafkaOrderTopicConfig.java b/services/order/src/main/java/com/pramithamj/ecommerce/config/KafkaOrderTopicConfig.java similarity index 90% rename from services/order/src/main/java/com/alibou/ecommerce/config/KafkaOrderTopicConfig.java rename to services/order/src/main/java/com/pramithamj/ecommerce/config/KafkaOrderTopicConfig.java index 5c9a18b..54844ab 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/config/KafkaOrderTopicConfig.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/config/KafkaOrderTopicConfig.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.config; +package com.pramithamj.ecommerce.config; import org.apache.kafka.clients.admin.NewTopic; import org.springframework.context.annotation.Bean; diff --git a/services/order/src/main/java/com/alibou/ecommerce/config/RestTemplateConfig.java b/services/order/src/main/java/com/pramithamj/ecommerce/config/RestTemplateConfig.java similarity index 88% rename from services/order/src/main/java/com/alibou/ecommerce/config/RestTemplateConfig.java rename to services/order/src/main/java/com/pramithamj/ecommerce/config/RestTemplateConfig.java index a3630b1..be07dbe 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/config/RestTemplateConfig.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/config/RestTemplateConfig.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.config; +package com.pramithamj.ecommerce.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/services/order/src/main/java/com/alibou/ecommerce/customer/CustomerClient.java b/services/order/src/main/java/com/pramithamj/ecommerce/customer/CustomerClient.java similarity index 91% rename from services/order/src/main/java/com/alibou/ecommerce/customer/CustomerClient.java rename to services/order/src/main/java/com/pramithamj/ecommerce/customer/CustomerClient.java index e145547..aa6c39e 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/customer/CustomerClient.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/customer/CustomerClient.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.customer; +package com.pramithamj.ecommerce.customer; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; diff --git a/services/order/src/main/java/com/alibou/ecommerce/customer/CustomerResponse.java b/services/order/src/main/java/com/pramithamj/ecommerce/customer/CustomerResponse.java similarity index 72% rename from services/order/src/main/java/com/alibou/ecommerce/customer/CustomerResponse.java rename to services/order/src/main/java/com/pramithamj/ecommerce/customer/CustomerResponse.java index 8e2f7a2..8becfb2 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/customer/CustomerResponse.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/customer/CustomerResponse.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.customer; +package com.pramithamj.ecommerce.customer; public record CustomerResponse( String id, diff --git a/services/order/src/main/java/com/alibou/ecommerce/exception/BusinessException.java b/services/order/src/main/java/com/pramithamj/ecommerce/exception/BusinessException.java similarity index 80% rename from services/order/src/main/java/com/alibou/ecommerce/exception/BusinessException.java rename to services/order/src/main/java/com/pramithamj/ecommerce/exception/BusinessException.java index 04a75fd..181400a 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/exception/BusinessException.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/exception/BusinessException.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.exception; +package com.pramithamj.ecommerce.exception; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/services/product/src/main/java/com/alibou/ecommerce/handler/ErrorResponse.java b/services/order/src/main/java/com/pramithamj/ecommerce/handler/ErrorResponse.java similarity index 68% rename from services/product/src/main/java/com/alibou/ecommerce/handler/ErrorResponse.java rename to services/order/src/main/java/com/pramithamj/ecommerce/handler/ErrorResponse.java index e1d0cf9..f9d2616 100644 --- a/services/product/src/main/java/com/alibou/ecommerce/handler/ErrorResponse.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/handler/ErrorResponse.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.handler; +package com.pramithamj.ecommerce.handler; import java.util.Map; diff --git a/services/order/src/main/java/com/alibou/ecommerce/handler/GlobalExceptionHandler.java b/services/order/src/main/java/com/pramithamj/ecommerce/handler/GlobalExceptionHandler.java similarity index 93% rename from services/order/src/main/java/com/alibou/ecommerce/handler/GlobalExceptionHandler.java rename to services/order/src/main/java/com/pramithamj/ecommerce/handler/GlobalExceptionHandler.java index 64bce5f..20d3e8f 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/handler/GlobalExceptionHandler.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/handler/GlobalExceptionHandler.java @@ -1,6 +1,6 @@ -package com.alibou.ecommerce.handler; +package com.pramithamj.ecommerce.handler; -import com.alibou.ecommerce.exception.BusinessException; +import com.pramithamj.ecommerce.exception.BusinessException; import jakarta.persistence.EntityNotFoundException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/services/order/src/main/java/com/alibou/ecommerce/kafka/OrderConfirmation.java b/services/order/src/main/java/com/pramithamj/ecommerce/kafka/OrderConfirmation.java similarity index 56% rename from services/order/src/main/java/com/alibou/ecommerce/kafka/OrderConfirmation.java rename to services/order/src/main/java/com/pramithamj/ecommerce/kafka/OrderConfirmation.java index 13fe202..e9a2a86 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/kafka/OrderConfirmation.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/kafka/OrderConfirmation.java @@ -1,8 +1,8 @@ -package com.alibou.ecommerce.kafka; +package com.pramithamj.ecommerce.kafka; -import com.alibou.ecommerce.customer.CustomerResponse; -import com.alibou.ecommerce.order.PaymentMethod; -import com.alibou.ecommerce.product.PurchaseResponse; +import com.pramithamj.ecommerce.customer.CustomerResponse; +import com.pramithamj.ecommerce.order.PaymentMethod; +import com.pramithamj.ecommerce.product.PurchaseResponse; import java.math.BigDecimal; import java.util.List; diff --git a/services/order/src/main/java/com/alibou/ecommerce/kafka/OrderProducer.java b/services/order/src/main/java/com/pramithamj/ecommerce/kafka/OrderProducer.java similarity index 95% rename from services/order/src/main/java/com/alibou/ecommerce/kafka/OrderProducer.java rename to services/order/src/main/java/com/pramithamj/ecommerce/kafka/OrderProducer.java index 6373f33..ee98b84 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/kafka/OrderProducer.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/kafka/OrderProducer.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.kafka; +package com.pramithamj.ecommerce.kafka; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/services/order/src/main/java/com/alibou/ecommerce/order/Order.java b/services/order/src/main/java/com/pramithamj/ecommerce/order/Order.java similarity index 93% rename from services/order/src/main/java/com/alibou/ecommerce/order/Order.java rename to services/order/src/main/java/com/pramithamj/ecommerce/order/Order.java index cc79984..fe9af5c 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/order/Order.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/order/Order.java @@ -1,6 +1,6 @@ -package com.alibou.ecommerce.order; +package com.pramithamj.ecommerce.order; -import com.alibou.ecommerce.orderline.OrderLine; +import com.pramithamj.ecommerce.orderline.OrderLine; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EntityListeners; diff --git a/services/order/src/main/java/com/alibou/ecommerce/order/OrderController.java b/services/order/src/main/java/com/pramithamj/ecommerce/order/OrderController.java similarity index 96% rename from services/order/src/main/java/com/alibou/ecommerce/order/OrderController.java rename to services/order/src/main/java/com/pramithamj/ecommerce/order/OrderController.java index 3a0a944..e38b76b 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/order/OrderController.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/order/OrderController.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.order; +package com.pramithamj.ecommerce.order; import java.util.List; diff --git a/services/order/src/main/java/com/alibou/ecommerce/order/OrderMapper.java b/services/order/src/main/java/com/pramithamj/ecommerce/order/OrderMapper.java similarity index 94% rename from services/order/src/main/java/com/alibou/ecommerce/order/OrderMapper.java rename to services/order/src/main/java/com/pramithamj/ecommerce/order/OrderMapper.java index b6ce828..c5266db 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/order/OrderMapper.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/order/OrderMapper.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.order; +package com.pramithamj.ecommerce.order; import org.springframework.stereotype.Service; diff --git a/services/order/src/main/java/com/alibou/ecommerce/order/OrderRepository.java b/services/order/src/main/java/com/pramithamj/ecommerce/order/OrderRepository.java similarity index 77% rename from services/order/src/main/java/com/alibou/ecommerce/order/OrderRepository.java rename to services/order/src/main/java/com/pramithamj/ecommerce/order/OrderRepository.java index 78116ad..3540345 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/order/OrderRepository.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/order/OrderRepository.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.order; +package com.pramithamj.ecommerce.order; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/services/order/src/main/java/com/alibou/ecommerce/order/OrderRequest.java b/services/order/src/main/java/com/pramithamj/ecommerce/order/OrderRequest.java similarity index 90% rename from services/order/src/main/java/com/alibou/ecommerce/order/OrderRequest.java rename to services/order/src/main/java/com/pramithamj/ecommerce/order/OrderRequest.java index 2ccd5ea..16d1a23 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/order/OrderRequest.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/order/OrderRequest.java @@ -1,6 +1,6 @@ -package com.alibou.ecommerce.order; +package com.pramithamj.ecommerce.order; -import com.alibou.ecommerce.product.PurchaseRequest; +import com.pramithamj.ecommerce.product.PurchaseRequest; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import jakarta.validation.constraints.NotBlank; diff --git a/services/order/src/main/java/com/alibou/ecommerce/order/OrderResponse.java b/services/order/src/main/java/com/pramithamj/ecommerce/order/OrderResponse.java similarity index 89% rename from services/order/src/main/java/com/alibou/ecommerce/order/OrderResponse.java rename to services/order/src/main/java/com/pramithamj/ecommerce/order/OrderResponse.java index 7ba12fa..d7beea8 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/order/OrderResponse.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/order/OrderResponse.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.order; +package com.pramithamj.ecommerce.order; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; diff --git a/services/order/src/main/java/com/alibou/ecommerce/order/OrderService.java b/services/order/src/main/java/com/pramithamj/ecommerce/order/OrderService.java similarity index 81% rename from services/order/src/main/java/com/alibou/ecommerce/order/OrderService.java rename to services/order/src/main/java/com/pramithamj/ecommerce/order/OrderService.java index 4982e27..86b8aff 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/order/OrderService.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/order/OrderService.java @@ -1,15 +1,15 @@ -package com.alibou.ecommerce.order; +package com.pramithamj.ecommerce.order; -import com.alibou.ecommerce.kafka.OrderConfirmation; -import com.alibou.ecommerce.customer.CustomerClient; -import com.alibou.ecommerce.exception.BusinessException; -import com.alibou.ecommerce.kafka.OrderProducer; -import com.alibou.ecommerce.orderline.OrderLineRequest; -import com.alibou.ecommerce.orderline.OrderLineService; -import com.alibou.ecommerce.payment.PaymentClient; -import com.alibou.ecommerce.payment.PaymentRequest; -import com.alibou.ecommerce.product.ProductClient; -import com.alibou.ecommerce.product.PurchaseRequest; +import com.pramithamj.ecommerce.kafka.OrderConfirmation; +import com.pramithamj.ecommerce.customer.CustomerClient; +import com.pramithamj.ecommerce.exception.BusinessException; +import com.pramithamj.ecommerce.kafka.OrderProducer; +import com.pramithamj.ecommerce.orderline.OrderLineRequest; +import com.pramithamj.ecommerce.orderline.OrderLineService; +import com.pramithamj.ecommerce.payment.PaymentClient; +import com.pramithamj.ecommerce.payment.PaymentRequest; +import com.pramithamj.ecommerce.product.ProductClient; +import com.pramithamj.ecommerce.product.PurchaseRequest; import jakarta.persistence.EntityNotFoundException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentMethod.java b/services/order/src/main/java/com/pramithamj/ecommerce/order/PaymentMethod.java similarity index 69% rename from services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentMethod.java rename to services/order/src/main/java/com/pramithamj/ecommerce/order/PaymentMethod.java index d219626..68bc224 100644 --- a/services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentMethod.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/order/PaymentMethod.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.payment; +package com.pramithamj.ecommerce.order; public enum PaymentMethod { diff --git a/services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLine.java b/services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLine.java similarity index 88% rename from services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLine.java rename to services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLine.java index 86b6e9c..fbff1c8 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLine.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLine.java @@ -1,7 +1,7 @@ -package com.alibou.ecommerce.orderline; +package com.pramithamj.ecommerce.orderline; -import com.alibou.ecommerce.order.Order; +import com.pramithamj.ecommerce.order.Order; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; diff --git a/services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineController.java b/services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineController.java similarity index 94% rename from services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineController.java rename to services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineController.java index b4617a4..3921ad2 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineController.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineController.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.orderline; +package com.pramithamj.ecommerce.orderline; import lombok.RequiredArgsConstructor; diff --git a/services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineMapper.java b/services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineMapper.java similarity index 89% rename from services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineMapper.java rename to services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineMapper.java index 0222f75..4fff27f 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineMapper.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineMapper.java @@ -1,6 +1,6 @@ -package com.alibou.ecommerce.orderline; +package com.pramithamj.ecommerce.orderline; -import com.alibou.ecommerce.order.Order; +import com.pramithamj.ecommerce.order.Order; import org.springframework.stereotype.Service; @Service diff --git a/services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineRepository.java b/services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineRepository.java similarity index 83% rename from services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineRepository.java rename to services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineRepository.java index 185bc97..0095d7c 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineRepository.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineRepository.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.orderline; +package com.pramithamj.ecommerce.orderline; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineRequest.java b/services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineRequest.java similarity index 75% rename from services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineRequest.java rename to services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineRequest.java index 1a6c061..4f15ef3 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineRequest.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineRequest.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.orderline; +package com.pramithamj.ecommerce.orderline; public record OrderLineRequest( Integer id, diff --git a/services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineResponse.java b/services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineResponse.java similarity index 65% rename from services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineResponse.java rename to services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineResponse.java index b8ecc53..90e9906 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineResponse.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineResponse.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.orderline; +package com.pramithamj.ecommerce.orderline; public record OrderLineResponse( Integer id, diff --git a/services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineService.java b/services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineService.java similarity index 94% rename from services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineService.java rename to services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineService.java index 5b8b4b7..8da30ce 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/orderline/OrderLineService.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/orderline/OrderLineService.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.orderline; +package com.pramithamj.ecommerce.orderline; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/services/order/src/main/java/com/alibou/ecommerce/payment/PaymentClient.java b/services/order/src/main/java/com/pramithamj/ecommerce/payment/PaymentClient.java similarity index 90% rename from services/order/src/main/java/com/alibou/ecommerce/payment/PaymentClient.java rename to services/order/src/main/java/com/pramithamj/ecommerce/payment/PaymentClient.java index 2d085e8..8f6df48 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/payment/PaymentClient.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/payment/PaymentClient.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.payment; +package com.pramithamj.ecommerce.payment; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; diff --git a/services/order/src/main/java/com/alibou/ecommerce/payment/PaymentRequest.java b/services/order/src/main/java/com/pramithamj/ecommerce/payment/PaymentRequest.java similarity index 56% rename from services/order/src/main/java/com/alibou/ecommerce/payment/PaymentRequest.java rename to services/order/src/main/java/com/pramithamj/ecommerce/payment/PaymentRequest.java index 0430172..0a74de8 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/payment/PaymentRequest.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/payment/PaymentRequest.java @@ -1,7 +1,7 @@ -package com.alibou.ecommerce.payment; +package com.pramithamj.ecommerce.payment; -import com.alibou.ecommerce.customer.CustomerResponse; -import com.alibou.ecommerce.order.PaymentMethod; +import com.pramithamj.ecommerce.customer.CustomerResponse; +import com.pramithamj.ecommerce.order.PaymentMethod; import java.math.BigDecimal; diff --git a/services/order/src/main/java/com/alibou/ecommerce/product/ProductClient.java b/services/order/src/main/java/com/pramithamj/ecommerce/product/ProductClient.java similarity index 94% rename from services/order/src/main/java/com/alibou/ecommerce/product/ProductClient.java rename to services/order/src/main/java/com/pramithamj/ecommerce/product/ProductClient.java index 47594e4..de8db00 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/product/ProductClient.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/product/ProductClient.java @@ -1,6 +1,6 @@ -package com.alibou.ecommerce.product; +package com.pramithamj.ecommerce.product; -import com.alibou.ecommerce.exception.BusinessException; +import com.pramithamj.ecommerce.exception.BusinessException; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.ParameterizedTypeReference; diff --git a/services/order/src/main/java/com/alibou/ecommerce/product/PurchaseRequest.java b/services/order/src/main/java/com/pramithamj/ecommerce/product/PurchaseRequest.java similarity index 89% rename from services/order/src/main/java/com/alibou/ecommerce/product/PurchaseRequest.java rename to services/order/src/main/java/com/pramithamj/ecommerce/product/PurchaseRequest.java index 4ae528e..18db6fe 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/product/PurchaseRequest.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/product/PurchaseRequest.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.product; +package com.pramithamj.ecommerce.product; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; diff --git a/services/order/src/main/java/com/alibou/ecommerce/product/PurchaseResponse.java b/services/order/src/main/java/com/pramithamj/ecommerce/product/PurchaseResponse.java similarity index 82% rename from services/order/src/main/java/com/alibou/ecommerce/product/PurchaseResponse.java rename to services/order/src/main/java/com/pramithamj/ecommerce/product/PurchaseResponse.java index af0ea4c..2c47e3a 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/product/PurchaseResponse.java +++ b/services/order/src/main/java/com/pramithamj/ecommerce/product/PurchaseResponse.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.product; +package com.pramithamj.ecommerce.product; import java.math.BigDecimal; diff --git a/services/order/src/main/java/com/pramithamj/ecommerce/saga/OrderSaga.java b/services/order/src/main/java/com/pramithamj/ecommerce/saga/OrderSaga.java new file mode 100644 index 0000000..54bb00d --- /dev/null +++ b/services/order/src/main/java/com/pramithamj/ecommerce/saga/OrderSaga.java @@ -0,0 +1,62 @@ +package com.pramithamj.ecommerce.saga; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Document(collection = "order_saga") +public class OrderSaga { + + @Id + private String sagaId; + private Integer orderId; + private String customerId; + private List products; + private String paymentMethod; + private SagaStatus status; + private Map sagaData; + private List steps; + private LocalDateTime startTime; + private LocalDateTime endTime; + private String errorMessage; + private Integer retryCount; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class OrderLineRequest { + private Integer productId; + private Integer quantity; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class SagaStep { + private String stepName; + private SagaStepStatus status; + private LocalDateTime executedAt; + private String errorMessage; + private Object stepData; + } + + public enum SagaStepStatus { + PENDING, + COMPLETED, + COMPENSATED, + FAILED + } +} diff --git a/services/order/src/main/java/com/pramithamj/ecommerce/saga/OrderSagaOrchestrator.java b/services/order/src/main/java/com/pramithamj/ecommerce/saga/OrderSagaOrchestrator.java new file mode 100644 index 0000000..d402baf --- /dev/null +++ b/services/order/src/main/java/com/pramithamj/ecommerce/saga/OrderSagaOrchestrator.java @@ -0,0 +1,308 @@ +package com.pramithamj.ecommerce.saga; + +import com.pramithamj.ecommerce.saga.events.*; +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +@Slf4j +public class OrderSagaOrchestrator { + + private final OrderSagaRepository sagaRepository; + private final KafkaTemplate kafkaTemplate; + private final CircuitBreaker paymentServiceCircuitBreaker; + + // Saga-specific circuit breakers + private final CircuitBreaker sagaOrchestrationCircuitBreaker; + private final CircuitBreaker sagaCompensationCircuitBreaker; + private final CircuitBreaker inventoryReservationCircuitBreaker; + private final CircuitBreaker customerValidationCircuitBreaker; + + public String startOrderSaga(List products, String customerId, String paymentMethod) { + return sagaOrchestrationCircuitBreaker.executeSupplier(() -> { + String sagaId = UUID.randomUUID().toString(); + + OrderSaga saga = OrderSaga.builder() + .sagaId(sagaId) + .customerId(customerId) + .products(products) + .paymentMethod(paymentMethod) + .status(SagaStatus.STARTED) + .sagaData(new HashMap<>()) + .steps(new ArrayList<>()) + .startTime(LocalDateTime.now()) + .retryCount(0) + .build(); + + saga = sagaRepository.save(saga); + log.info("Started Order Saga: {}", sagaId); + + // Start the saga by validating customer + executeCustomerValidationStep(saga); + + return sagaId; + }); + } + + private void executeCustomerValidationStep(OrderSaga saga) { + try { + // Execute with saga-specific circuit breaker for customer validation + customerValidationCircuitBreaker.executeSupplier(() -> { + log.info("Validating customer: {} for saga: {}", saga.getCustomerId(), saga.getSagaId()); + + // Send customer validation event + CustomerValidationEvent event = CustomerValidationEvent.builder() + .sagaId(saga.getSagaId()) + .customerId(saga.getCustomerId()) + .build(); + + kafkaTemplate.send("customer-validation-topic", event); + return true; + }); + + addSagaStep(saga, "CUSTOMER_VALIDATION", OrderSaga.SagaStepStatus.PENDING); + updateSagaStatus(saga, SagaStatus.CUSTOMER_VALIDATED); + + } catch (Exception e) { + log.error("Customer validation failed for saga: {}", saga.getSagaId(), e); + handleSagaFailure(saga, "Customer validation failed: " + e.getMessage()); + } + } + + public void handleCustomerValidationSuccess(String sagaId, String customerId) { + OrderSaga saga = sagaRepository.findBySagaId(sagaId); + if (saga == null) return; + + log.info("Customer validation successful for saga: {}", sagaId); + updateSagaStep(saga, "CUSTOMER_VALIDATION", OrderSaga.SagaStepStatus.COMPLETED); + + // Proceed to inventory reservation + executeInventoryReservationStep(saga); + } + + public void handleCustomerValidationFailure(String sagaId, String errorMessage) { + OrderSaga saga = sagaRepository.findBySagaId(sagaId); + if (saga == null) return; + + log.error("Customer validation failed for saga: {}: {}", sagaId, errorMessage); + updateSagaStep(saga, "CUSTOMER_VALIDATION", OrderSaga.SagaStepStatus.FAILED); + handleSagaFailure(saga, errorMessage); + } + + private void executeInventoryReservationStep(OrderSaga saga) { + try { + inventoryReservationCircuitBreaker.executeSupplier(() -> { + log.info("Reserving inventory for saga: {}", saga.getSagaId()); + + InventoryReservationEvent event = InventoryReservationEvent.builder() + .sagaId(saga.getSagaId()) + .products(saga.getProducts()) + .build(); + + kafkaTemplate.send("inventory-reservation-topic", event); + return true; + }); + + addSagaStep(saga, "INVENTORY_RESERVATION", OrderSaga.SagaStepStatus.PENDING); + updateSagaStatus(saga, SagaStatus.INVENTORY_RESERVED); + + } catch (Exception e) { + log.error("Inventory reservation failed for saga: {}", saga.getSagaId(), e); + startCompensation(saga, "Inventory reservation failed: " + e.getMessage()); + } + } + + public void handleInventoryReservationSuccess(String sagaId) { + OrderSaga saga = sagaRepository.findBySagaId(sagaId); + if (saga == null) return; + + log.info("Inventory reservation successful for saga: {}", sagaId); + updateSagaStep(saga, "INVENTORY_RESERVATION", OrderSaga.SagaStepStatus.COMPLETED); + + // Proceed to payment processing + executePaymentProcessingStep(saga); + } + + public void handleInventoryReservationFailure(String sagaId, String errorMessage) { + OrderSaga saga = sagaRepository.findBySagaId(sagaId); + if (saga == null) return; + + log.error("Inventory reservation failed for saga: {}: {}", sagaId, errorMessage); + updateSagaStep(saga, "INVENTORY_RESERVATION", OrderSaga.SagaStepStatus.FAILED); + startCompensation(saga, errorMessage); + } + + private void executePaymentProcessingStep(OrderSaga saga) { + try { + paymentServiceCircuitBreaker.executeSupplier(() -> { + log.info("Processing payment for saga: {}", saga.getSagaId()); + + PaymentProcessingEvent event = PaymentProcessingEvent.builder() + .sagaId(saga.getSagaId()) + .customerId(saga.getCustomerId()) + .amount(calculateTotalAmount(saga.getProducts())) + .paymentMethod(saga.getPaymentMethod()) + .build(); + + kafkaTemplate.send("payment-processing-topic", event); + return true; + }); + + addSagaStep(saga, "PAYMENT_PROCESSING", OrderSaga.SagaStepStatus.PENDING); + updateSagaStatus(saga, SagaStatus.PAYMENT_PROCESSED); + + } catch (Exception e) { + log.error("Payment processing failed for saga: {}", saga.getSagaId(), e); + startCompensation(saga, "Payment processing failed: " + e.getMessage()); + } + } + + public void handlePaymentProcessingSuccess(String sagaId, Integer paymentId) { + OrderSaga saga = sagaRepository.findBySagaId(sagaId); + if (saga == null) return; + + log.info("Payment processing successful for saga: {}", sagaId); + updateSagaStep(saga, "PAYMENT_PROCESSING", OrderSaga.SagaStepStatus.COMPLETED); + saga.getSagaData().put("paymentId", paymentId); + + // Complete the saga + completeSaga(saga); + } + + public void handlePaymentProcessingFailure(String sagaId, String errorMessage) { + OrderSaga saga = sagaRepository.findBySagaId(sagaId); + if (saga == null) return; + + log.error("Payment processing failed for saga: {}: {}", sagaId, errorMessage); + updateSagaStep(saga, "PAYMENT_PROCESSING", OrderSaga.SagaStepStatus.FAILED); + startCompensation(saga, errorMessage); + } + + private void completeSaga(OrderSaga saga) { + log.info("Completing saga: {}", saga.getSagaId()); + + OrderCompletionEvent event = OrderCompletionEvent.builder() + .sagaId(saga.getSagaId()) + .orderId(saga.getOrderId()) + .customerId(saga.getCustomerId()) + .products(saga.getProducts()) + .paymentId((Integer) saga.getSagaData().get("paymentId")) + .build(); + + kafkaTemplate.send("order-completion-topic", event); + + updateSagaStatus(saga, SagaStatus.COMPLETED); + saga.setEndTime(LocalDateTime.now()); + sagaRepository.save(saga); + + log.info("Saga completed successfully: {}", saga.getSagaId()); + } + + private void startCompensation(OrderSaga saga, String errorMessage) { + sagaCompensationCircuitBreaker.executeSupplier(() -> { + log.warn("Starting compensation for saga: {} due to: {}", saga.getSagaId(), errorMessage); + + saga.setStatus(SagaStatus.COMPENSATING); + saga.setErrorMessage(errorMessage); + sagaRepository.save(saga); + + // Compensate in reverse order + if (isStepCompleted(saga, "PAYMENT_PROCESSING")) { + compensatePayment(saga); + } else if (isStepCompleted(saga, "INVENTORY_RESERVATION")) { + compensateInventoryReservation(saga); + } else { + // No compensation needed, just mark as cancelled + updateSagaStatus(saga, SagaStatus.CANCELLED); + saga.setEndTime(LocalDateTime.now()); + sagaRepository.save(saga); + } + return true; + }); + } + + private void compensatePayment(OrderSaga saga) { + sagaCompensationCircuitBreaker.executeSupplier(() -> { + log.info("Compensating payment for saga: {}", saga.getSagaId()); + + PaymentCompensationEvent event = PaymentCompensationEvent.builder() + .sagaId(saga.getSagaId()) + .paymentId((Integer) saga.getSagaData().get("paymentId")) + .build(); + + kafkaTemplate.send("payment-compensation-topic", event); + return true; + }); + } + + private void compensateInventoryReservation(OrderSaga saga) { + sagaCompensationCircuitBreaker.executeSupplier(() -> { + log.info("Compensating inventory reservation for saga: {}", saga.getSagaId()); + + InventoryCompensationEvent event = InventoryCompensationEvent.builder() + .sagaId(saga.getSagaId()) + .products(saga.getProducts()) + .build(); + + kafkaTemplate.send("inventory-compensation-topic", event); + return true; + }); + } + + private void handleSagaFailure(OrderSaga saga, String errorMessage) { + log.error("Saga failed: {} - {}", saga.getSagaId(), errorMessage); + + saga.setStatus(SagaStatus.FAILED); + saga.setErrorMessage(errorMessage); + saga.setEndTime(LocalDateTime.now()); + sagaRepository.save(saga); + } + + private void addSagaStep(OrderSaga saga, String stepName, OrderSaga.SagaStepStatus status) { + OrderSaga.SagaStep step = OrderSaga.SagaStep.builder() + .stepName(stepName) + .status(status) + .executedAt(LocalDateTime.now()) + .build(); + + saga.getSteps().add(step); + sagaRepository.save(saga); + } + + private void updateSagaStep(OrderSaga saga, String stepName, OrderSaga.SagaStepStatus status) { + saga.getSteps().stream() + .filter(step -> step.getStepName().equals(stepName)) + .findFirst() + .ifPresent(step -> step.setStatus(status)); + + sagaRepository.save(saga); + } + + private void updateSagaStatus(OrderSaga saga, SagaStatus status) { + saga.setStatus(status); + sagaRepository.save(saga); + } + + private boolean isStepCompleted(OrderSaga saga, String stepName) { + return saga.getSteps().stream() + .anyMatch(step -> step.getStepName().equals(stepName) && + step.getStatus() == OrderSaga.SagaStepStatus.COMPLETED); + } + + private Double calculateTotalAmount(List products) { + // This would typically calculate from product prices + return products.stream() + .mapToDouble(p -> p.getQuantity() * 100.0) // Placeholder calculation + .sum(); + } +} diff --git a/services/order/src/main/java/com/pramithamj/ecommerce/saga/OrderSagaRepository.java b/services/order/src/main/java/com/pramithamj/ecommerce/saga/OrderSagaRepository.java new file mode 100644 index 0000000..c42664d --- /dev/null +++ b/services/order/src/main/java/com/pramithamj/ecommerce/saga/OrderSagaRepository.java @@ -0,0 +1,23 @@ +package com.pramithamj.ecommerce.saga; + +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.List; + +@Repository +public interface OrderSagaRepository extends MongoRepository { + OrderSaga findBySagaId(String sagaId); + List findByStatus(SagaStatus status); + List findByCustomerId(String customerId); + List findByOrderId(Integer orderId); + + // For saga recovery + List findByStatusInAndStartTimeBefore(List statuses, LocalDateTime threshold); + List findByStatusInAndEndTimeBefore(List statuses, LocalDateTime threshold); + + // For monitoring and metrics + long countByStatus(SagaStatus status); + long countByStatusAndStartTimeAfter(SagaStatus status, LocalDateTime since); +} diff --git a/services/order/src/main/java/com/pramithamj/ecommerce/saga/SagaRecoveryManager.java b/services/order/src/main/java/com/pramithamj/ecommerce/saga/SagaRecoveryManager.java new file mode 100644 index 0000000..83035e4 --- /dev/null +++ b/services/order/src/main/java/com/pramithamj/ecommerce/saga/SagaRecoveryManager.java @@ -0,0 +1,342 @@ +package com.pramithamj.ecommerce.saga; + +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * Saga Recovery Manager handles timeout scenarios and saga cleanup + * Implements compensation logic for failed or timed-out sagas + */ +@Component +@RequiredArgsConstructor +@Slf4j +public class SagaRecoveryManager { + + private final OrderSagaRepository sagaRepository; + private final KafkaTemplate kafkaTemplate; + private final CircuitBreaker sagaRecoveryCircuitBreaker; + + private static final int SAGA_TIMEOUT_MINUTES = 30; + private static final int MAX_RETRY_ATTEMPTS = 3; + + /** + * Runs every 5 minutes to check for timed-out sagas + */ + @Scheduled(fixedRate = 300000) // 5 minutes + public void recoverTimedOutSagas() { + log.info("Starting saga recovery process..."); + + LocalDateTime timeoutThreshold = LocalDateTime.now().minusMinutes(SAGA_TIMEOUT_MINUTES); + + // Find sagas that are stuck in pending states + List timedOutSagas = sagaRepository.findByStatusInAndStartTimeBefore( + List.of(SagaStatus.STARTED, SagaStatus.CUSTOMER_VALIDATED, + SagaStatus.INVENTORY_RESERVED, SagaStatus.PAYMENT_PROCESSED), + timeoutThreshold + ); + + for (OrderSaga saga : timedOutSagas) { + handleTimedOutSaga(saga); + } + + log.info("Saga recovery process completed. Processed {} timed-out sagas", timedOutSagas.size()); + } + + /** + * Handles a timed-out saga by either retrying or starting compensation + */ + private void handleTimedOutSaga(OrderSaga saga) { + sagaRecoveryCircuitBreaker.executeSupplier(() -> { + log.warn("Handling timed-out saga: {} in status: {}", saga.getSagaId(), saga.getStatus()); + + if (saga.getRetryCount() < MAX_RETRY_ATTEMPTS) { + // Retry the saga + retrySaga(saga); + } else { + // Start compensation process + startCompensationForTimedOutSaga(saga); + } + return true; + }); + } + + /** + * Retries a saga based on its current status + */ + private void retrySaga(OrderSaga saga) { + saga.setRetryCount(saga.getRetryCount() + 1); + + log.info("Retrying saga: {} (attempt {}/{})", saga.getSagaId(), saga.getRetryCount(), MAX_RETRY_ATTEMPTS); + + switch (saga.getStatus()) { + case STARTED: + // Retry customer validation + retryCustomerValidation(saga); + break; + case CUSTOMER_VALIDATED: + // Retry inventory reservation + retryInventoryReservation(saga); + break; + case INVENTORY_RESERVED: + // Retry payment processing + retryPaymentProcessing(saga); + break; + case PAYMENT_PROCESSED: + // Check if saga should be completed + checkSagaCompletion(saga); + break; + default: + log.warn("Cannot retry saga in status: {}", saga.getStatus()); + break; + } + + sagaRepository.save(saga); + } + + /** + * Starts compensation for a timed-out saga + */ + private void startCompensationForTimedOutSaga(OrderSaga saga) { + sagaRecoveryCircuitBreaker.executeSupplier(() -> { + log.warn("Starting compensation for timed-out saga: {}", saga.getSagaId()); + + saga.setStatus(SagaStatus.COMPENSATING); + saga.setErrorMessage("Saga timed out after " + SAGA_TIMEOUT_MINUTES + " minutes"); + saga.setEndTime(LocalDateTime.now()); + + // Start compensation based on how far the saga progressed + switch (saga.getStatus()) { + case PAYMENT_PROCESSED: + compensatePayment(saga); + break; + case INVENTORY_RESERVED: + compensateInventory(saga); + break; + default: + // No compensation needed for earlier stages + saga.setStatus(SagaStatus.CANCELLED); + break; + } + + sagaRepository.save(saga); + return true; + }); + } + + /** + * Retry customer validation step + */ + private void retryCustomerValidation(OrderSaga saga) { + log.info("Retrying customer validation for saga: {}", saga.getSagaId()); + + // Publish customer validation event + kafkaTemplate.send("customer-validation-topic", new CustomerValidationRetryEvent( + saga.getSagaId(), + saga.getCustomerId(), + saga.getRetryCount() + )); + } + + /** + * Retry inventory reservation step + */ + private void retryInventoryReservation(OrderSaga saga) { + log.info("Retrying inventory reservation for saga: {}", saga.getSagaId()); + + // Publish inventory reservation event + kafkaTemplate.send("inventory-reservation-topic", new InventoryReservationRetryEvent( + saga.getSagaId(), + saga.getProducts(), + saga.getRetryCount() + )); + } + + /** + * Retry payment processing step + */ + private void retryPaymentProcessing(OrderSaga saga) { + log.info("Retrying payment processing for saga: {}", saga.getSagaId()); + + // Calculate total amount + Double totalAmount = saga.getProducts().stream() + .mapToDouble(product -> product.getQuantity() * getProductPrice(product.getProductId())) + .sum(); + + // Publish payment processing event + kafkaTemplate.send("payment-processing-topic", new PaymentProcessingRetryEvent( + saga.getSagaId(), + saga.getCustomerId(), + totalAmount, + saga.getPaymentMethod(), + saga.getRetryCount() + )); + } + + /** + * Check if saga should be completed + */ + private void checkSagaCompletion(OrderSaga saga) { + log.info("Checking completion status for saga: {}", saga.getSagaId()); + + // Check if all steps are completed + boolean allStepsCompleted = saga.getSteps().stream() + .allMatch(step -> step.getStatus() == OrderSaga.SagaStepStatus.COMPLETED); + + if (allStepsCompleted) { + saga.setStatus(SagaStatus.COMPLETED); + saga.setEndTime(LocalDateTime.now()); + log.info("Saga completed during recovery: {}", saga.getSagaId()); + } else { + log.warn("Saga steps not all completed during recovery check: {}", saga.getSagaId()); + } + } + + /** + * Compensate payment for failed saga + */ + private void compensatePayment(OrderSaga saga) { + log.info("Compensating payment for saga: {}", saga.getSagaId()); + + Integer paymentId = (Integer) saga.getSagaData().get("paymentId"); + if (paymentId != null) { + kafkaTemplate.send("payment-compensation-topic", new PaymentCompensationEvent( + saga.getSagaId(), + paymentId + )); + } + } + + /** + * Compensate inventory reservation for failed saga + */ + private void compensateInventory(OrderSaga saga) { + log.info("Compensating inventory for saga: {}", saga.getSagaId()); + + kafkaTemplate.send("inventory-compensation-topic", new InventoryCompensationEvent( + saga.getSagaId(), + saga.getProducts() + )); + } + + /** + * Get product price (this would typically call Product Service) + */ + private Double getProductPrice(Integer productId) { + // In a real implementation, this would call the Product Service + // For now, return a default price + return 100.0; + } + + /** + * Clean up old completed sagas (runs daily) + */ + @Scheduled(cron = "0 0 2 * * ?") // Daily at 2 AM + public void cleanupOldSagas() { + log.info("Starting cleanup of old completed sagas..."); + + LocalDateTime cleanupThreshold = LocalDateTime.now().minusDays(30); + + List oldSagas = sagaRepository.findByStatusInAndEndTimeBefore( + List.of(SagaStatus.COMPLETED, SagaStatus.CANCELLED, SagaStatus.FAILED), + cleanupThreshold + ); + + if (!oldSagas.isEmpty()) { + sagaRepository.deleteAll(oldSagas); + log.info("Cleaned up {} old saga records", oldSagas.size()); + } + } + + // Event classes for retry scenarios + private static class CustomerValidationRetryEvent { + private String sagaId; + private String customerId; + private int retryAttempt; + + public CustomerValidationRetryEvent(String sagaId, String customerId, int retryAttempt) { + this.sagaId = sagaId; + this.customerId = customerId; + this.retryAttempt = retryAttempt; + } + + // Getters + public String getSagaId() { return sagaId; } + public String getCustomerId() { return customerId; } + public int getRetryAttempt() { return retryAttempt; } + } + + private static class InventoryReservationRetryEvent { + private String sagaId; + private List products; + private int retryAttempt; + + public InventoryReservationRetryEvent(String sagaId, List products, int retryAttempt) { + this.sagaId = sagaId; + this.products = products; + this.retryAttempt = retryAttempt; + } + + // Getters + public String getSagaId() { return sagaId; } + public List getProducts() { return products; } + public int getRetryAttempt() { return retryAttempt; } + } + + private static class PaymentProcessingRetryEvent { + private String sagaId; + private String customerId; + private Double amount; + private String paymentMethod; + private int retryAttempt; + + public PaymentProcessingRetryEvent(String sagaId, String customerId, Double amount, String paymentMethod, int retryAttempt) { + this.sagaId = sagaId; + this.customerId = customerId; + this.amount = amount; + this.paymentMethod = paymentMethod; + this.retryAttempt = retryAttempt; + } + + // Getters + public String getSagaId() { return sagaId; } + public String getCustomerId() { return customerId; } + public Double getAmount() { return amount; } + public String getPaymentMethod() { return paymentMethod; } + public int getRetryAttempt() { return retryAttempt; } + } + + private static class PaymentCompensationEvent { + private String sagaId; + private Integer paymentId; + + public PaymentCompensationEvent(String sagaId, Integer paymentId) { + this.sagaId = sagaId; + this.paymentId = paymentId; + } + + // Getters + public String getSagaId() { return sagaId; } + public Integer getPaymentId() { return paymentId; } + } + + private static class InventoryCompensationEvent { + private String sagaId; + private List products; + + public InventoryCompensationEvent(String sagaId, List products) { + this.sagaId = sagaId; + this.products = products; + } + + // Getters + public String getSagaId() { return sagaId; } + public List getProducts() { return products; } + } +} diff --git a/services/order/src/main/java/com/pramithamj/ecommerce/saga/SagaStatus.java b/services/order/src/main/java/com/pramithamj/ecommerce/saga/SagaStatus.java new file mode 100644 index 0000000..1ae1fd4 --- /dev/null +++ b/services/order/src/main/java/com/pramithamj/ecommerce/saga/SagaStatus.java @@ -0,0 +1,13 @@ +package com.pramithamj.ecommerce.saga; + +public enum SagaStatus { + STARTED, + CUSTOMER_VALIDATED, + INVENTORY_RESERVED, + PAYMENT_PROCESSED, + ORDER_CONFIRMED, + COMPLETED, + COMPENSATING, + CANCELLED, + FAILED +} diff --git a/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/CustomerValidationEvent.java b/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/CustomerValidationEvent.java new file mode 100644 index 0000000..1027781 --- /dev/null +++ b/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/CustomerValidationEvent.java @@ -0,0 +1,15 @@ +package com.pramithamj.ecommerce.saga.events; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CustomerValidationEvent { + private String sagaId; + private String customerId; +} diff --git a/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/InventoryCompensationEvent.java b/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/InventoryCompensationEvent.java new file mode 100644 index 0000000..7a79775 --- /dev/null +++ b/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/InventoryCompensationEvent.java @@ -0,0 +1,18 @@ +package com.pramithamj.ecommerce.saga.events; + +import com.pramithamj.ecommerce.saga.OrderSaga; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InventoryCompensationEvent { + private String sagaId; + private List products; +} diff --git a/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/InventoryReservationEvent.java b/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/InventoryReservationEvent.java new file mode 100644 index 0000000..f631821 --- /dev/null +++ b/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/InventoryReservationEvent.java @@ -0,0 +1,18 @@ +package com.pramithamj.ecommerce.saga.events; + +import com.pramithamj.ecommerce.saga.OrderSaga; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InventoryReservationEvent { + private String sagaId; + private List products; +} diff --git a/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/OrderCompletionEvent.java b/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/OrderCompletionEvent.java new file mode 100644 index 0000000..3b739cc --- /dev/null +++ b/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/OrderCompletionEvent.java @@ -0,0 +1,21 @@ +package com.pramithamj.ecommerce.saga.events; + +import com.pramithamj.ecommerce.saga.OrderSaga; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OrderCompletionEvent { + private String sagaId; + private Integer orderId; + private String customerId; + private List products; + private Integer paymentId; +} diff --git a/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/PaymentCompensationEvent.java b/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/PaymentCompensationEvent.java new file mode 100644 index 0000000..2048c0e --- /dev/null +++ b/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/PaymentCompensationEvent.java @@ -0,0 +1,15 @@ +package com.pramithamj.ecommerce.saga.events; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PaymentCompensationEvent { + private String sagaId; + private Integer paymentId; +} diff --git a/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/PaymentProcessingEvent.java b/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/PaymentProcessingEvent.java new file mode 100644 index 0000000..142307b --- /dev/null +++ b/services/order/src/main/java/com/pramithamj/ecommerce/saga/events/PaymentProcessingEvent.java @@ -0,0 +1,17 @@ +package com.pramithamj.ecommerce.saga.events; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PaymentProcessingEvent { + private String sagaId; + private String customerId; + private Double amount; + private String paymentMethod; +} diff --git a/services/order/src/main/java/com/pramithamj/ecommerce/saga/handlers/CustomerValidationEventHandler.java b/services/order/src/main/java/com/pramithamj/ecommerce/saga/handlers/CustomerValidationEventHandler.java new file mode 100644 index 0000000..901bf69 --- /dev/null +++ b/services/order/src/main/java/com/pramithamj/ecommerce/saga/handlers/CustomerValidationEventHandler.java @@ -0,0 +1,46 @@ +package com.pramithamj.ecommerce.saga.handlers; + +import com.pramithamj.ecommerce.saga.OrderSagaOrchestrator; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +@Slf4j +public class CustomerValidationEventHandler { + + private final OrderSagaOrchestrator sagaOrchestrator; + + @KafkaListener(topics = "customer-validation-response", groupId = "order-saga-group") + public void handleCustomerValidationResponse(CustomerValidationResponse response) { + log.info("Received customer validation response for saga: {}", response.getSagaId()); + + if (response.isValid()) { + sagaOrchestrator.handleCustomerValidationSuccess(response.getSagaId(), response.getCustomerId()); + } else { + sagaOrchestrator.handleCustomerValidationFailure(response.getSagaId(), response.getErrorMessage()); + } + } + + public static class CustomerValidationResponse { + private String sagaId; + private String customerId; + private boolean valid; + private String errorMessage; + + // Getters and setters + public String getSagaId() { return sagaId; } + public void setSagaId(String sagaId) { this.sagaId = sagaId; } + + public String getCustomerId() { return customerId; } + public void setCustomerId(String customerId) { this.customerId = customerId; } + + public boolean isValid() { return valid; } + public void setValid(boolean valid) { this.valid = valid; } + + public String getErrorMessage() { return errorMessage; } + public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } + } +} diff --git a/services/order/src/main/java/com/pramithamj/ecommerce/saga/handlers/InventoryReservationEventHandler.java b/services/order/src/main/java/com/pramithamj/ecommerce/saga/handlers/InventoryReservationEventHandler.java new file mode 100644 index 0000000..82ded79 --- /dev/null +++ b/services/order/src/main/java/com/pramithamj/ecommerce/saga/handlers/InventoryReservationEventHandler.java @@ -0,0 +1,42 @@ +package com.pramithamj.ecommerce.saga.handlers; + +import com.pramithamj.ecommerce.saga.OrderSagaOrchestrator; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +@Slf4j +public class InventoryReservationEventHandler { + + private final OrderSagaOrchestrator sagaOrchestrator; + + @KafkaListener(topics = "inventory-reservation-response", groupId = "order-saga-group") + public void handleInventoryReservationResponse(InventoryReservationResponse response) { + log.info("Received inventory reservation response for saga: {}", response.getSagaId()); + + if (response.isReserved()) { + sagaOrchestrator.handleInventoryReservationSuccess(response.getSagaId()); + } else { + sagaOrchestrator.handleInventoryReservationFailure(response.getSagaId(), response.getErrorMessage()); + } + } + + public static class InventoryReservationResponse { + private String sagaId; + private boolean reserved; + private String errorMessage; + + // Getters and setters + public String getSagaId() { return sagaId; } + public void setSagaId(String sagaId) { this.sagaId = sagaId; } + + public boolean isReserved() { return reserved; } + public void setReserved(boolean reserved) { this.reserved = reserved; } + + public String getErrorMessage() { return errorMessage; } + public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } + } +} diff --git a/services/order/src/main/java/com/pramithamj/ecommerce/saga/handlers/PaymentEventHandler.java b/services/order/src/main/java/com/pramithamj/ecommerce/saga/handlers/PaymentEventHandler.java new file mode 100644 index 0000000..bc44d24 --- /dev/null +++ b/services/order/src/main/java/com/pramithamj/ecommerce/saga/handlers/PaymentEventHandler.java @@ -0,0 +1,81 @@ +package com.pramithamj.ecommerce.saga.handlers; + +import com.pramithamj.ecommerce.saga.OrderSagaOrchestrator; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +@Slf4j +public class PaymentEventHandler { + + private final OrderSagaOrchestrator sagaOrchestrator; + + @KafkaListener(topics = "payment-processing-response", groupId = "order-saga-group") + public void handlePaymentProcessingResponse(PaymentProcessingResponse response) { + log.info("Received payment processing response for saga: {}", response.getSagaId()); + + if (response.isSuccess()) { + sagaOrchestrator.handlePaymentProcessingSuccess(response.getSagaId(), response.getPaymentId()); + } else { + sagaOrchestrator.handlePaymentProcessingFailure(response.getSagaId(), response.getErrorMessage()); + } + } + + @KafkaListener(topics = "payment-compensation-response", groupId = "order-saga-group") + public void handlePaymentCompensationResponse(PaymentCompensationResponse response) { + log.info("Received payment compensation response for saga: {}", response.getSagaId()); + + if (response.isSuccess()) { + log.info("Payment compensation completed for saga: {}", response.getSagaId()); + } else { + log.error("Payment compensation failed for saga: {}: {}", response.getSagaId(), response.getErrorMessage()); + } + } + + public static class PaymentProcessingResponse { + private String sagaId; + private Integer paymentId; + private boolean success; + private String errorMessage; + private Double amount; + + // Getters and setters + public String getSagaId() { return sagaId; } + public void setSagaId(String sagaId) { this.sagaId = sagaId; } + + public Integer getPaymentId() { return paymentId; } + public void setPaymentId(Integer paymentId) { this.paymentId = paymentId; } + + public boolean isSuccess() { return success; } + public void setSuccess(boolean success) { this.success = success; } + + public String getErrorMessage() { return errorMessage; } + public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } + + public Double getAmount() { return amount; } + public void setAmount(Double amount) { this.amount = amount; } + } + + public static class PaymentCompensationResponse { + private String sagaId; + private Integer paymentId; + private boolean success; + private String errorMessage; + + // Getters and setters + public String getSagaId() { return sagaId; } + public void setSagaId(String sagaId) { this.sagaId = sagaId; } + + public Integer getPaymentId() { return paymentId; } + public void setPaymentId(Integer paymentId) { this.paymentId = paymentId; } + + public boolean isSuccess() { return success; } + public void setSuccess(boolean success) { this.success = success; } + + public String getErrorMessage() { return errorMessage; } + public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } + } +} diff --git a/services/order/src/main/java/com/pramithamj/ecommerce/saga/handlers/PaymentProcessingEventHandler.java b/services/order/src/main/java/com/pramithamj/ecommerce/saga/handlers/PaymentProcessingEventHandler.java new file mode 100644 index 0000000..f5f1339 --- /dev/null +++ b/services/order/src/main/java/com/pramithamj/ecommerce/saga/handlers/PaymentProcessingEventHandler.java @@ -0,0 +1,46 @@ +package com.pramithamj.ecommerce.saga.handlers; + +import com.pramithamj.ecommerce.saga.OrderSagaOrchestrator; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +@Slf4j +public class PaymentProcessingEventHandler { + + private final OrderSagaOrchestrator sagaOrchestrator; + + @KafkaListener(topics = "payment-processing-response", groupId = "order-saga-group") + public void handlePaymentProcessingResponse(PaymentProcessingResponse response) { + log.info("Received payment processing response for saga: {}", response.getSagaId()); + + if (response.isProcessed()) { + sagaOrchestrator.handlePaymentProcessingSuccess(response.getSagaId(), response.getPaymentId()); + } else { + sagaOrchestrator.handlePaymentProcessingFailure(response.getSagaId(), response.getErrorMessage()); + } + } + + public static class PaymentProcessingResponse { + private String sagaId; + private Integer paymentId; + private boolean processed; + private String errorMessage; + + // Getters and setters + public String getSagaId() { return sagaId; } + public void setSagaId(String sagaId) { this.sagaId = sagaId; } + + public Integer getPaymentId() { return paymentId; } + public void setPaymentId(Integer paymentId) { this.paymentId = paymentId; } + + public boolean isProcessed() { return processed; } + public void setProcessed(boolean processed) { this.processed = processed; } + + public String getErrorMessage() { return errorMessage; } + public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } + } +} diff --git a/services/order/src/test/java/com/alibou/ecommerce/OrderApplicationTests.java b/services/order/src/test/java/com/pramithamj/ecommerce/OrderApplicationTests.java similarity index 84% rename from services/order/src/test/java/com/alibou/ecommerce/OrderApplicationTests.java rename to services/order/src/test/java/com/pramithamj/ecommerce/OrderApplicationTests.java index 5f312ec..310b349 100644 --- a/services/order/src/test/java/com/alibou/ecommerce/OrderApplicationTests.java +++ b/services/order/src/test/java/com/pramithamj/ecommerce/OrderApplicationTests.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce; +package com.pramithamj.ecommerce; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; diff --git a/services/payment/pom.xml b/services/payment/pom.xml index e1097bf..0f26207 100644 --- a/services/payment/pom.xml +++ b/services/payment/pom.xml @@ -8,7 +8,7 @@ 3.2.5 - com.alibou + com.pramithamj payment 0.0.1-SNAPSHOT payment diff --git a/services/payment/src/main/java/com/alibou/ecommerce/PaymentApplication.java b/services/payment/src/main/java/com/pramithamj/ecommerce/PaymentApplication.java similarity index 91% rename from services/payment/src/main/java/com/alibou/ecommerce/PaymentApplication.java rename to services/payment/src/main/java/com/pramithamj/ecommerce/PaymentApplication.java index f2e057b..7768c70 100644 --- a/services/payment/src/main/java/com/alibou/ecommerce/PaymentApplication.java +++ b/services/payment/src/main/java/com/pramithamj/ecommerce/PaymentApplication.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce; +package com.pramithamj.ecommerce; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/services/payment/src/main/java/com/alibou/ecommerce/configuration/KafkaPaymentTopicConfig.java b/services/payment/src/main/java/com/pramithamj/ecommerce/configuration/KafkaPaymentTopicConfig.java similarity index 89% rename from services/payment/src/main/java/com/alibou/ecommerce/configuration/KafkaPaymentTopicConfig.java rename to services/payment/src/main/java/com/pramithamj/ecommerce/configuration/KafkaPaymentTopicConfig.java index ccad582..1d32cca 100644 --- a/services/payment/src/main/java/com/alibou/ecommerce/configuration/KafkaPaymentTopicConfig.java +++ b/services/payment/src/main/java/com/pramithamj/ecommerce/configuration/KafkaPaymentTopicConfig.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.configuration; +package com.pramithamj.ecommerce.configuration; import org.apache.kafka.clients.admin.NewTopic; import org.springframework.context.annotation.Bean; diff --git a/services/payment/src/main/java/com/alibou/ecommerce/exception/BusinessException.java b/services/payment/src/main/java/com/pramithamj/ecommerce/exception/BusinessException.java similarity index 80% rename from services/payment/src/main/java/com/alibou/ecommerce/exception/BusinessException.java rename to services/payment/src/main/java/com/pramithamj/ecommerce/exception/BusinessException.java index 04a75fd..181400a 100644 --- a/services/payment/src/main/java/com/alibou/ecommerce/exception/BusinessException.java +++ b/services/payment/src/main/java/com/pramithamj/ecommerce/exception/BusinessException.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.exception; +package com.pramithamj.ecommerce.exception; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/services/order/src/main/java/com/alibou/ecommerce/handler/ErrorResponse.java b/services/payment/src/main/java/com/pramithamj/ecommerce/handler/ErrorResponse.java similarity index 68% rename from services/order/src/main/java/com/alibou/ecommerce/handler/ErrorResponse.java rename to services/payment/src/main/java/com/pramithamj/ecommerce/handler/ErrorResponse.java index e1d0cf9..f9d2616 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/handler/ErrorResponse.java +++ b/services/payment/src/main/java/com/pramithamj/ecommerce/handler/ErrorResponse.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.handler; +package com.pramithamj.ecommerce.handler; import java.util.Map; diff --git a/services/payment/src/main/java/com/alibou/ecommerce/handler/GlobalExceptionHandler.java b/services/payment/src/main/java/com/pramithamj/ecommerce/handler/GlobalExceptionHandler.java similarity index 93% rename from services/payment/src/main/java/com/alibou/ecommerce/handler/GlobalExceptionHandler.java rename to services/payment/src/main/java/com/pramithamj/ecommerce/handler/GlobalExceptionHandler.java index 64bce5f..20d3e8f 100644 --- a/services/payment/src/main/java/com/alibou/ecommerce/handler/GlobalExceptionHandler.java +++ b/services/payment/src/main/java/com/pramithamj/ecommerce/handler/GlobalExceptionHandler.java @@ -1,6 +1,6 @@ -package com.alibou.ecommerce.handler; +package com.pramithamj.ecommerce.handler; -import com.alibou.ecommerce.exception.BusinessException; +import com.pramithamj.ecommerce.exception.BusinessException; import jakarta.persistence.EntityNotFoundException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/services/payment/src/main/java/com/alibou/ecommerce/notification/NotificationProducer.java b/services/payment/src/main/java/com/pramithamj/ecommerce/notification/NotificationProducer.java similarity index 94% rename from services/payment/src/main/java/com/alibou/ecommerce/notification/NotificationProducer.java rename to services/payment/src/main/java/com/pramithamj/ecommerce/notification/NotificationProducer.java index fdc7bc1..6299548 100644 --- a/services/payment/src/main/java/com/alibou/ecommerce/notification/NotificationProducer.java +++ b/services/payment/src/main/java/com/pramithamj/ecommerce/notification/NotificationProducer.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.notification; +package com.pramithamj.ecommerce.notification; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/services/payment/src/main/java/com/alibou/ecommerce/notification/PaymentNotificationRequest.java b/services/payment/src/main/java/com/pramithamj/ecommerce/notification/PaymentNotificationRequest.java similarity index 72% rename from services/payment/src/main/java/com/alibou/ecommerce/notification/PaymentNotificationRequest.java rename to services/payment/src/main/java/com/pramithamj/ecommerce/notification/PaymentNotificationRequest.java index f291640..9691af1 100644 --- a/services/payment/src/main/java/com/alibou/ecommerce/notification/PaymentNotificationRequest.java +++ b/services/payment/src/main/java/com/pramithamj/ecommerce/notification/PaymentNotificationRequest.java @@ -1,6 +1,6 @@ -package com.alibou.ecommerce.notification; +package com.pramithamj.ecommerce.notification; -import com.alibou.ecommerce.payment.PaymentMethod; +import com.pramithamj.ecommerce.payment.PaymentMethod; import java.math.BigDecimal; diff --git a/services/payment/src/main/java/com/alibou/ecommerce/payment/Customer.java b/services/payment/src/main/java/com/pramithamj/ecommerce/payment/Customer.java similarity index 92% rename from services/payment/src/main/java/com/alibou/ecommerce/payment/Customer.java rename to services/payment/src/main/java/com/pramithamj/ecommerce/payment/Customer.java index 03fa96a..a5a58a7 100644 --- a/services/payment/src/main/java/com/alibou/ecommerce/payment/Customer.java +++ b/services/payment/src/main/java/com/pramithamj/ecommerce/payment/Customer.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.payment; +package com.pramithamj.ecommerce.payment; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotNull; diff --git a/services/payment/src/main/java/com/alibou/ecommerce/payment/Payment.java b/services/payment/src/main/java/com/pramithamj/ecommerce/payment/Payment.java similarity index 96% rename from services/payment/src/main/java/com/alibou/ecommerce/payment/Payment.java rename to services/payment/src/main/java/com/pramithamj/ecommerce/payment/Payment.java index 3653957..392e931 100644 --- a/services/payment/src/main/java/com/alibou/ecommerce/payment/Payment.java +++ b/services/payment/src/main/java/com/pramithamj/ecommerce/payment/Payment.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.payment; +package com.pramithamj.ecommerce.payment; import jakarta.persistence.Column; diff --git a/services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentController.java b/services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentController.java similarity index 94% rename from services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentController.java rename to services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentController.java index 90f67cc..ee741f1 100644 --- a/services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentController.java +++ b/services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentController.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.payment; +package com.pramithamj.ecommerce.payment; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; diff --git a/services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentMapper.java b/services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentMapper.java similarity index 90% rename from services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentMapper.java rename to services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentMapper.java index 40ad350..249b9db 100644 --- a/services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentMapper.java +++ b/services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentMapper.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.payment; +package com.pramithamj.ecommerce.payment; import org.springframework.stereotype.Service; diff --git a/services/order/src/main/java/com/alibou/ecommerce/order/PaymentMethod.java b/services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentMethod.java similarity index 68% rename from services/order/src/main/java/com/alibou/ecommerce/order/PaymentMethod.java rename to services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentMethod.java index edb0057..86bd200 100644 --- a/services/order/src/main/java/com/alibou/ecommerce/order/PaymentMethod.java +++ b/services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentMethod.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.order; +package com.pramithamj.ecommerce.payment; public enum PaymentMethod { diff --git a/services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentRepository.java b/services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentRepository.java similarity index 77% rename from services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentRepository.java rename to services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentRepository.java index 454570c..26e50e8 100644 --- a/services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentRepository.java +++ b/services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentRepository.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.payment; +package com.pramithamj.ecommerce.payment; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentRequest.java b/services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentRequest.java similarity index 83% rename from services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentRequest.java rename to services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentRequest.java index e0b8133..cd12e8a 100644 --- a/services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentRequest.java +++ b/services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentRequest.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.payment; +package com.pramithamj.ecommerce.payment; import java.math.BigDecimal; diff --git a/services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentService.java b/services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentService.java similarity index 82% rename from services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentService.java rename to services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentService.java index 7113e0d..41190b5 100644 --- a/services/payment/src/main/java/com/alibou/ecommerce/payment/PaymentService.java +++ b/services/payment/src/main/java/com/pramithamj/ecommerce/payment/PaymentService.java @@ -1,7 +1,7 @@ -package com.alibou.ecommerce.payment; +package com.pramithamj.ecommerce.payment; -import com.alibou.ecommerce.notification.NotificationProducer; -import com.alibou.ecommerce.notification.PaymentNotificationRequest; +import com.pramithamj.ecommerce.notification.NotificationProducer; +import com.pramithamj.ecommerce.notification.PaymentNotificationRequest; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/services/payment/src/test/java/com/alibou/ecommerce/PaymentApplicationTests.java b/services/payment/src/test/java/com/pramithamj/ecommerce/PaymentApplicationTests.java similarity index 84% rename from services/payment/src/test/java/com/alibou/ecommerce/PaymentApplicationTests.java rename to services/payment/src/test/java/com/pramithamj/ecommerce/PaymentApplicationTests.java index 7eaeb05..8bb91cb 100644 --- a/services/payment/src/test/java/com/alibou/ecommerce/PaymentApplicationTests.java +++ b/services/payment/src/test/java/com/pramithamj/ecommerce/PaymentApplicationTests.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce; +package com.pramithamj.ecommerce; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; diff --git a/services/product/pom.xml b/services/product/pom.xml index 3d5250e..ec3177a 100644 --- a/services/product/pom.xml +++ b/services/product/pom.xml @@ -8,7 +8,7 @@ 3.2.5 - com.alibou + com.pramithamj product 0.0.1-SNAPSHOT product diff --git a/services/product/src/main/java/com/alibou/ecommerce/ProductApplication.java b/services/product/src/main/java/com/pramithamj/ecommerce/ProductApplication.java similarity index 89% rename from services/product/src/main/java/com/alibou/ecommerce/ProductApplication.java rename to services/product/src/main/java/com/pramithamj/ecommerce/ProductApplication.java index dc5bb64..3897eb9 100644 --- a/services/product/src/main/java/com/alibou/ecommerce/ProductApplication.java +++ b/services/product/src/main/java/com/pramithamj/ecommerce/ProductApplication.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce; +package com.pramithamj.ecommerce; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/services/product/src/main/java/com/alibou/ecommerce/category/Category.java b/services/product/src/main/java/com/pramithamj/ecommerce/category/Category.java similarity index 87% rename from services/product/src/main/java/com/alibou/ecommerce/category/Category.java rename to services/product/src/main/java/com/pramithamj/ecommerce/category/Category.java index d256617..4958ea3 100644 --- a/services/product/src/main/java/com/alibou/ecommerce/category/Category.java +++ b/services/product/src/main/java/com/pramithamj/ecommerce/category/Category.java @@ -1,6 +1,6 @@ -package com.alibou.ecommerce.category; +package com.pramithamj.ecommerce.category; -import com.alibou.ecommerce.product.Product; +import com.pramithamj.ecommerce.product.Product; import jakarta.persistence.CascadeType; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; diff --git a/services/product/src/main/java/com/alibou/ecommerce/exception/ProductPurchaseException.java b/services/product/src/main/java/com/pramithamj/ecommerce/exception/ProductPurchaseException.java similarity index 76% rename from services/product/src/main/java/com/alibou/ecommerce/exception/ProductPurchaseException.java rename to services/product/src/main/java/com/pramithamj/ecommerce/exception/ProductPurchaseException.java index 9e58a0f..01d9de0 100644 --- a/services/product/src/main/java/com/alibou/ecommerce/exception/ProductPurchaseException.java +++ b/services/product/src/main/java/com/pramithamj/ecommerce/exception/ProductPurchaseException.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.exception; +package com.pramithamj.ecommerce.exception; public class ProductPurchaseException extends RuntimeException { public ProductPurchaseException(String s) { diff --git a/services/payment/src/main/java/com/alibou/ecommerce/handler/ErrorResponse.java b/services/product/src/main/java/com/pramithamj/ecommerce/handler/ErrorResponse.java similarity index 68% rename from services/payment/src/main/java/com/alibou/ecommerce/handler/ErrorResponse.java rename to services/product/src/main/java/com/pramithamj/ecommerce/handler/ErrorResponse.java index e1d0cf9..f9d2616 100644 --- a/services/payment/src/main/java/com/alibou/ecommerce/handler/ErrorResponse.java +++ b/services/product/src/main/java/com/pramithamj/ecommerce/handler/ErrorResponse.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.handler; +package com.pramithamj.ecommerce.handler; import java.util.Map; diff --git a/services/product/src/main/java/com/alibou/ecommerce/handler/GlobalExceptionHandler.java b/services/product/src/main/java/com/pramithamj/ecommerce/handler/GlobalExceptionHandler.java similarity index 93% rename from services/product/src/main/java/com/alibou/ecommerce/handler/GlobalExceptionHandler.java rename to services/product/src/main/java/com/pramithamj/ecommerce/handler/GlobalExceptionHandler.java index 53d4f67..f5a3c42 100644 --- a/services/product/src/main/java/com/alibou/ecommerce/handler/GlobalExceptionHandler.java +++ b/services/product/src/main/java/com/pramithamj/ecommerce/handler/GlobalExceptionHandler.java @@ -1,6 +1,6 @@ -package com.alibou.ecommerce.handler; +package com.pramithamj.ecommerce.handler; -import com.alibou.ecommerce.exception.ProductPurchaseException; +import com.pramithamj.ecommerce.exception.ProductPurchaseException; import jakarta.persistence.EntityNotFoundException; import org.springframework.http.ResponseEntity; import org.springframework.validation.FieldError; diff --git a/services/product/src/main/java/com/alibou/ecommerce/product/Product.java b/services/product/src/main/java/com/pramithamj/ecommerce/product/Product.java similarity index 88% rename from services/product/src/main/java/com/alibou/ecommerce/product/Product.java rename to services/product/src/main/java/com/pramithamj/ecommerce/product/Product.java index 06a9493..0624d09 100644 --- a/services/product/src/main/java/com/alibou/ecommerce/product/Product.java +++ b/services/product/src/main/java/com/pramithamj/ecommerce/product/Product.java @@ -1,6 +1,6 @@ -package com.alibou.ecommerce.product; +package com.pramithamj.ecommerce.product; -import com.alibou.ecommerce.category.Category; +import com.pramithamj.ecommerce.category.Category; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; diff --git a/services/product/src/main/java/com/alibou/ecommerce/product/ProductController.java b/services/product/src/main/java/com/pramithamj/ecommerce/product/ProductController.java similarity index 97% rename from services/product/src/main/java/com/alibou/ecommerce/product/ProductController.java rename to services/product/src/main/java/com/pramithamj/ecommerce/product/ProductController.java index 412af67..3ad57c5 100644 --- a/services/product/src/main/java/com/alibou/ecommerce/product/ProductController.java +++ b/services/product/src/main/java/com/pramithamj/ecommerce/product/ProductController.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.product; +package com.pramithamj.ecommerce.product; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; diff --git a/services/product/src/main/java/com/alibou/ecommerce/product/ProductMapper.java b/services/product/src/main/java/com/pramithamj/ecommerce/product/ProductMapper.java similarity index 93% rename from services/product/src/main/java/com/alibou/ecommerce/product/ProductMapper.java rename to services/product/src/main/java/com/pramithamj/ecommerce/product/ProductMapper.java index ae5a47c..006a84c 100644 --- a/services/product/src/main/java/com/alibou/ecommerce/product/ProductMapper.java +++ b/services/product/src/main/java/com/pramithamj/ecommerce/product/ProductMapper.java @@ -1,6 +1,6 @@ -package com.alibou.ecommerce.product; +package com.pramithamj.ecommerce.product; -import com.alibou.ecommerce.category.Category; +import com.pramithamj.ecommerce.category.Category; import org.springframework.stereotype.Service; @Service diff --git a/services/product/src/main/java/com/alibou/ecommerce/product/ProductPurchaseRequest.java b/services/product/src/main/java/com/pramithamj/ecommerce/product/ProductPurchaseRequest.java similarity index 87% rename from services/product/src/main/java/com/alibou/ecommerce/product/ProductPurchaseRequest.java rename to services/product/src/main/java/com/pramithamj/ecommerce/product/ProductPurchaseRequest.java index c20f918..0be4169 100644 --- a/services/product/src/main/java/com/alibou/ecommerce/product/ProductPurchaseRequest.java +++ b/services/product/src/main/java/com/pramithamj/ecommerce/product/ProductPurchaseRequest.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.product; +package com.pramithamj.ecommerce.product; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; diff --git a/services/product/src/main/java/com/alibou/ecommerce/product/ProductPurchaseResponse.java b/services/product/src/main/java/com/pramithamj/ecommerce/product/ProductPurchaseResponse.java similarity index 82% rename from services/product/src/main/java/com/alibou/ecommerce/product/ProductPurchaseResponse.java rename to services/product/src/main/java/com/pramithamj/ecommerce/product/ProductPurchaseResponse.java index 7e2a4bb..79b9e34 100644 --- a/services/product/src/main/java/com/alibou/ecommerce/product/ProductPurchaseResponse.java +++ b/services/product/src/main/java/com/pramithamj/ecommerce/product/ProductPurchaseResponse.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.product; +package com.pramithamj.ecommerce.product; import java.math.BigDecimal; diff --git a/services/product/src/main/java/com/alibou/ecommerce/product/ProductRepository.java b/services/product/src/main/java/com/pramithamj/ecommerce/product/ProductRepository.java similarity index 84% rename from services/product/src/main/java/com/alibou/ecommerce/product/ProductRepository.java rename to services/product/src/main/java/com/pramithamj/ecommerce/product/ProductRepository.java index 2203791..f462ed1 100644 --- a/services/product/src/main/java/com/alibou/ecommerce/product/ProductRepository.java +++ b/services/product/src/main/java/com/pramithamj/ecommerce/product/ProductRepository.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.product; +package com.pramithamj.ecommerce.product; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/services/product/src/main/java/com/alibou/ecommerce/product/ProductRequest.java b/services/product/src/main/java/com/pramithamj/ecommerce/product/ProductRequest.java similarity index 93% rename from services/product/src/main/java/com/alibou/ecommerce/product/ProductRequest.java rename to services/product/src/main/java/com/pramithamj/ecommerce/product/ProductRequest.java index 7d9e80e..49a1bd6 100644 --- a/services/product/src/main/java/com/alibou/ecommerce/product/ProductRequest.java +++ b/services/product/src/main/java/com/pramithamj/ecommerce/product/ProductRequest.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.product; +package com.pramithamj.ecommerce.product; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; diff --git a/services/product/src/main/java/com/alibou/ecommerce/product/ProductResponse.java b/services/product/src/main/java/com/pramithamj/ecommerce/product/ProductResponse.java similarity index 87% rename from services/product/src/main/java/com/alibou/ecommerce/product/ProductResponse.java rename to services/product/src/main/java/com/pramithamj/ecommerce/product/ProductResponse.java index 9cd484f..9bb96f1 100644 --- a/services/product/src/main/java/com/alibou/ecommerce/product/ProductResponse.java +++ b/services/product/src/main/java/com/pramithamj/ecommerce/product/ProductResponse.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce.product; +package com.pramithamj.ecommerce.product; import java.math.BigDecimal; diff --git a/services/product/src/main/java/com/alibou/ecommerce/product/ProductService.java b/services/product/src/main/java/com/pramithamj/ecommerce/product/ProductService.java similarity index 96% rename from services/product/src/main/java/com/alibou/ecommerce/product/ProductService.java rename to services/product/src/main/java/com/pramithamj/ecommerce/product/ProductService.java index 26346ed..3f78e38 100644 --- a/services/product/src/main/java/com/alibou/ecommerce/product/ProductService.java +++ b/services/product/src/main/java/com/pramithamj/ecommerce/product/ProductService.java @@ -1,6 +1,6 @@ -package com.alibou.ecommerce.product; +package com.pramithamj.ecommerce.product; -import com.alibou.ecommerce.exception.ProductPurchaseException; +import com.pramithamj.ecommerce.exception.ProductPurchaseException; import jakarta.persistence.EntityNotFoundException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/services/product/src/test/java/com/alibou/ecommerce/ProductApplicationTests.java b/services/product/src/test/java/com/pramithamj/ecommerce/ProductApplicationTests.java similarity index 84% rename from services/product/src/test/java/com/alibou/ecommerce/ProductApplicationTests.java rename to services/product/src/test/java/com/pramithamj/ecommerce/ProductApplicationTests.java index efd7d68..6d21338 100644 --- a/services/product/src/test/java/com/alibou/ecommerce/ProductApplicationTests.java +++ b/services/product/src/test/java/com/pramithamj/ecommerce/ProductApplicationTests.java @@ -1,4 +1,4 @@ -package com.alibou.ecommerce; +package com.pramithamj.ecommerce; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; From 15b31b92565f17abb18052d8442b4bc54e49a7df Mon Sep 17 00:00:00 2001 From: Pramitha Jayasooriya Date: Sun, 10 Aug 2025 22:40:29 +0530 Subject: [PATCH 2/3] refactor: Update README and index.html for improved clarity and organization --- README.md | 41 +-- learning-platform/index.html | 495 ++++++++--------------------------- 2 files changed, 130 insertions(+), 406 deletions(-) diff --git a/README.md b/README.md index 6887fa6..80802ff 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# πŸ—οΈ Complete Microservices Architecture with Advanced Patterns +# Complete Microservices Architecture with Advanced Patterns [![Java](https://img.shields.io/badge/Java-17-orange)](https://www.oracle.com/java/) [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.2.5-brightgreen)](https://spring.io/projects/spring-boot) @@ -9,11 +9,11 @@ > A comprehensive, production-ready microservices implementation featuring **Circuit Breaker Pattern**, **Saga Pattern**, and **Blue-Green Deployment** with Kubernetes orchestration. -## 🎯 Project Overview +## Project Overview This project demonstrates enterprise-grade microservices architecture patterns through a complete e-commerce platform implementation. It showcases advanced distributed systems patterns essential for building resilient, scalable applications. -### πŸ† Key Features +### Key Features - βœ… **6 Production-Ready Microservices** - βœ… **Circuit Breaker Pattern** with Resilience4j @@ -24,21 +24,24 @@ This project demonstrates enterprise-grade microservices architecture patterns t - βœ… **Interactive Learning Platform** - βœ… **Zero-Downtime Deployments** -## πŸ—οΈ Advanced Patterns Implemented +## Advanced Patterns Implemented + +### Circuit Breaker Pattern -### πŸ›‘οΈ Circuit Breaker Pattern - **Technology**: Resilience4j - **Implementation**: All inter-service communications - **Features**: Configurable thresholds, automatic recovery, fallback strategies - **Monitoring**: Prometheus metrics integration -### πŸ”„ Saga Pattern (Orchestration) +### Saga Pattern (Orchestration) + - **Coordinator**: OrderSagaOrchestrator - **State Storage**: MongoDB - **Event Bus**: Apache Kafka - **Features**: Automatic compensation, timeout handling, retry mechanisms -### πŸ”΅πŸŸ’ Blue-Green Deployment +### Blue-Green Deployment + - **Platform**: Kubernetes with Helm - **Features**: Zero-downtime deployment, instant rollback, canary support - **Automation**: Custom deployment scripts with health checks @@ -50,35 +53,36 @@ This repository contains a collection of fully completed microservices built wit ## Microservices Description 1. **Config Server** + - Provides centralized configuration for all microservices. - Uses Spring Cloud Config Server. - 2. **Customer Service** + - Manages customer data and operations. - Integrated with Eureka Discovery. - 3. **Discovery Service** + - Service registry using Netflix Eureka. - Enables service discovery for other microservices. - 4. **Gateway Service** + - API Gateway for routing requests to appropriate microservices. - Uses Spring Cloud Gateway. - Includes distributed tracing and circuit breaker. - 5. **Notification Service** + - Handles notifications and alerts. - Uses Kafka for messaging. - 6. **Order Service** + - Manages orders and their statuses. - Integrated with Eureka Discovery. - 7. **Payment Service** + - Processes payments. - Uses Eureka Discovery and Zipkin for tracing. - 8. **Product Service** + - Manages product information. - Integrated with Eureka Discovery. @@ -100,23 +104,23 @@ This repository contains a collection of fully completed microservices built wit ## Running the Microservices 1. **Clone the repository** + ```sh git clone https://github.com/PramithaMJ/fully-completed-microservices.git cd fully-completed-microservices ``` - 2. **Start Config Server** + ```sh cd config-server mvn spring-boot:run ``` - 3. **Start Discovery Service** + ```sh cd discovery mvn spring-boot:run ``` - 4. **Start Other Microservices** Start the remaining microservices in any order. Ensure they are configured to register with the Discovery Service. @@ -162,10 +166,11 @@ https://pramithamj.live + ## Donation ***If you like what I do, maybe consider buying me a coffee*** Buy Me A Coffee -*** +--- diff --git a/learning-platform/index.html b/learning-platform/index.html index 15d0749..59cab61 100644 --- a/learning-platform/index.html +++ b/learning-platform/index.html @@ -922,394 +922,7 @@
Email Testing
- -
-
-
-
-

- - Complete Source Code Available -

-

- Access the complete, production-ready microservices implementation with detailed documentation, - configuration files, and step-by-step commit history. -

- -
-
-
-
    -
  • Complete Spring Boot 3.2.5 implementation
  • -
  • Docker Compose configuration
  • -
  • Detailed README documentation
  • -
  • Step-by-step commit history
  • -
-
-
-
    -
  • Production-ready configurations
  • -
  • Comprehensive testing examples
  • -
  • MIT License - Free to use
  • -
  • Active community support
  • -
-
-
-
-
- -
-
-
- -

Repository

-
-
-
fully-completed-microservices
-

Spring Boot Microservices Architecture

-
- - Java - - - Production Ready - - - Educational - -
-
- -
-
-
- - - -
-
- - -
-
-

Microservices Deep Dive

- - - - - -
- -
-
-
-
-
-

Config Server

-

Port: 8888

-

Technology: Spring Cloud Config

-

Database: File System

-
-
-
-
-
Purpose & Functionality
-

The Config Server acts as a centralized configuration management system for all microservices. It eliminates the need to rebuild and redeploy services when configuration changes.

- -
Key Features:
-
    -
  • Centralized configuration for all services
  • -
  • Environment-specific configurations (dev, staging, prod)
  • -
  • Dynamic configuration refresh without restart
  • -
  • Version control integration with Git
  • -
  • Security for sensitive configurations
  • -
- -
How it Works:
-
    -
  1. Stores all service configurations in a central location
  2. -
  3. Services request their configuration on startup
  4. -
  5. Provides environment-specific overrides
  6. -
  7. Enables runtime configuration updates via refresh endpoints
  8. -
-
-
-
-
-
- - -
-
-
-
-
-

Discovery Service

-

Port: 8761

-

Technology: Netflix Eureka

-

Database: In-Memory Registry

-
-
-
-
-
Service Registry & Discovery
-

The Discovery Service maintains a registry of all available microservice instances, enabling dynamic service-to-service communication without hardcoded URLs.

- -
Key Features:
-
    -
  • Automatic service registration
  • -
  • Health check monitoring
  • -
  • Load balancing support
  • -
  • Failover and resilience
  • -
  • Web dashboard for monitoring
  • -
- -
Service Registration Flow:
-
    -
  1. Services register themselves on startup
  2. -
  3. Send periodic heartbeats to maintain registration
  4. -
  5. Other services discover available instances
  6. -
  7. Automatic deregistration on shutdown
  8. -
-
-
-
-
-
- - -
-
-
-
-
-

API Gateway

-

Port: 8222

-

Technology: Spring Cloud Gateway

-

Features: Routing, Load Balancing, Tracing

-
-
-
-
-
Single Entry Point
-

The API Gateway serves as the single entry point for all client requests, routing them to appropriate microservices while providing cross-cutting concerns.

- -
Key Responsibilities:
-
    -
  • Request routing to appropriate services
  • -
  • Load balancing across service instances
  • -
  • Authentication and authorization
  • -
  • Request/response transformation
  • -
  • Rate limiting and throttling
  • -
  • Distributed tracing integration
  • -
- -
Routing Example:
-
- - /api/customers/** β†’ Customer Service
- /api/products/** β†’ Product Service
- /api/orders/** β†’ Order Service
- /api/payments/** β†’ Payment Service -
-
-
-
-
-
-
- - -
-
-
-
-
-

Customer Service

-

Database: MongoDB

-

Technology: Spring Data MongoDB

-

Pattern: Document Store

-
-
-
-
-
Customer Profile Management
-

Manages all customer-related data and operations, storing flexible customer profiles in MongoDB for scalability and schema evolution.

- -
Data Model:
-
    -
  • Personal Info: First name, Last name, Email
  • -
  • Address: Street, City, Zipcode
  • -
  • Metadata: Creation date, Update history
  • -
- -
API Endpoints:
-
-
- POST /api/customers - Create customer -
-
- GET /api/customers/{id} - Get customer details -
-
- PUT /api/customers/{id} - Update customer -
-
- GET /api/customers/exists/{id} - Check existence -
-
-
-
-
-
-
- - -
-
-
-
-
-

Order Service

-

Database: PostgreSQL

-

Technology: Spring Data JPA

-

Integration: OpenFeign, Kafka

-
-
-
-
-
Order Processing Hub
-

Orchestrates the complete order lifecycle, integrating with multiple services to validate customers, check product availability, process payments, and trigger notifications.

- -
Order Processing Flow:
-
    -
  1. Validation: Check customer exists via Customer Service
  2. -
  3. Inventory: Validate products and quantities via Product Service
  4. -
  5. Order Creation: Create order with PENDING status
  6. -
  7. Payment: Process payment via Payment Service
  8. -
  9. Confirmation: Send confirmation via Kafka to Notification Service
  10. -
  11. Status Update: Update order status based on payment result
  12. -
- -
Service Dependencies:
-
    -
  • CustomerClient: Customer validation
  • -
  • ProductClient: Product verification
  • -
  • PaymentClient: Payment processing
  • -
  • Kafka Producer: Event notifications
  • -
-
-
-
-
-
- - -
-
-
-
-
-

Notification Service

-

Database: MongoDB

-

Technology: Kafka, Thymeleaf

-

Integration: Java Mail Sender

-
-
-
-
-
Event-Driven Notifications
-

Handles all notification requirements using event-driven architecture. Listens to Kafka events and sends appropriate email notifications to customers.

- -
Notification Types:
-
    -
  • Order Confirmation: When order is successfully created
  • -
  • Payment Success: When payment is processed successfully
  • -
  • Payment Failure: When payment processing fails
  • -
  • Order Updates: Status changes and updates
  • -
- -
Event Processing Flow:
-
    -
  1. Listen to Kafka topics (order-topic, payment-topic)
  2. -
  3. Parse incoming event messages
  4. -
  5. Generate HTML email using Thymeleaf templates
  6. -
  7. Send email via configured SMTP server
  8. -
  9. Store notification history in MongoDB
  10. -
-
Email Templates:
-
    -
  • order-confirmation.html
  • -
  • payment-confirmation.html
  • -
  • payment-failed.html
  • -
-
-
-
-
-
-
-
-
@@ -1501,6 +1114,112 @@
Prerequisites:
+ +
+
+
+
+

+ + Complete Source Code Available +

+

+ Access the complete, production-ready microservices implementation with detailed documentation, + configuration files, and step-by-step commit history. +

+ +
+
+
+
    +
  • Complete Spring Boot 3.2.5 implementation
  • +
  • Docker Compose configuration
  • +
  • Detailed README documentation
  • +
  • Step-by-step commit history
  • +
+
+
+
    +
  • Production-ready configurations
  • +
  • Comprehensive testing examples
  • +
  • MIT License - Free to use
  • +
  • Active community support
  • +
+
+
+
+
+ +
+
+
+ +

Repository

+
+
+
fully-completed-microservices
+

Spring Boot Microservices Architecture

+
+ + Java + + + Production Ready + + + Educational + +
+
+ +
+
+
+ + + +
+
+