├── .idea ├── .gitignore ├── vcs.xml ├── aws.xml ├── encodings.xml ├── misc.xml ├── uiDesigner.xml └── dbnavigator.xml ├── payments-service ├── .idea │ ├── .gitignore │ ├── vcs.xml │ ├── aws.xml │ ├── misc.xml │ ├── encodings.xml │ ├── uiDesigner.xml │ └── dbnavigator.xml ├── README.md ├── src │ └── main │ │ ├── java │ │ └── br │ │ │ └── com │ │ │ └── souza │ │ │ └── eventsdrivenarchitecture │ │ │ ├── enums │ │ │ └── ValidPaymentTypeEnum.java │ │ │ ├── service │ │ │ ├── payment │ │ │ │ ├── PaymentStatusEnum.java │ │ │ │ ├── PaymentHistoricDTO.java │ │ │ │ └── PaymentService.java │ │ │ ├── sqs │ │ │ │ └── OrdersQueueConsumer.java │ │ │ └── sns │ │ │ │ └── AwsSnsService.java │ │ │ ├── dto │ │ │ ├── CustomErrorResponse.java │ │ │ ├── QueueMessageRequest.java │ │ │ ├── Product.java │ │ │ └── OrderResponse.java │ │ │ ├── PaymentsServiceApplication.java │ │ │ └── config │ │ │ ├── SQSConfig.java │ │ │ └── SNSConfig.java │ │ └── resources │ │ └── application.yml ├── .gitignore └── pom.xml ├── orders-service ├── README.md ├── src │ ├── main │ │ ├── java │ │ │ └── br │ │ │ │ └── com │ │ │ │ └── souza │ │ │ │ └── eventsdrivenarchitecture │ │ │ │ ├── controller │ │ │ │ ├── IOrderController.java │ │ │ │ ├── IProductController.java │ │ │ │ └── impl │ │ │ │ │ ├── OrderControllerImpl.java │ │ │ │ │ └── ProductControllerImpl.java │ │ │ │ ├── enums │ │ │ │ ├── PaymentStatusEnum.java │ │ │ │ └── PaymentTypeEnum.java │ │ │ │ ├── exceptions │ │ │ │ ├── OrderNotFoundException.java │ │ │ │ ├── ProductNotFoundException.java │ │ │ │ ├── InsufficientStockException.java │ │ │ │ └── ProductNameAlreadyExistsException.java │ │ │ │ ├── dto │ │ │ │ ├── CustomErrorResponse.java │ │ │ │ ├── ProductRequest.java │ │ │ │ ├── OrderRequest.java │ │ │ │ └── OrderResponse.java │ │ │ │ ├── OrdersServiceApplication.java │ │ │ │ ├── database │ │ │ │ ├── repository │ │ │ │ │ ├── IOrderRepository.java │ │ │ │ │ └── IProductRepository.java │ │ │ │ └── model │ │ │ │ │ ├── Product.java │ │ │ │ │ └── Order.java │ │ │ │ ├── config │ │ │ │ ├── SQSConfig.java │ │ │ │ └── SNSConfig.java │ │ │ │ ├── service │ │ │ │ ├── sns │ │ │ │ │ └── AwsSnsService.java │ │ │ │ ├── sqs │ │ │ │ │ └── ProcessedPaymentsQueueConsumer.java │ │ │ │ ├── product │ │ │ │ │ └── ProductService.java │ │ │ │ └── order │ │ │ │ │ └── OrderService.java │ │ │ │ └── handler │ │ │ │ └── GlobalExceptionHandler.java │ │ └── resources │ │ │ └── application.yml │ └── test │ │ └── java │ │ └── br │ │ └── com │ │ └── souza │ │ └── eventsdrivenarchitecture │ │ └── EventsDrivenArchitectureApplicationTests.java ├── .gitignore └── pom.xml ├── README.md ├── pom.xml ├── .gitignore └── payments-historic-lambda ├── .gitignore ├── pom.xml └── src └── main └── java └── br └── com └── souza └── paymentshistoriclambda └── PaymentsHistoricFunction.java /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /payments-service/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /orders-service/README.md: -------------------------------------------------------------------------------- 1 | # Event_Driven_Architecture_Example 2 | EDA (Events-Driven Architecture) example with Java, Spring, AWS SNS, AWS SQS and AWS Lambda. 3 | -------------------------------------------------------------------------------- /payments-service/README.md: -------------------------------------------------------------------------------- 1 | # Event_Driven_Architecture_Example 2 | EDA (Events-Driven Architecture) example with Java, Spring, AWS SNS, AWS SQS and AWS Lambda. 3 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/controller/IOrderController.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.controller; 2 | 3 | public interface IOrderController { 4 | } 5 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/controller/IProductController.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.controller; 2 | 3 | public interface IProductController { 4 | } 5 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/enums/PaymentStatusEnum.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.enums; 2 | 3 | public enum PaymentStatusEnum { 4 | 5 | PENDING 6 | 7 | } 8 | -------------------------------------------------------------------------------- /payments-service/src/main/java/br/com/souza/eventsdrivenarchitecture/enums/ValidPaymentTypeEnum.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.enums; 2 | 3 | public enum ValidPaymentTypeEnum { 4 | 5 | PIX, 6 | DEBIT_CARD 7 | 8 | } 9 | -------------------------------------------------------------------------------- /payments-service/src/main/java/br/com/souza/eventsdrivenarchitecture/service/payment/PaymentStatusEnum.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.service.payment; 2 | 3 | public enum PaymentStatusEnum { 4 | 5 | APPROVED, 6 | RECUSED 7 | 8 | } 9 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/enums/PaymentTypeEnum.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.enums; 2 | 3 | public enum PaymentTypeEnum { 4 | 5 | PIX, 6 | CREDIT_CARD, 7 | DEBIT_CARD, 8 | PAYPAL 9 | 10 | } 11 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/exceptions/OrderNotFoundException.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.exceptions; 2 | 3 | public class OrderNotFoundException extends Exception{ 4 | 5 | public OrderNotFoundException() { 6 | super(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/exceptions/ProductNotFoundException.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.exceptions; 2 | 3 | public class ProductNotFoundException extends Exception{ 4 | 5 | public ProductNotFoundException() { 6 | super(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/exceptions/InsufficientStockException.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.exceptions; 2 | 3 | public class InsufficientStockException extends Exception{ 4 | 5 | public InsufficientStockException() { 6 | super(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/exceptions/ProductNameAlreadyExistsException.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.exceptions; 2 | 3 | public class ProductNameAlreadyExistsException extends Exception{ 4 | 5 | public ProductNameAlreadyExistsException() { 6 | super(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /payments-service/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ecommerce Events Driven Architecture 2 |

Project Architecture

3 | 4 |

Used technologies

5 |

-Java

6 |

-Spring

7 |

-MongoDB

8 |

-SNS

9 |

-SQS

10 |

-Lambda

11 |

-Bucket S3

12 | -------------------------------------------------------------------------------- /orders-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | data: 3 | mongodb: 4 | uri: mongodb://root:root@localhost 5 | database: ecommerce 6 | 7 | aws: 8 | region: us-east-1 9 | sqs: 10 | endpoint: https://sqs.us-east-1.amazonaws.com/337328321041/processed-orders-queue 11 | sns: 12 | orders-topic: arn:aws:sns:us-east-1:337328321041:orders-topic -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/dto/CustomErrorResponse.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class CustomErrorResponse { 11 | 12 | private String error; 13 | private int status; 14 | } 15 | -------------------------------------------------------------------------------- /payments-service/src/main/java/br/com/souza/eventsdrivenarchitecture/dto/CustomErrorResponse.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class CustomErrorResponse { 11 | 12 | private String error; 13 | private int status; 14 | } 15 | -------------------------------------------------------------------------------- /payments-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | data: 3 | mongodb: 4 | uri: mongodb://root:root@localhost 5 | database: payments 6 | 7 | aws: 8 | region: us-east-1 9 | sqs: 10 | endpoint: https://sqs.us-east-1.amazonaws.com/337328321041/orders-queue 11 | sns: 12 | processed-orders-topic: arn:aws:sns:us-east-1:337328321041:processed-orders-topic 13 | 14 | server: 15 | port: 8081 -------------------------------------------------------------------------------- /orders-service/src/test/java/br/com/souza/eventsdrivenarchitecture/EventsDrivenArchitectureApplicationTests.java: -------------------------------------------------------------------------------- 1 | //package br.com.souza.eventsdrivenarchitecture; 2 | // 3 | //import org.junit.jupiter.api.Test; 4 | //import org.springframework.boot.test.context.SpringBootTest; 5 | // 6 | //@SpringBootTest 7 | //class EventsDrivenArchitectureApplicationTests { 8 | // 9 | // @Test 10 | // void contextLoads() { 11 | // } 12 | // 13 | //} 14 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/OrdersServiceApplication.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class OrdersServiceApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(OrdersServiceApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /payments-service/src/main/java/br/com/souza/eventsdrivenarchitecture/PaymentsServiceApplication.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class PaymentsServiceApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(PaymentsServiceApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/database/repository/IOrderRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.database.repository; 2 | 3 | import br.com.souza.eventsdrivenarchitecture.database.model.Order; 4 | import java.util.UUID; 5 | import org.springframework.data.mongodb.repository.MongoRepository; 6 | import org.springframework.stereotype.Repository; 7 | 8 | @Repository 9 | public interface IOrderRepository extends MongoRepository { 10 | } 11 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | org.example 5 | ecommerce_events_driven 6 | 1.0-SNAPSHOT 7 | Archetype - ecommerce_event_driven 8 | http://maven.apache.org 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /payments-service/src/main/java/br/com/souza/eventsdrivenarchitecture/dto/QueueMessageRequest.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class QueueMessageRequest { 11 | 12 | private String Type; 13 | private String MessageId; 14 | private String TopicArn; 15 | private OrderResponse Message; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /.idea/aws.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /orders-service/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ -------------------------------------------------------------------------------- /payments-service/.idea/aws.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/database/repository/IProductRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.database.repository; 2 | 3 | import br.com.souza.eventsdrivenarchitecture.database.model.Product; 4 | import java.util.Optional; 5 | import java.util.UUID; 6 | import org.springframework.data.mongodb.repository.MongoRepository; 7 | import org.springframework.stereotype.Repository; 8 | 9 | @Repository 10 | public interface IProductRepository extends MongoRepository { 11 | 12 | Optional findByName(String name); 13 | } 14 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/dto/ProductRequest.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.dto; 2 | 3 | import jakarta.validation.constraints.NotNull; 4 | import java.math.BigDecimal; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | @Data 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | @Builder 14 | public class ProductRequest { 15 | 16 | @NotNull 17 | private String name; 18 | @NotNull 19 | private BigDecimal price; 20 | @NotNull 21 | private Integer quantity; 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /payments-service/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /payments-historic-lambda/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/dto/OrderRequest.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.dto; 2 | 3 | import br.com.souza.eventsdrivenarchitecture.enums.PaymentTypeEnum; 4 | import jakarta.validation.constraints.NotNull; 5 | import java.util.UUID; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | @Data 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @Builder 15 | public class OrderRequest { 16 | 17 | @NotNull 18 | private UUID productId; 19 | @NotNull 20 | private Integer quantity; 21 | @NotNull 22 | private PaymentTypeEnum paymentType; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /payments-service/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /payments-service/src/main/java/br/com/souza/eventsdrivenarchitecture/dto/Product.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.dto; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.UUID; 5 | import org.json.JSONObject; 6 | import org.springframework.data.annotation.Id; 7 | 8 | public class Product { 9 | 10 | private UUID id; 11 | private String name; 12 | private BigDecimal price; 13 | private Integer quantity; 14 | 15 | @Override 16 | public String toString(){ 17 | JSONObject json = new JSONObject(); 18 | json.put("id", id); 19 | json.put("name", name); 20 | json.put("quantity", quantity); 21 | json.put("price", price); 22 | 23 | return json.toString(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /payments-service/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /payments-service/src/main/java/br/com/souza/eventsdrivenarchitecture/service/sqs/OrdersQueueConsumer.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.service.sqs; 2 | 3 | import br.com.souza.eventsdrivenarchitecture.service.payment.PaymentService; 4 | import io.awspring.cloud.sqs.annotation.SqsListener; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Component 8 | public class OrdersQueueConsumer { 9 | 10 | private final PaymentService paymentService; 11 | 12 | public OrdersQueueConsumer(PaymentService paymentService) { 13 | this.paymentService = paymentService; 14 | } 15 | 16 | @SqsListener("orders-queue") 17 | public void listen(String message) throws Exception{ 18 | paymentService.validateOrderPayment(message); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/config/SQSConfig.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.config; 2 | 3 | import java.net.URI; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import software.amazon.awssdk.regions.Region; 8 | import software.amazon.awssdk.services.sqs.SqsAsyncClient; 9 | 10 | @Configuration 11 | public class SQSConfig { 12 | 13 | @Value("${aws.sqs.endpoint}") 14 | private String amazonSqsEndpoint; 15 | 16 | @Bean 17 | public SqsAsyncClient amazonSQSAsync() { 18 | return SqsAsyncClient.builder() 19 | .endpointOverride(URI.create(amazonSqsEndpoint)) 20 | .region(Region.US_EAST_1) 21 | .build(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /payments-service/src/main/java/br/com/souza/eventsdrivenarchitecture/config/SQSConfig.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.config; 2 | 3 | import java.net.URI; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import software.amazon.awssdk.regions.Region; 8 | import software.amazon.awssdk.services.sqs.SqsAsyncClient; 9 | 10 | @Configuration 11 | public class SQSConfig { 12 | 13 | @Value("${aws.sqs.endpoint}") 14 | private String amazonSqsEndpoint; 15 | 16 | @Bean 17 | public SqsAsyncClient amazonSQSAsync() { 18 | return SqsAsyncClient.builder() 19 | .endpointOverride(URI.create(amazonSqsEndpoint)) 20 | .region(Region.US_EAST_1) 21 | .build(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /payments-service/src/main/java/br/com/souza/eventsdrivenarchitecture/service/payment/PaymentHistoricDTO.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.service.payment; 2 | 3 | import java.util.UUID; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import org.json.JSONObject; 9 | import org.springframework.data.annotation.Id; 10 | import org.springframework.data.mongodb.core.mapping.Document; 11 | 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | @Builder 16 | public class PaymentHistoricDTO { 17 | 18 | private UUID orderId; 19 | private String paymentStatus; 20 | 21 | @Override 22 | public String toString(){ 23 | JSONObject json = new JSONObject(); 24 | json.put("orderId", orderId); 25 | json.put("paymentStatus", paymentStatus); 26 | 27 | return json.toString(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/service/sns/AwsSnsService.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.service.sns; 2 | 3 | import com.amazonaws.services.sns.AmazonSNS; 4 | import com.amazonaws.services.sns.model.Topic; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Qualifier; 7 | import org.springframework.stereotype.Service; 8 | 9 | @Slf4j 10 | @Service 11 | public class AwsSnsService { 12 | private final AmazonSNS snsClient; 13 | private final Topic ordersTopic; 14 | 15 | public AwsSnsService(AmazonSNS snsClient, 16 | @Qualifier("ordersTopic") Topic ordersTopic){ 17 | this.snsClient = snsClient; 18 | this.ordersTopic = ordersTopic; 19 | } 20 | 21 | public void publish(String message){ 22 | snsClient.publish(ordersTopic.getTopicArn(), message); 23 | System.out.println("Message published"); 24 | } 25 | } -------------------------------------------------------------------------------- /payments-service/src/main/java/br/com/souza/eventsdrivenarchitecture/service/sns/AwsSnsService.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.service.sns; 2 | 3 | import com.amazonaws.services.sns.AmazonSNS; 4 | import com.amazonaws.services.sns.model.Topic; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Qualifier; 7 | import org.springframework.stereotype.Service; 8 | 9 | @Slf4j 10 | @Service 11 | public class AwsSnsService { 12 | private final AmazonSNS snsClient; 13 | private final Topic processedPaymentsTopic; 14 | 15 | public AwsSnsService(AmazonSNS snsClient, 16 | Topic processedPaymentsTopic){ 17 | this.snsClient = snsClient; 18 | this.processedPaymentsTopic = processedPaymentsTopic; 19 | } 20 | 21 | public void publish(String message){ 22 | snsClient.publish(processedPaymentsTopic.getTopicArn(), message); 23 | System.out.println("Message published"); 24 | } 25 | } -------------------------------------------------------------------------------- /payments-service/src/main/java/br/com/souza/eventsdrivenarchitecture/dto/OrderResponse.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.dto; 2 | 3 | import java.math.BigDecimal; 4 | import java.time.LocalDateTime; 5 | import java.util.UUID; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | import org.json.JSONObject; 11 | 12 | @Data 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | @Builder 16 | public class OrderResponse { 17 | 18 | private UUID id; 19 | private Integer quantity; 20 | private BigDecimal amount; 21 | private LocalDateTime orderTime; 22 | private String paymentStatus; 23 | private Product product; 24 | 25 | @Override 26 | public String toString(){ 27 | JSONObject json = new JSONObject(); 28 | json.put("id", id); 29 | json.put("quantity", quantity); 30 | json.put("amount", amount); 31 | json.put("paymentStatus", paymentStatus); 32 | json.put("product", product.toString()); 33 | 34 | return json.toString(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/config/SNSConfig.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.config; 2 | 3 | import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; 4 | import com.amazonaws.services.sns.AmazonSNS; 5 | import com.amazonaws.services.sns.AmazonSNSClientBuilder; 6 | import com.amazonaws.services.sns.model.Topic; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | @Configuration 12 | public class SNSConfig { 13 | 14 | @Value("${aws.region}") 15 | private String region; 16 | @Value("${aws.sns.orders-topic}") 17 | private String ordersTopicArn; 18 | 19 | @Bean 20 | public AmazonSNS amazonSNSBuilder(){ 21 | return AmazonSNSClientBuilder.standard() 22 | .withCredentials(new DefaultAWSCredentialsProviderChain()) 23 | .withRegion(region) 24 | .build(); 25 | } 26 | 27 | @Bean(name = "ordersTopic") 28 | public Topic snsOrdersTopicBuilder(){ 29 | return new Topic().withTopicArn(ordersTopicArn); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /payments-service/src/main/java/br/com/souza/eventsdrivenarchitecture/config/SNSConfig.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.config; 2 | 3 | import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; 4 | import com.amazonaws.services.sns.AmazonSNS; 5 | import com.amazonaws.services.sns.AmazonSNSClientBuilder; 6 | import com.amazonaws.services.sns.model.Topic; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | @Configuration 12 | public class SNSConfig { 13 | 14 | @Value("${aws.region}") 15 | private String region; 16 | @Value("${aws.sns.processed-orders-topic}") 17 | private String processedOrdersTopicArn; 18 | 19 | @Bean 20 | public AmazonSNS amazonSNSBuilder(){ 21 | return AmazonSNSClientBuilder.standard() 22 | .withCredentials(new DefaultAWSCredentialsProviderChain()) 23 | .withRegion(region) 24 | .build(); 25 | } 26 | 27 | @Bean 28 | public Topic snsProcessedOrdersTopicBuilder(){ 29 | return new Topic().withTopicArn(processedOrdersTopicArn); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/database/model/Product.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.database.model; 2 | 3 | import br.com.souza.eventsdrivenarchitecture.dto.ProductRequest; 4 | import java.math.BigDecimal; 5 | import java.util.UUID; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | import org.json.JSONObject; 11 | import org.springframework.data.annotation.Id; 12 | import org.springframework.data.mongodb.core.mapping.Document; 13 | 14 | @Data 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Builder 18 | @Document(collection = "products") 19 | public class Product { 20 | 21 | @Id 22 | private UUID id; 23 | private String name; 24 | private BigDecimal price; 25 | private Integer quantity; 26 | 27 | public Product(ProductRequest request){ 28 | this.id = UUID.randomUUID(); 29 | this.name = request.getName(); 30 | this.price = request.getPrice(); 31 | this.quantity = request.getQuantity(); 32 | } 33 | 34 | @Override 35 | public String toString(){ 36 | JSONObject json = new JSONObject(); 37 | json.put("id", id); 38 | json.put("name", name); 39 | json.put("quantity", quantity); 40 | json.put("price", price); 41 | 42 | return json.toString(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/service/sqs/ProcessedPaymentsQueueConsumer.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.service.sqs; 2 | 3 | import br.com.souza.eventsdrivenarchitecture.service.order.OrderService; 4 | import io.awspring.cloud.sqs.annotation.SqsListener; 5 | import java.util.UUID; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.json.JSONObject; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Slf4j 11 | @Component 12 | public class ProcessedPaymentsQueueConsumer { 13 | 14 | private final OrderService orderService; 15 | 16 | public ProcessedPaymentsQueueConsumer(OrderService orderService) { 17 | this.orderService = orderService; 18 | } 19 | 20 | @SqsListener("processed-orders-queue") 21 | public void listen(String message) throws Exception{ 22 | System.out.println("Payment processed"); 23 | JSONObject queueMessage = new JSONObject(message); 24 | String messageJsonString = queueMessage.getString("Message"); 25 | 26 | JSONObject messageObject = null; 27 | try { 28 | messageObject = new JSONObject(messageJsonString); 29 | }catch (Exception e){ 30 | log.error("Error: ", e); 31 | return; 32 | } 33 | 34 | orderService.updatePaymentStatus(UUID.fromString(messageObject.getString("orderId")), messageObject.getString("paymentStatus")); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/service/product/ProductService.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.service.product; 2 | 3 | import br.com.souza.eventsdrivenarchitecture.database.model.Product; 4 | import br.com.souza.eventsdrivenarchitecture.database.repository.IProductRepository; 5 | import br.com.souza.eventsdrivenarchitecture.dto.ProductRequest; 6 | import br.com.souza.eventsdrivenarchitecture.exceptions.ProductNameAlreadyExistsException; 7 | import br.com.souza.eventsdrivenarchitecture.exceptions.ProductNotFoundException; 8 | import java.util.List; 9 | import java.util.UUID; 10 | import org.springframework.stereotype.Service; 11 | 12 | @Service 13 | public class ProductService { 14 | 15 | private final IProductRepository iProductRepository; 16 | 17 | public ProductService(IProductRepository iProductRepository) { 18 | this.iProductRepository = iProductRepository; 19 | } 20 | 21 | public void saveNewProduct(ProductRequest request) throws Exception { 22 | if (iProductRepository.findByName(request.getName()).isPresent()) { 23 | throw new ProductNameAlreadyExistsException(); 24 | } 25 | 26 | iProductRepository.save(new Product(request)); 27 | } 28 | 29 | public void updateProduct(Product product) throws Exception { 30 | getProductById(product.getId()); 31 | iProductRepository.save(product); 32 | } 33 | 34 | public Product getProductById(UUID id) throws Exception{ 35 | return iProductRepository.findById(id) 36 | .orElseThrow(ProductNotFoundException::new); 37 | } 38 | 39 | public List getAllProducts() { 40 | return iProductRepository.findAll(); 41 | } 42 | 43 | public void deleteProduct(UUID id) throws Exception{ 44 | iProductRepository.delete(getProductById(id)); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/dto/OrderResponse.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.dto; 2 | 3 | import br.com.souza.eventsdrivenarchitecture.database.model.Order; 4 | import br.com.souza.eventsdrivenarchitecture.database.model.Product; 5 | import br.com.souza.eventsdrivenarchitecture.database.repository.IProductRepository; 6 | import java.math.BigDecimal; 7 | import java.time.LocalDateTime; 8 | import java.util.UUID; 9 | import lombok.AllArgsConstructor; 10 | import lombok.Builder; 11 | import lombok.Data; 12 | import lombok.NoArgsConstructor; 13 | import org.json.JSONObject; 14 | 15 | @Data 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | @Builder 19 | public class OrderResponse { 20 | 21 | private UUID id; 22 | private Integer quantity; 23 | private BigDecimal amount; 24 | private LocalDateTime orderTime; 25 | private String paymentStatus; 26 | private String paymentType; 27 | private Product product; 28 | 29 | public OrderResponse(Order order, IProductRepository iProductRepository){ 30 | this.id = order.getId(); 31 | this.quantity = order.getQuantity(); 32 | this.amount = order.getAmount(); 33 | this.orderTime = order.getOrderTime(); 34 | this.paymentStatus = order.getPaymentStatus(); 35 | this.paymentType = order.getPaymentType(); 36 | this.product = iProductRepository.findById(order.getProductId()) 37 | .orElse(null); 38 | } 39 | 40 | @Override 41 | public String toString(){ 42 | JSONObject json = new JSONObject(); 43 | json.put("id", id); 44 | json.put("quantity", quantity); 45 | json.put("amount", amount); 46 | json.put("orderTime", orderTime); 47 | json.put("paymentStatus", paymentStatus); 48 | json.put("paymentType", paymentType); 49 | json.put("product", product.toString()); 50 | 51 | return json.toString(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/database/model/Order.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.database.model; 2 | 3 | import br.com.souza.eventsdrivenarchitecture.dto.OrderRequest; 4 | import br.com.souza.eventsdrivenarchitecture.enums.PaymentStatusEnum; 5 | import java.math.BigDecimal; 6 | import java.time.LocalDateTime; 7 | import java.util.UUID; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Builder; 10 | import lombok.Data; 11 | import lombok.NoArgsConstructor; 12 | import org.json.JSONObject; 13 | import org.springframework.data.annotation.Id; 14 | import org.springframework.data.mongodb.core.mapping.Document; 15 | 16 | @Data 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | @Builder 20 | @Document(collection = "orders") 21 | public class Order { 22 | 23 | @Id 24 | private UUID id; 25 | private UUID productId; 26 | private Integer quantity; 27 | private BigDecimal amount; 28 | private LocalDateTime orderTime; 29 | private String paymentStatus; 30 | private String paymentType; 31 | 32 | public Order(OrderRequest request, Product product){ 33 | this.id = UUID.randomUUID(); 34 | this.productId = request.getProductId(); 35 | this.quantity = request.getQuantity(); 36 | this.amount = product.getPrice().multiply(new BigDecimal(request.getQuantity())); 37 | this.orderTime = LocalDateTime.now(); 38 | this.paymentStatus = PaymentStatusEnum.PENDING.name().toUpperCase(); 39 | this.paymentType = request.getPaymentType().name().toUpperCase(); 40 | } 41 | 42 | @Override 43 | public String toString(){ 44 | JSONObject json = new JSONObject(); 45 | json.put("id", id); 46 | json.put("productId", productId); 47 | json.put("quantity", quantity); 48 | json.put("amount", amount); 49 | json.put("paymentType", paymentType); 50 | json.put("paymentStatus", paymentStatus); 51 | json.put("orderTime", orderTime); 52 | 53 | return json.toString(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/controller/impl/OrderControllerImpl.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.controller.impl; 2 | 3 | import br.com.souza.eventsdrivenarchitecture.dto.OrderRequest; 4 | import br.com.souza.eventsdrivenarchitecture.dto.OrderResponse; 5 | import br.com.souza.eventsdrivenarchitecture.service.order.OrderService; 6 | import java.util.List; 7 | import java.util.UUID; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.http.MediaType; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | import org.springframework.web.bind.annotation.PostMapping; 14 | import org.springframework.web.bind.annotation.RequestBody; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | import org.springframework.web.bind.annotation.RestController; 17 | 18 | @RestController 19 | @RequestMapping(value = "/v1/order") 20 | public class OrderControllerImpl { 21 | 22 | private final OrderService orderService; 23 | 24 | public OrderControllerImpl(OrderService orderService) { 25 | this.orderService = orderService; 26 | } 27 | 28 | @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) 29 | public ResponseEntity createNewOrder(@RequestBody OrderRequest request) throws Exception { 30 | orderService.saveNewOrder(request); 31 | return new ResponseEntity<>(HttpStatus.CREATED); 32 | } 33 | 34 | @GetMapping(value = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE) 35 | public ResponseEntity getOrderById(@PathVariable("id") UUID id) throws Exception { 36 | return new ResponseEntity<>(orderService.getOrderById(id), HttpStatus.OK); 37 | } 38 | 39 | @GetMapping(consumes = MediaType.APPLICATION_JSON_VALUE) 40 | public ResponseEntity> getAllOrders(){ 41 | return new ResponseEntity<>(orderService.getAllOrders(), HttpStatus.OK); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /payments-service/src/main/java/br/com/souza/eventsdrivenarchitecture/service/payment/PaymentService.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.service.payment; 2 | 3 | import br.com.souza.eventsdrivenarchitecture.enums.ValidPaymentTypeEnum; 4 | import br.com.souza.eventsdrivenarchitecture.service.sns.AwsSnsService; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.UUID; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.json.JSONObject; 10 | import org.springframework.stereotype.Service; 11 | 12 | @Slf4j 13 | @Service 14 | public class PaymentService { 15 | 16 | private final AwsSnsService awsSnsService; 17 | 18 | public PaymentService(AwsSnsService awsSnsService) { 19 | this.awsSnsService = awsSnsService; 20 | } 21 | 22 | public void validateOrderPayment(String message) throws Exception{ 23 | System.out.println("Processando tipo de pagamento do pedido..."); 24 | 25 | JSONObject queueMessage = new JSONObject(message); 26 | String messageJsonString = queueMessage.getString("Message"); 27 | 28 | JSONObject messageObject = null; 29 | try { 30 | messageObject = new JSONObject(messageJsonString); 31 | }catch (Exception e){ 32 | log.error("Error: ", e); 33 | return; 34 | } 35 | String paymentType = messageObject.getString("paymentType"); 36 | 37 | PaymentHistoricDTO paymentHistoricLog = PaymentHistoricDTO.builder() 38 | .orderId(UUID.fromString(messageObject.getString("id"))) 39 | .paymentStatus(isValidPaymentType(paymentType) ? PaymentStatusEnum.APPROVED.name() : PaymentStatusEnum.RECUSED.name()) 40 | .build(); 41 | 42 | awsSnsService.publish(paymentHistoricLog.toString()); 43 | } 44 | 45 | private boolean isValidPaymentType(String paymentType){ 46 | List validPaymentTypes = Arrays.stream(ValidPaymentTypeEnum.values()) 47 | .map(ValidPaymentTypeEnum::name) 48 | .map(String::toUpperCase) 49 | .toList(); 50 | 51 | return validPaymentTypes.contains(paymentType.toUpperCase()); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /payments-historic-lambda/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.2.2 9 | 10 | 11 | br.com.souza 12 | payments-historic-lambda 13 | 0.0.1-SNAPSHOT 14 | payments-historic-lambda 15 | Lambda Function to store payments JSON into Bucket S3 16 | 17 | 17 18 | 17 19 | 17 20 | 21 | 22 | 23 | 24 | com.amazonaws 25 | aws-lambda-java-core 26 | 1.2.3 27 | 28 | 29 | 30 | com.amazonaws 31 | aws-java-sdk-lambda 32 | 1.12.661 33 | 34 | 35 | com.amazonaws 36 | aws-lambda-java-events 37 | 3.11.4 38 | 39 | 40 | 41 | com.amazonaws 42 | aws-java-sdk-s3 43 | 1.12.661 44 | 45 | 46 | org.json 47 | json 48 | 20231013 49 | 50 | 51 | 52 | 53 | payments-historic-lambda 54 | 55 | 56 | org.apache.maven.plugins 57 | maven-shade-plugin 58 | 3.5.1 59 | 60 | false 61 | 62 | 63 | 64 | package 65 | 66 | shade 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /payments-historic-lambda/src/main/java/br/com/souza/paymentshistoriclambda/PaymentsHistoricFunction.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.paymentshistoriclambda; 2 | 3 | import com.amazonaws.auth.AWSStaticCredentialsProvider; 4 | import com.amazonaws.auth.BasicAWSCredentials; 5 | import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; 6 | import com.amazonaws.regions.Regions; 7 | import com.amazonaws.services.lambda.runtime.Context; 8 | import com.amazonaws.services.lambda.runtime.RequestHandler; 9 | import com.amazonaws.services.lambda.runtime.events.SQSEvent; 10 | import com.amazonaws.services.s3.AmazonS3; 11 | import com.amazonaws.services.s3.AmazonS3ClientBuilder; 12 | import com.amazonaws.services.s3.model.PutObjectRequest; 13 | import java.io.File; 14 | import java.io.FileOutputStream; 15 | import java.io.IOException; 16 | import java.io.OutputStream; 17 | import org.json.JSONObject; 18 | 19 | 20 | public class PaymentsHistoricFunction implements RequestHandler { 21 | 22 | @Override 23 | public String handleRequest(SQSEvent event, Context context) { 24 | 25 | Regions clientRegion = Regions.fromName(System.getenv("REGION")); 26 | 27 | BasicAWSCredentials credentials = new BasicAWSCredentials( 28 | System.getenv("ACCESS_KEY"), 29 | System.getenv("SECRET_ACCESS_KEY")); 30 | 31 | AmazonS3 s3Client = AmazonS3ClientBuilder.standard() 32 | .withRegion(clientRegion) 33 | .withCredentials(new AWSStaticCredentialsProvider(credentials)) 34 | .build(); 35 | 36 | String bucketName = System.getenv("BUCKET_NAME"); 37 | 38 | for (SQSEvent.SQSMessage msg : event.getRecords()) { 39 | System.out.println("Received message: " + msg.getBody()); 40 | JSONObject jsonObject = new JSONObject(msg.getBody()); 41 | 42 | JSONObject paymentObject = new JSONObject(jsonObject.getString("Message")); 43 | String orderId = paymentObject.getString("orderId"); 44 | 45 | File file = new File("/tmp/" + orderId + "-payment.json"); 46 | try (OutputStream os = new FileOutputStream(file)) { 47 | os.write(paymentObject.toString().getBytes()); 48 | } catch (IOException e) { 49 | throw new RuntimeException(e); 50 | } 51 | 52 | s3Client.putObject(new PutObjectRequest(bucketName, orderId + "-payment.json", file)); 53 | System.out.println("Successfully saved into Bucket S3"); 54 | file.delete(); 55 | } 56 | return "Successfully saved into Bucket S3"; 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /payments-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | br.com.souza 7 | payments-service 8 | 1.0.0 9 | payments-service 10 | EDA payments service project example with Java, Spring, SNS, SQS, S3 and Lambda 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-parent 15 | 3.2.2 16 | 17 | 18 | 19 | 20 | 17 21 | 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-data-mongodb 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | 34 | org.projectlombok 35 | lombok 36 | true 37 | 38 | 39 | 40 | org.json 41 | json 42 | 20231013 43 | 44 | 45 | 46 | com.amazonaws 47 | aws-java-sdk-sns 48 | 1.12.62 49 | 50 | 51 | io.awspring.cloud 52 | spring-cloud-aws-starter-sqs 53 | 54 | 55 | 56 | 57 | 58 | 59 | io.awspring.cloud 60 | spring-cloud-aws-dependencies 61 | 3.0.1 62 | pom 63 | import 64 | 65 | 66 | 67 | 68 | 69 | payments-service 70 | 71 | 72 | 73 | org.springframework.boot 74 | spring-boot-maven-plugin 75 | 76 | 77 | 78 | org.projectlombok 79 | lombok 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/controller/impl/ProductControllerImpl.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.controller.impl; 2 | 3 | import br.com.souza.eventsdrivenarchitecture.database.model.Product; 4 | import br.com.souza.eventsdrivenarchitecture.dto.ProductRequest; 5 | import br.com.souza.eventsdrivenarchitecture.service.product.ProductService; 6 | import java.util.List; 7 | import java.util.UUID; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.http.MediaType; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.web.bind.annotation.DeleteMapping; 12 | import org.springframework.web.bind.annotation.GetMapping; 13 | import org.springframework.web.bind.annotation.PathVariable; 14 | import org.springframework.web.bind.annotation.PostMapping; 15 | import org.springframework.web.bind.annotation.PutMapping; 16 | import org.springframework.web.bind.annotation.RequestBody; 17 | import org.springframework.web.bind.annotation.RequestMapping; 18 | import org.springframework.web.bind.annotation.RestController; 19 | 20 | @RestController 21 | @RequestMapping(value = "/v1/product") 22 | public class ProductControllerImpl { 23 | 24 | private final ProductService productService; 25 | 26 | public ProductControllerImpl(ProductService productService) { 27 | this.productService = productService; 28 | } 29 | 30 | @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) 31 | public ResponseEntity createNewProduct(@RequestBody ProductRequest request) throws Exception { 32 | productService.saveNewProduct(request); 33 | return new ResponseEntity<>(HttpStatus.CREATED); 34 | } 35 | 36 | @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE) 37 | public ResponseEntity updateProduct(@RequestBody Product product) throws Exception { 38 | productService.updateProduct(product); 39 | return new ResponseEntity<>(HttpStatus.CREATED); 40 | } 41 | 42 | @GetMapping(value = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE) 43 | public ResponseEntity getProductById(@PathVariable("id") UUID id) throws Exception { 44 | return new ResponseEntity<>(productService.getProductById(id), HttpStatus.OK); 45 | } 46 | 47 | @GetMapping(consumes = MediaType.APPLICATION_JSON_VALUE) 48 | public ResponseEntity> getAllProducts(){ 49 | return new ResponseEntity<>(productService.getAllProducts(), HttpStatus.OK); 50 | } 51 | 52 | @DeleteMapping(value = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE) 53 | public ResponseEntity deleteProduct(@PathVariable("id") UUID id) throws Exception { 54 | productService.deleteProduct(id); 55 | return new ResponseEntity<>(HttpStatus.OK); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /orders-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | br.com.souza 7 | orders-service 8 | 1.0.0 9 | orders-service 10 | EDA ecommerce project example with Java, Spring, SNS, SQS, S3 and Lambda 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-parent 15 | 3.2.2 16 | 17 | 18 | 19 | 20 | 17 21 | 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-data-mongodb 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | 34 | org.projectlombok 35 | lombok 36 | true 37 | 38 | 39 | 40 | org.json 41 | json 42 | 20231013 43 | 44 | 45 | 46 | com.amazonaws 47 | aws-java-sdk-sns 48 | 1.12.62 49 | 50 | 51 | io.awspring.cloud 52 | spring-cloud-aws-starter-sqs 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-starter-validation 57 | 3.2.2 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | io.awspring.cloud 67 | spring-cloud-aws-dependencies 68 | 3.0.1 69 | pom 70 | import 71 | 72 | 73 | 74 | 75 | 76 | orders-service 77 | 78 | 79 | 80 | org.springframework.boot 81 | spring-boot-maven-plugin 82 | 83 | 84 | 85 | org.projectlombok 86 | lombok 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/service/order/OrderService.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.service.order; 2 | 3 | import br.com.souza.eventsdrivenarchitecture.database.model.Order; 4 | import br.com.souza.eventsdrivenarchitecture.database.model.Product; 5 | import br.com.souza.eventsdrivenarchitecture.database.repository.IOrderRepository; 6 | import br.com.souza.eventsdrivenarchitecture.database.repository.IProductRepository; 7 | import br.com.souza.eventsdrivenarchitecture.dto.OrderRequest; 8 | import br.com.souza.eventsdrivenarchitecture.dto.OrderResponse; 9 | import br.com.souza.eventsdrivenarchitecture.exceptions.InsufficientStockException; 10 | import br.com.souza.eventsdrivenarchitecture.exceptions.OrderNotFoundException; 11 | import br.com.souza.eventsdrivenarchitecture.service.sns.AwsSnsService; 12 | import br.com.souza.eventsdrivenarchitecture.service.product.ProductService; 13 | import java.util.Comparator; 14 | import java.util.List; 15 | import java.util.UUID; 16 | import org.springframework.stereotype.Service; 17 | 18 | @Service 19 | public class OrderService { 20 | 21 | private final IOrderRepository iOrderRepository; 22 | private final ProductService productService; 23 | private final IProductRepository iProductRepository; 24 | private final AwsSnsService awsSnsService; 25 | 26 | public OrderService(IOrderRepository iOrderRepository, 27 | ProductService productService, 28 | IProductRepository iProductRepository, 29 | AwsSnsService awsSnsService) { 30 | this.iOrderRepository = iOrderRepository; 31 | this.productService = productService; 32 | this.iProductRepository = iProductRepository; 33 | this.awsSnsService = awsSnsService; 34 | } 35 | 36 | public void saveNewOrder(OrderRequest request) throws Exception { 37 | Product product = productService.getProductById(request.getProductId()); 38 | 39 | if(product.getQuantity() < request.getQuantity()) throw new InsufficientStockException(); 40 | 41 | product.setQuantity(product.getQuantity() - request.getQuantity()); 42 | productService.updateProduct(product); 43 | 44 | Order order = new Order(request, product); 45 | iOrderRepository.save(order); 46 | 47 | awsSnsService.publish(new OrderResponse(order, iProductRepository).toString()); 48 | } 49 | 50 | public OrderResponse getOrderById(UUID id) throws Exception{ 51 | Order order = iOrderRepository.findById(id) 52 | .orElseThrow(OrderNotFoundException::new); 53 | 54 | return new OrderResponse(order, iProductRepository); 55 | } 56 | 57 | public List getAllOrders() { 58 | return iOrderRepository.findAll().stream() 59 | .map(o -> new OrderResponse(o, iProductRepository)) 60 | .sorted(Comparator.comparing(OrderResponse::getOrderTime).reversed()) 61 | .toList(); 62 | } 63 | 64 | public void updatePaymentStatus(UUID id, String status) throws Exception{ 65 | Order order = iOrderRepository.findById(id) 66 | .orElseThrow(OrderNotFoundException::new); 67 | 68 | order.setPaymentStatus(status); 69 | 70 | iOrderRepository.save(order); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /orders-service/src/main/java/br/com/souza/eventsdrivenarchitecture/handler/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package br.com.souza.eventsdrivenarchitecture.handler; 2 | 3 | import br.com.souza.eventsdrivenarchitecture.dto.CustomErrorResponse; 4 | import br.com.souza.eventsdrivenarchitecture.exceptions.InsufficientStockException; 5 | import br.com.souza.eventsdrivenarchitecture.exceptions.OrderNotFoundException; 6 | import br.com.souza.eventsdrivenarchitecture.exceptions.ProductNameAlreadyExistsException; 7 | import br.com.souza.eventsdrivenarchitecture.exceptions.ProductNotFoundException; 8 | import org.springframework.http.HttpHeaders; 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.http.MediaType; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.web.bind.annotation.ControllerAdvice; 13 | import org.springframework.web.bind.annotation.ExceptionHandler; 14 | import org.springframework.web.context.request.WebRequest; 15 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; 16 | 17 | @ControllerAdvice 18 | public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { 19 | 20 | @ExceptionHandler(InsufficientStockException.class) 21 | public ResponseEntity handleInsufficientException(InsufficientStockException e, WebRequest request) { 22 | CustomErrorResponse error = new CustomErrorResponse(); 23 | error.setError("Insuficient products on stock"); 24 | error.setStatus(HttpStatus.BAD_REQUEST.value()); 25 | 26 | HttpHeaders headers = new HttpHeaders(); 27 | headers.setContentType(MediaType.APPLICATION_JSON); 28 | 29 | return handleExceptionInternal(e, error, headers, HttpStatus.BAD_REQUEST, request); 30 | } 31 | 32 | @ExceptionHandler(OrderNotFoundException.class) 33 | public ResponseEntity handleOrderNotFoundException(OrderNotFoundException e, WebRequest request) { 34 | CustomErrorResponse error = new CustomErrorResponse(); 35 | error.setError("Order not found"); 36 | error.setStatus(HttpStatus.NOT_FOUND.value()); 37 | 38 | HttpHeaders headers = new HttpHeaders(); 39 | headers.setContentType(MediaType.APPLICATION_JSON); 40 | 41 | return handleExceptionInternal(e, error, headers, HttpStatus.NOT_FOUND, request); 42 | } 43 | 44 | @ExceptionHandler(ProductNotFoundException.class) 45 | public ResponseEntity handleProductNotFoundException(ProductNotFoundException e, WebRequest request) { 46 | CustomErrorResponse error = new CustomErrorResponse(); 47 | error.setError("Product not found"); 48 | error.setStatus(HttpStatus.NOT_FOUND.value()); 49 | 50 | HttpHeaders headers = new HttpHeaders(); 51 | headers.setContentType(MediaType.APPLICATION_JSON); 52 | 53 | return handleExceptionInternal(e, error, headers, HttpStatus.NOT_FOUND, request); 54 | } 55 | 56 | @ExceptionHandler(ProductNameAlreadyExistsException.class) 57 | public ResponseEntity handleProductNameAlreadyExistsException(ProductNameAlreadyExistsException e, WebRequest request) { 58 | CustomErrorResponse error = new CustomErrorResponse(); 59 | error.setError("Already exists a product with this name"); 60 | error.setStatus(HttpStatus.BAD_REQUEST.value()); 61 | 62 | HttpHeaders headers = new HttpHeaders(); 63 | headers.setContentType(MediaType.APPLICATION_JSON); 64 | 65 | return handleExceptionInternal(e, error, headers, HttpStatus.BAD_REQUEST, request); 66 | } 67 | 68 | @ExceptionHandler(Exception.class) 69 | public ResponseEntity handleGenericException(Exception e, WebRequest request) { 70 | CustomErrorResponse error = new CustomErrorResponse(); 71 | error.setError(e.getMessage()); 72 | error.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); 73 | 74 | HttpHeaders headers = new HttpHeaders(); 75 | headers.setContentType(MediaType.APPLICATION_JSON); 76 | 77 | return handleExceptionInternal(e, error, headers, HttpStatus.INTERNAL_SERVER_ERROR, request); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /payments-service/.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/dbnavigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | -------------------------------------------------------------------------------- /payments-service/.idea/dbnavigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | --------------------------------------------------------------------------------