├── .gitignore ├── README.md ├── commons ├── pom.xml └── src │ └── main │ └── java │ └── domain │ ├── KafkaGroupIds.java │ ├── KafkaIds.java │ ├── Order.java │ ├── OrderSource.java │ ├── OrderStatus.java │ └── Topics.java ├── docker-compose.yml ├── inf ├── kafka.svg ├── overview.png └── spring.svg ├── ms-orders ├── DockerFile ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── zatribune │ │ │ └── spring │ │ │ └── ecommerce │ │ │ └── orders │ │ │ ├── OrderApplication.java │ │ │ ├── config │ │ │ └── KafkaConfig.java │ │ │ ├── controller │ │ │ └── OrderController.java │ │ │ └── service │ │ │ ├── OrderService.java │ │ │ └── OrderServiceImpl.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── zatribune │ └── spring │ └── ecommerce │ └── orders │ └── OrderApplicationTests.java ├── ms-payment ├── DockerFile ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── zatribune │ │ │ └── spring │ │ │ └── ecommerce │ │ │ └── payments │ │ │ ├── PaymentApplication.java │ │ │ ├── db │ │ │ ├── DevBootstrap.java │ │ │ ├── entities │ │ │ │ └── Customer.java │ │ │ └── repository │ │ │ │ └── CustomerRepository.java │ │ │ ├── listener │ │ │ └── OrderListener.java │ │ │ └── service │ │ │ ├── OrderService.java │ │ │ └── OrderServiceImpl.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── zatribune │ └── spring │ └── ecommerce │ └── payments │ └── PaymentApplicationTests.java ├── ms-stock ├── DockerFile ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── zatribune │ │ │ └── spring │ │ │ └── ecommerce │ │ │ └── stock │ │ │ ├── StockApplication.java │ │ │ ├── db │ │ │ ├── DevBootstrap.java │ │ │ ├── entities │ │ │ │ └── Product.java │ │ │ └── repository │ │ │ │ └── ProductRepository.java │ │ │ ├── listener │ │ │ └── OrderListener.java │ │ │ └── service │ │ │ ├── OrderService.java │ │ │ └── OrderServiceImpl.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── zatribune │ └── spring │ └── ecommerce │ └── stock │ └── StockApplicationTests.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | commons/target/classes 2 | ms-orders/target/ 3 | ms-payment/target/ 4 | ms-stock/target/ 5 | /.vscode/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Linkedin](https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white&label=contact%20Me)](https://linkedin.com/in/zatribune) 2 | ## Overview 3 | This is a Demo Project for an E-commerce Microservices Application that demonstrates Event-driven architecture using Kafka. 4 |

5 | spring 6 | kafka 7 |

8 | 9 | ## Specs 10 | The System consists of 3 microservices as shown in the next figure. 11 | - ms-orders: a restful webservice & an orchestrator for the ordering process. 12 | - ms-stock: management of product inventory. 13 | - ms-payments: for payment processing. 14 | 15 | 16 |

17 | overview 18 |

19 | 20 | ## Steps to deploy 21 | #### Deploying Kafka + MYSQL to Docker 22 | First, we'll need to execute the docker compose file in order to initialize the required containers 23 | for our environment. 24 | 1. mysql 25 | 2. kafka-cluster-1 26 | 3. kafka-cluster-2 27 | 4. zookeeper-1 28 | 5. zookeeper-2 29 | 30 | run this command in the root directory 31 | ```shell 32 | docker-compose up -d 33 | ``` 34 | The generated containers will have their name prefixed with "ecommerce". 35 | In order to list generated containers, run this command: 36 | ```shell 37 | docker ps --filter "name=ecommerce" 38 | ``` 39 | Since we are going to utilize 5 Docker containers which should communicate with 40 | each other, we will need to start them on same network. 41 | By default, these containers will be mapped to a generated network named 42 | **ecommerce_default** with a (Bridge) driver. 43 | To list docker networks, run this command to validate. 44 | ```shell 45 | docker network ls 46 | ``` 47 | You can also run this command to inspect this network. 48 | ```shell 49 | docker network inspect ecommerce_default 50 | ``` 51 | To validate containers' ports 52 | ```shell 53 | docker port {container_name} 54 | ``` 55 | Next, we'll create Docker images for our 3 spring-boot apps. 56 | ```shell 57 | docker build -t ecommerce-orders -f DockerFile . 58 | docker build -t ecommerce-payments -f DockerFile . 59 | docker build -t ecommerce-stock -f DockerFile . 60 | ``` 61 | Finally, you can run those images locally (**ms-orders** is exposed to port 9091). 62 | Alternately, you can use those images with **Kubernetes**. 63 | 64 | ## Built With 65 |     **Java (JDK 11)** - The Main Programming Language and Framework. 66 |     **Spring Boot** - Software Platform for creating and delivering Web Applications. 67 |     **Apache Kafka** - distributed data streaming platform. 68 |     **MySQL** - A relational database management system [Optional]. 69 |     **Docker** - A containerization platform. 70 |     **Maven** - Build tool & Dependency Management. 71 | -------------------------------------------------------------------------------- /commons/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | 9 | com.zatribune.spring 10 | ecommerce 11 | 1.0-SNAPSHOT 12 | 13 | 14 | commons 15 | 1.0-SNAPSHOT 16 | jar 17 | 18 | 19 | 11 20 | 11 21 | UTF-8 22 | UTF-8 23 | true 24 | 25 | 26 | -------------------------------------------------------------------------------- /commons/src/main/java/domain/KafkaGroupIds.java: -------------------------------------------------------------------------------- 1 | package domain; 2 | 3 | public interface KafkaGroupIds { 4 | String PAYMENTS = "payments"; 5 | String STOCK = "stock"; 6 | } 7 | -------------------------------------------------------------------------------- /commons/src/main/java/domain/KafkaIds.java: -------------------------------------------------------------------------------- 1 | package domain; 2 | 3 | public interface KafkaIds { 4 | String ORDERS = "orders"; 5 | } 6 | -------------------------------------------------------------------------------- /commons/src/main/java/domain/Order.java: -------------------------------------------------------------------------------- 1 | package domain; 2 | 3 | 4 | import lombok.*; 5 | 6 | @Getter 7 | @Setter 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | @Builder 11 | public class Order { 12 | private Long id; 13 | private Long customerId; 14 | private Long productId; 15 | private int productCount; 16 | private int price; 17 | private OrderStatus status; 18 | private OrderSource source; 19 | 20 | @Override 21 | public String toString() { 22 | return "Order{" + 23 | "id=" + id + 24 | ", customerId=" + customerId + 25 | ", productId=" + productId + 26 | ", productCount=" + productCount + 27 | ", price=" + price + 28 | ", status=" + status + 29 | ", source=" + source + 30 | '}'; 31 | } 32 | } -------------------------------------------------------------------------------- /commons/src/main/java/domain/OrderSource.java: -------------------------------------------------------------------------------- 1 | package domain; 2 | 3 | public enum OrderSource { 4 | PAYMENT,STOCK 5 | } 6 | -------------------------------------------------------------------------------- /commons/src/main/java/domain/OrderStatus.java: -------------------------------------------------------------------------------- 1 | package domain; 2 | 3 | public enum OrderStatus { 4 | NEW,ACCEPT,REJECT,REJECTED,ROLLBACK,CONFIRMED 5 | } 6 | -------------------------------------------------------------------------------- /commons/src/main/java/domain/Topics.java: -------------------------------------------------------------------------------- 1 | package domain; 2 | 3 | public interface Topics { 4 | String ORDERS = "orders"; 5 | String PAYMENTS = "payments"; 6 | String STOCK = "stock"; 7 | } 8 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: '2' 3 | services: 4 | zookeeper-1: 5 | container_name: ecommerce-zookeeper-1 6 | image: confluentinc/cp-zookeeper:latest 7 | environment: 8 | ZOOKEEPER_CLIENT_PORT: 2181 9 | ZOOKEEPER_TICK_TIME: 2000 10 | ports: 11 | - 22181:2181 12 | 13 | zookeeper-2: 14 | container_name: ecommerce-zookeeper-2 15 | image: confluentinc/cp-zookeeper:latest 16 | environment: 17 | ZOOKEEPER_CLIENT_PORT: 2181 18 | ZOOKEEPER_TICK_TIME: 2000 19 | ports: 20 | - 32181:2181 21 | 22 | kafka-1: 23 | container_name: ecommerce-kafka-1 24 | image: confluentinc/cp-kafka:latest 25 | depends_on: 26 | - zookeeper-1 27 | - zookeeper-2 28 | 29 | ports: 30 | - 29092:29092 31 | environment: 32 | KAFKA_BROKER_ID: 1 33 | KAFKA_ZOOKEEPER_CONNECT: zookeeper-1:2181,zookeeper-2:2181 34 | KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-1:9092,PLAINTEXT_HOST://localhost:29092 35 | KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT 36 | KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT 37 | KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 38 | kafka-2: 39 | container_name: ecommerce-kafka-2 40 | image: confluentinc/cp-kafka:latest 41 | depends_on: 42 | - zookeeper-1 43 | - zookeeper-2 44 | ports: 45 | - 39092:39092 46 | environment: 47 | KAFKA_BROKER_ID: 2 48 | KAFKA_ZOOKEEPER_CONNECT: zookeeper-1:2181,zookeeper-2:2181 49 | KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-2:9092,PLAINTEXT_HOST://localhost:39092 50 | KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT 51 | KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT 52 | KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 53 | 54 | mysql: 55 | container_name: ecommerce-mysql 56 | image: mysql:latest 57 | volumes: 58 | - "./.mysql-data/db:/var/lib/mysql" 59 | restart: always 60 | ports: 61 | - 33066:3306 62 | environment: 63 | MYSQL_ROOT_PASSWORD: 1234 64 | MYSQL_DATABASE: commerce 65 | MYSQL_USER: john.doe 66 | MYSQL_PASSWORD: 1234 -------------------------------------------------------------------------------- /inf/kafka.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 35 | 36 | -------------------------------------------------------------------------------- /inf/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdalton0518/Microservices-kafka-SpringBoot/9ad09852f7e635d71066eb8a193161260630042d/inf/overview.png -------------------------------------------------------------------------------- /inf/spring.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ms-orders/DockerFile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk11:jdk-11.0.2.9-slim 2 | WORKDIR /opt 3 | EXPOSE 8080 4 | COPY target/*.jar /opt/app.jar 5 | ENTRYPOINT exec java $JAVA_OPTS -jar app.jar -------------------------------------------------------------------------------- /ms-orders/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.zatribune.spring 7 | ecommerce 8 | 1.0-SNAPSHOT 9 | 10 | ms-orders 11 | 1.0-SNAPSHOT 12 | Orders Microservice (Orchestrator) 13 | 14 | 11 15 | 16 | 17 | 18 | com.zatribune.spring 19 | commons 20 | ${project.version} 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-data-jpa 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-web 29 | 30 | 31 | org.apache.kafka 32 | kafka-streams 33 | 34 | 35 | org.springframework.kafka 36 | spring-kafka 37 | 38 | 39 | 40 | com.fasterxml.jackson.core 41 | jackson-databind 42 | 43 | 44 | mysql 45 | mysql-connector-java 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-starter-test 50 | test 51 | 52 | 53 | org.springframework.kafka 54 | spring-kafka-test 55 | test 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-maven-plugin 64 | 65 | 66 | 67 | org.projectlombok 68 | lombok 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /ms-orders/src/main/java/com/zatribune/spring/ecommerce/orders/OrderApplication.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.orders; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.kafka.annotation.EnableKafkaStreams; 6 | import org.springframework.scheduling.annotation.EnableAsync; 7 | 8 | @EnableAsync 9 | @EnableKafkaStreams 10 | @SpringBootApplication 11 | public class OrderApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(OrderApplication.class, args); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /ms-orders/src/main/java/com/zatribune/spring/ecommerce/orders/config/KafkaConfig.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.orders.config; 2 | 3 | 4 | import domain.Order; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.kafka.clients.admin.NewTopic; 7 | import org.apache.kafka.common.serialization.Serde; 8 | import org.apache.kafka.common.serialization.Serdes; 9 | import org.apache.kafka.streams.StreamsBuilder; 10 | import org.apache.kafka.streams.kstream.*; 11 | import org.apache.kafka.streams.state.KeyValueBytesStoreSupplier; 12 | import org.apache.kafka.streams.state.Stores; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.context.annotation.Bean; 15 | import org.springframework.context.annotation.Configuration; 16 | import org.springframework.core.task.TaskExecutor; 17 | import org.springframework.kafka.config.TopicBuilder; 18 | import org.springframework.kafka.support.serializer.JsonSerde; 19 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 20 | import com.zatribune.spring.ecommerce.orders.service.OrderService; 21 | 22 | import java.time.Duration; 23 | 24 | import static domain.Topics.*; 25 | 26 | 27 | @Slf4j 28 | @Configuration 29 | public class KafkaConfig { 30 | 31 | 32 | private final OrderService orderService; 33 | 34 | 35 | @Autowired 36 | public KafkaConfig(OrderService orderService) { 37 | this.orderService = orderService; 38 | } 39 | 40 | @Bean 41 | public NewTopic orders() { 42 | return TopicBuilder.name(ORDERS) 43 | .partitions(3) 44 | .compact() 45 | .build(); 46 | } 47 | 48 | @Bean 49 | public NewTopic paymentTopic() { 50 | return TopicBuilder.name(PAYMENTS) 51 | .partitions(3) 52 | .compact() 53 | .build(); 54 | } 55 | 56 | @Bean 57 | public NewTopic stockTopic() { 58 | return TopicBuilder.name(STOCK) 59 | .partitions(3) 60 | .compact() 61 | .build(); 62 | } 63 | 64 | //this is the one that will get results from other topics to check 65 | @Bean 66 | public KStream stream(StreamsBuilder builder) { 67 | //provides serialization and deserialization in JSON format 68 | 69 | 70 | Serde keySerde = Serdes.Long();// same as // Serdes.LongSerde keySerde=new Serdes.LongSerde(); 71 | JsonSerde valueSerde = new JsonSerde<>(Order.class); 72 | 73 | // Kafka stream => it's a record stream that represents key & value pairs 74 | // key and value serdes means Key & value serializers & deserializers 75 | //of course, the key in our case is the order id 76 | KStream paymentStream = builder 77 | .stream(PAYMENTS, Consumed.with(keySerde, valueSerde));//Consumed With == passing some parameters for configuring the generated stream 78 | 79 | KStream stockStream = builder 80 | .stream(STOCK,Consumed.with(keySerde, valueSerde)); 81 | 82 | //join records from both tables 83 | paymentStream.join( 84 | stockStream, 85 | orderService::confirm,//the value joiner == the one responsible for joining the two records 86 | JoinWindows.of(Duration.ofSeconds(10)), // timestamps of matched records must fall within this window of time 87 | StreamJoined.with(keySerde, valueSerde, valueSerde)//the key must be the same, 1st stream serde, 2nd stream serde 88 | ) 89 | .peek((k,v)->log.info("Kafka stream match: key[{}],value[{}]",k,v)) 90 | .to(ORDERS); 91 | 92 | return paymentStream; 93 | } 94 | /** 95 | * To build a persistent key-value store 96 | * This KTable will be used to store all the Orders 97 | ***/ 98 | 99 | @Bean 100 | public KTable table(StreamsBuilder builder) { 101 | 102 | KeyValueBytesStoreSupplier store = Stores.persistentKeyValueStore(ORDERS); 103 | 104 | Serde keySerde = Serdes.Long(); 105 | JsonSerde valueSerde = new JsonSerde<>(Order.class); 106 | 107 | KStream stream = builder 108 | .stream(ORDERS, Consumed.with(keySerde, valueSerde)) 109 | .peek((k,v)->log.info("Kafka persistence table: key[{}],value[{}]",k,v)); 110 | 111 | return stream.toTable(Materialized.as(store) 112 | .withKeySerde(keySerde) 113 | .withValueSerde(valueSerde)); 114 | } 115 | 116 | @Bean 117 | public TaskExecutor taskExecutor() { 118 | ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 119 | executor.setCorePoolSize(5); 120 | executor.setMaxPoolSize(5); 121 | executor.setThreadNamePrefix("kafkaSender-"); 122 | executor.initialize(); 123 | return executor; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /ms-orders/src/main/java/com/zatribune/spring/ecommerce/orders/controller/OrderController.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.orders.controller; 2 | 3 | 4 | import domain.Order; 5 | import domain.OrderStatus; 6 | import domain.Topics; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.apache.kafka.streams.StoreQueryParameters; 9 | import org.apache.kafka.streams.state.KeyValueIterator; 10 | import org.apache.kafka.streams.state.QueryableStoreTypes; 11 | import org.apache.kafka.streams.state.ReadOnlyKeyValueStore; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.kafka.config.StreamsBuilderFactoryBean; 14 | import org.springframework.kafka.core.KafkaTemplate; 15 | import org.springframework.kafka.support.SendResult; 16 | import org.springframework.web.bind.annotation.*; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.concurrent.CompletableFuture; 21 | import java.util.concurrent.ExecutionException; 22 | import java.util.concurrent.atomic.AtomicLong; 23 | 24 | @Slf4j 25 | @RequestMapping("/orders") 26 | @RestController 27 | public class OrderController { 28 | 29 | 30 | private final AtomicLong id = new AtomicLong(); 31 | private final KafkaTemplate kafkaTemplate; 32 | private final StreamsBuilderFactoryBean kafkaStreamsFactory; 33 | 34 | 35 | @Autowired 36 | public OrderController(KafkaTemplate kafkaTemplate, 37 | StreamsBuilderFactoryBean kafkaStreamsFactory) { 38 | this.kafkaTemplate = kafkaTemplate; 39 | this.kafkaStreamsFactory = kafkaStreamsFactory; 40 | } 41 | 42 | @PostMapping 43 | public Order create(@RequestBody Order order) throws ExecutionException, InterruptedException { 44 | order.setId(id.incrementAndGet()); 45 | order.setStatus(OrderStatus.NEW); 46 | log.info("Sent: {}", order); 47 | return kafkaTemplate.send(Topics.ORDERS, order.getId(), order).get().getProducerRecord().value(); 48 | } 49 | 50 | @GetMapping 51 | public List all() { 52 | List orders = new ArrayList<>(); 53 | ReadOnlyKeyValueStore store = kafkaStreamsFactory 54 | .getKafkaStreams() 55 | .store(StoreQueryParameters.fromNameAndType( 56 | Topics.ORDERS, 57 | QueryableStoreTypes.keyValueStore())); 58 | KeyValueIterator it = store.all(); 59 | it.forEachRemaining(kv -> orders.add(kv.value)); 60 | return orders; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ms-orders/src/main/java/com/zatribune/spring/ecommerce/orders/service/OrderService.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.orders.service; 2 | 3 | import domain.Order; 4 | 5 | public interface OrderService { 6 | 7 | Order confirm(Order orderPayment, Order orderStock); 8 | } 9 | -------------------------------------------------------------------------------- /ms-orders/src/main/java/com/zatribune/spring/ecommerce/orders/service/OrderServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.orders.service; 2 | 3 | import domain.Order; 4 | import domain.OrderSource; 5 | import domain.OrderStatus; 6 | import org.springframework.stereotype.Service; 7 | 8 | import static domain.OrderStatus.*; 9 | import static domain.OrderSource.*; 10 | 11 | @Service 12 | public class OrderServiceImpl implements OrderService{ 13 | 14 | 15 | @Override 16 | public Order confirm(Order orderPayment, Order orderStock) { 17 | 18 | Order o = Order.builder() 19 | .id(orderPayment.getId()) 20 | .customerId(orderPayment.getCustomerId()) 21 | .productId(orderPayment.getProductId()) 22 | .productCount(orderPayment.getProductCount()) 23 | .price(orderPayment.getPrice()) 24 | .build(); 25 | 26 | if (orderPayment.getStatus().equals(ACCEPT) && 27 | orderStock.getStatus().equals(ACCEPT)) { 28 | o.setStatus(CONFIRMED); 29 | } else if (orderPayment.getStatus().equals(REJECT) && 30 | orderStock.getStatus().equals(REJECT)) { 31 | o.setStatus(REJECTED); 32 | } else if (orderPayment.getStatus().equals(REJECT) || 33 | orderStock.getStatus().equals(REJECT)) { 34 | OrderSource source = orderPayment.getStatus().equals(REJECT) 35 | ? PAYMENT : STOCK; 36 | o.setStatus(OrderStatus.ROLLBACK); 37 | o.setSource(source); 38 | } 39 | return o; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /ms-orders/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application.name: ms-orders 3 | jpa: 4 | open-in-view: false 5 | hibernate: 6 | ddl-auto: create-drop 7 | datasource: 8 | url: jdbc:mysql://127.0.0.1:33066/commerce 9 | username: root 10 | password: 1234 11 | h2: 12 | console: 13 | enabled: true 14 | 15 | spring.kafka: 16 | bootstrap-servers: 127.0.0.1:39092, 127.0.0.1:29092 17 | producer: 18 | key-serializer: org.apache.kafka.common.serialization.LongSerializer 19 | value-serializer: org.springframework.kafka.support.serializer.JsonSerializer 20 | streams: 21 | properties: 22 | default.key.serde: org.apache.kafka.common.serialization.Serdes$LongSerde 23 | default.value.serde: org.springframework.kafka.support.serializer.JsonSerde 24 | spring.json.trusted.packages: "*" 25 | state-dir: /tmp/kafka-streams/ 26 | 27 | 28 | spring.output.ansi.enabled: ALWAYS 29 | 30 | logging.pattern.console: "%clr(%d{HH:mm:ss.SSS}){blue} %clr(---){faint} %clr([%15.15t]){yellow} %clr(:){red} %clr(%m){faint}%n" 31 | 32 | 33 | server: 34 | port: 9091 35 | -------------------------------------------------------------------------------- /ms-orders/src/test/java/com/zatribune/spring/ecommerce/orders/OrderApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.orders; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class OrderApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /ms-payment/DockerFile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk11:jdk-11.0.2.9-slim 2 | WORKDIR /opt 3 | EXPOSE 8080 4 | COPY target/*.jar /opt/app.jar 5 | ENTRYPOINT exec java $JAVA_OPTS -jar app.jar -------------------------------------------------------------------------------- /ms-payment/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.zatribune.spring 7 | ecommerce 8 | 1.0-SNAPSHOT 9 | 10 | 11 | ms-payment 12 | 1.0-SNAPSHOT 13 | Payments Microservice 14 | 15 | 11 16 | 17 | 18 | 19 | com.zatribune.spring 20 | commons 21 | ${project.version} 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-data-jpa 26 | 27 | 28 | org.springframework.kafka 29 | spring-kafka 30 | 31 | 32 | 33 | com.fasterxml.jackson.core 34 | jackson-databind 35 | 36 | 37 | mysql 38 | mysql-connector-java 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | org.springframework.kafka 47 | spring-kafka-test 48 | test 49 | 50 | 51 | 52 | 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-maven-plugin 57 | 58 | 59 | 60 | org.projectlombok 61 | lombok 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /ms-payment/src/main/java/com/zatribune/spring/ecommerce/payments/PaymentApplication.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.payments; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.kafka.annotation.EnableKafka; 6 | 7 | 8 | @EnableKafka 9 | @SpringBootApplication 10 | public class PaymentApplication { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(PaymentApplication.class, args); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /ms-payment/src/main/java/com/zatribune/spring/ecommerce/payments/db/DevBootstrap.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.payments.db; 2 | 3 | 4 | import com.zatribune.spring.ecommerce.payments.db.entities.Customer; 5 | import com.zatribune.spring.ecommerce.payments.db.repository.CustomerRepository; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.CommandLineRunner; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.util.List; 12 | 13 | 14 | @Slf4j 15 | @Component 16 | public class DevBootstrap implements CommandLineRunner { 17 | 18 | 19 | private final CustomerRepository repository; 20 | 21 | @Autowired 22 | public DevBootstrap(CustomerRepository repository) { 23 | this.repository = repository; 24 | } 25 | 26 | @Override 27 | public void run(String... args) throws Exception { 28 | Customer p1=Customer.builder().name("John Doe") 29 | .amountAvailable(4000) 30 | .amountReserved(0) 31 | .build(); 32 | 33 | Customer p2=Customer.builder().name("Muhammad Ali") 34 | .amountAvailable(8000) 35 | .amountReserved(0) 36 | .build(); 37 | 38 | Customer p3=Customer.builder().name("Steve Jobs") 39 | .amountAvailable(1000) 40 | .amountReserved(0) 41 | .build(); 42 | 43 | Customer p4=Customer.builder().name("Bill Gits") 44 | .amountAvailable(2000) 45 | .amountReserved(0) 46 | .build(); 47 | 48 | repository.saveAll(List.of(p1,p2,p3,p4)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ms-payment/src/main/java/com/zatribune/spring/ecommerce/payments/db/entities/Customer.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.payments.db.entities; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.Entity; 6 | import javax.persistence.GeneratedValue; 7 | import javax.persistence.GenerationType; 8 | import javax.persistence.Id; 9 | 10 | 11 | @Getter 12 | @Setter 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | @Builder 16 | @Entity 17 | public class Customer { 18 | 19 | @Id 20 | @GeneratedValue(strategy = GenerationType.IDENTITY) 21 | private Long id; 22 | private String name; 23 | private int amountAvailable; 24 | private int amountReserved; 25 | 26 | @Override 27 | public String toString() { 28 | return "Customer{" + 29 | "id=" + id + 30 | ", name='" + name + '\'' + 31 | ", amountAvailable=" + amountAvailable + 32 | ", amountReserved=" + amountReserved + 33 | '}'; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ms-payment/src/main/java/com/zatribune/spring/ecommerce/payments/db/repository/CustomerRepository.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.payments.db.repository; 2 | 3 | import com.zatribune.spring.ecommerce.payments.db.entities.Customer; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface CustomerRepository extends JpaRepository { 7 | } -------------------------------------------------------------------------------- /ms-payment/src/main/java/com/zatribune/spring/ecommerce/payments/listener/OrderListener.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.payments.listener; 2 | 3 | 4 | import domain.*; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.kafka.annotation.KafkaListener; 8 | import org.springframework.stereotype.Component; 9 | import com.zatribune.spring.ecommerce.payments.service.OrderService; 10 | 11 | @Slf4j 12 | @Component 13 | public class OrderListener { 14 | 15 | 16 | private final OrderService orderService; 17 | 18 | @Autowired 19 | public OrderListener(OrderService orderService) { 20 | this.orderService = orderService; 21 | } 22 | 23 | 24 | @KafkaListener(id = KafkaIds.ORDERS, topics = Topics.ORDERS, groupId = KafkaGroupIds.PAYMENTS) 25 | public void onEvent(Order o) { 26 | log.info("Received: {}" , o); 27 | if (o.getStatus().equals(OrderStatus.NEW)) 28 | orderService.reserve(o); 29 | else 30 | orderService.confirm(o); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ms-payment/src/main/java/com/zatribune/spring/ecommerce/payments/service/OrderService.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.payments.service; 2 | 3 | import domain.Order; 4 | 5 | public interface OrderService { 6 | 7 | void reserve(Order order); 8 | 9 | void confirm(Order order); 10 | } 11 | -------------------------------------------------------------------------------- /ms-payment/src/main/java/com/zatribune/spring/ecommerce/payments/service/OrderServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.payments.service; 2 | 3 | 4 | import com.zatribune.spring.ecommerce.payments.db.entities.Customer; 5 | import com.zatribune.spring.ecommerce.payments.db.repository.CustomerRepository; 6 | import domain.Order; 7 | import domain.OrderSource; 8 | import domain.Topics; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.kafka.core.KafkaTemplate; 11 | import org.springframework.stereotype.Service; 12 | 13 | import static domain.OrderStatus.*; 14 | 15 | @Slf4j 16 | @Service 17 | public class OrderServiceImpl implements OrderService{ 18 | 19 | private static final OrderSource SOURCE = OrderSource.PAYMENT; 20 | private final CustomerRepository repository; 21 | private final KafkaTemplate template; 22 | 23 | public OrderServiceImpl(CustomerRepository repository, KafkaTemplate template) { 24 | this.repository = repository; 25 | this.template = template; 26 | } 27 | 28 | @Override 29 | public void reserve(Order order) { 30 | Customer customer = repository.findById(order.getCustomerId()).orElseThrow(); 31 | log.info("reserve order [{}] , for customer[{}]",order.getId(), customer); 32 | if (order.getPrice() < customer.getAmountAvailable()) { 33 | order.setStatus(ACCEPT); 34 | customer.setAmountReserved(customer.getAmountReserved() + order.getPrice()); 35 | customer.setAmountAvailable(customer.getAmountAvailable() - order.getPrice()); 36 | } else { 37 | order.setStatus(REJECTED); 38 | } 39 | order.setSource(SOURCE); 40 | repository.save(customer); 41 | template.send(Topics.PAYMENTS, order.getId(), order); 42 | log.info("Sent: {}", order); 43 | } 44 | 45 | @Override 46 | public void confirm(Order order) { 47 | Customer customer = repository.findById(order.getCustomerId()).orElseThrow(); 48 | log.info("confirm order [{}] , for customer[{}]",order.getId(), customer); 49 | if (order.getStatus().equals(CONFIRMED)) { 50 | customer.setAmountReserved(customer.getAmountReserved() - order.getPrice()); 51 | repository.save(customer); 52 | } else if (order.getStatus().equals(ROLLBACK) && !order.getSource().equals(SOURCE)) { 53 | customer.setAmountReserved(customer.getAmountReserved() - order.getPrice()); 54 | customer.setAmountAvailable(customer.getAmountAvailable() + order.getPrice()); 55 | repository.save(customer); 56 | } 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ms-payment/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application.name: ms-payments 3 | jpa: 4 | open-in-view: false 5 | hibernate: 6 | ddl-auto: create-drop 7 | show-sql: true 8 | datasource: 9 | url: jdbc:mysql://127.0.0.1:33066/commerce 10 | username: root 11 | password: 1234 12 | 13 | spring.kafka: 14 | bootstrap-servers: 127.0.0.1:39092, 127.0.0.1:29092 15 | consumer: 16 | key-deserializer: org.apache.kafka.common.serialization.LongDeserializer 17 | value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer 18 | properties: 19 | spring.json.trusted.packages: "*" 20 | producer: 21 | key-serializer: org.apache.kafka.common.serialization.LongSerializer 22 | value-serializer: org.springframework.kafka.support.serializer.JsonSerializer 23 | 24 | spring.output.ansi.enabled: ALWAYS 25 | 26 | logging.pattern.console: "%clr(%d{HH:mm:ss.SSS}){blue} %clr(---){faint} %clr([%15.15t]){yellow} %clr(:){red} %clr(%m){faint}%n" 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /ms-payment/src/test/java/com/zatribune/spring/ecommerce/payments/PaymentApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.payments; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class PaymentApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /ms-stock/DockerFile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk11:jdk-11.0.2.9-slim 2 | WORKDIR /opt 3 | EXPOSE 8080 4 | COPY target/*.jar /opt/app.jar 5 | ENTRYPOINT exec java $JAVA_OPTS -jar app.jar -------------------------------------------------------------------------------- /ms-stock/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | com.zatribune.spring 8 | ecommerce 9 | 1.0-SNAPSHOT 10 | 11 | 12 | ms-stock 13 | 1.0-SNAPSHOT 14 | Stock Microservice 15 | 16 | 17 | 11 18 | 19 | 20 | 21 | com.zatribune.spring 22 | commons 23 | ${project.version} 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-data-jpa 28 | 29 | 30 | org.springframework.kafka 31 | spring-kafka 32 | 33 | 34 | 35 | com.fasterxml.jackson.core 36 | jackson-databind 37 | 38 | 39 | mysql 40 | mysql-connector-java 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-starter-test 45 | test 46 | 47 | 48 | org.springframework.kafka 49 | spring-kafka-test 50 | test 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.apache.maven.plugins 59 | maven-jar-plugin 60 | 3.2.2 61 | 62 | 63 | 64 | true 65 | lib/ 66 | com.zatribune.spring.ecommerce.stock.StockApplication 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /ms-stock/src/main/java/com/zatribune/spring/ecommerce/stock/StockApplication.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.stock; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.kafka.annotation.EnableKafka; 6 | 7 | @EnableKafka 8 | @SpringBootApplication 9 | public class StockApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(StockApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /ms-stock/src/main/java/com/zatribune/spring/ecommerce/stock/db/DevBootstrap.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.stock.db; 2 | 3 | 4 | import com.zatribune.spring.ecommerce.stock.db.entities.Product; 5 | import com.zatribune.spring.ecommerce.stock.db.repository.ProductRepository; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.CommandLineRunner; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.util.List; 12 | 13 | 14 | @Slf4j 15 | @Component 16 | public class DevBootstrap implements CommandLineRunner { 17 | 18 | 19 | private final ProductRepository repository; 20 | 21 | @Autowired 22 | public DevBootstrap(ProductRepository repository) { 23 | this.repository = repository; 24 | } 25 | 26 | @Override 27 | public void run(String... args) throws Exception { 28 | Product p1=Product.builder().name("Chicken") 29 | .availableItems(5000) 30 | .reservedItems(0) 31 | .build(); 32 | 33 | Product p2=Product.builder().name("Rice") 34 | .availableItems(3500) 35 | .reservedItems(0) 36 | .build(); 37 | 38 | Product p3=Product.builder().name("Spaghetti") 39 | .availableItems(100) 40 | .reservedItems(0) 41 | .build(); 42 | 43 | Product p4=Product.builder().name("Salad") 44 | .availableItems(120) 45 | .reservedItems(0) 46 | .build(); 47 | 48 | repository.saveAll(List.of(p1,p2,p3,p4)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ms-stock/src/main/java/com/zatribune/spring/ecommerce/stock/db/entities/Product.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.stock.db.entities; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.Entity; 6 | import javax.persistence.GeneratedValue; 7 | import javax.persistence.GenerationType; 8 | import javax.persistence.Id; 9 | 10 | @Getter 11 | @Setter 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | @Builder 15 | @Entity 16 | public class Product { 17 | 18 | @Id 19 | @GeneratedValue(strategy = GenerationType.IDENTITY) 20 | private Long id; 21 | private String name; 22 | private int availableItems; 23 | private int reservedItems; 24 | 25 | @Override 26 | public String toString() { 27 | return "Product{" + 28 | "id=" + id + 29 | ", name='" + name + '\'' + 30 | ", availableItems=" + availableItems + 31 | ", reservedItems=" + reservedItems + 32 | '}'; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ms-stock/src/main/java/com/zatribune/spring/ecommerce/stock/db/repository/ProductRepository.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.stock.db.repository; 2 | 3 | import com.zatribune.spring.ecommerce.stock.db.entities.Product; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface ProductRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /ms-stock/src/main/java/com/zatribune/spring/ecommerce/stock/listener/OrderListener.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.stock.listener; 2 | 3 | 4 | import domain.*; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.kafka.annotation.KafkaListener; 8 | import org.springframework.stereotype.Component; 9 | import com.zatribune.spring.ecommerce.stock.service.OrderService; 10 | 11 | @Slf4j 12 | @Component 13 | public class OrderListener { 14 | 15 | private final OrderService orderService; 16 | 17 | @Autowired 18 | public OrderListener(OrderService orderService) { 19 | this.orderService = orderService; 20 | } 21 | 22 | @KafkaListener(id = KafkaIds.ORDERS, topics = Topics.ORDERS, groupId = KafkaGroupIds.STOCK) 23 | public void onEvent(Order o) { 24 | log.info("Received: {}" , o); 25 | if (o.getStatus().equals(OrderStatus.NEW)) 26 | orderService.reserve(o); 27 | else 28 | orderService.confirm(o); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ms-stock/src/main/java/com/zatribune/spring/ecommerce/stock/service/OrderService.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.stock.service; 2 | 3 | import domain.Order; 4 | 5 | public interface OrderService { 6 | 7 | void reserve(Order order); 8 | 9 | void confirm(Order order); 10 | } 11 | -------------------------------------------------------------------------------- /ms-stock/src/main/java/com/zatribune/spring/ecommerce/stock/service/OrderServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.stock.service; 2 | 3 | import com.zatribune.spring.ecommerce.stock.db.entities.Product; 4 | import com.zatribune.spring.ecommerce.stock.db.repository.ProductRepository; 5 | import domain.OrderSource; 6 | import domain.OrderStatus; 7 | import domain.Topics; 8 | import domain.Order; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.kafka.core.KafkaTemplate; 11 | import org.springframework.stereotype.Service; 12 | 13 | 14 | @Slf4j 15 | @Service 16 | public class OrderServiceImpl implements OrderService{ 17 | 18 | private static final OrderSource SOURCE = OrderSource.STOCK; 19 | private final ProductRepository repository; 20 | private final KafkaTemplate template; 21 | 22 | public OrderServiceImpl(ProductRepository repository, KafkaTemplate template) { 23 | this.repository = repository; 24 | this.template = template; 25 | } 26 | 27 | @Override 28 | public void reserve(Order order) { 29 | Product product = repository.findById(order.getProductId()).orElseThrow(); 30 | log.info("Found: {}", product); 31 | if (order.getStatus().equals(OrderStatus.NEW)) { 32 | if (order.getProductCount() < product.getAvailableItems()) { 33 | product.setReservedItems(product.getReservedItems() + order.getProductCount()); 34 | product.setAvailableItems(product.getAvailableItems() - order.getProductCount()); 35 | order.setStatus(OrderStatus.ACCEPT); 36 | repository.save(product); 37 | } else { 38 | order.setStatus(OrderStatus.REJECT); 39 | } 40 | template.send(Topics.STOCK, order.getId(), order); 41 | log.info("Sent: {}", order); 42 | } 43 | } 44 | 45 | @Override 46 | public void confirm(Order order) { 47 | Product product = repository.findById(order.getProductId()).orElseThrow(); 48 | log.info("Found: {}", product); 49 | if (order.getStatus().equals(OrderStatus.CONFIRMED)) { 50 | product.setReservedItems(product.getReservedItems() - order.getProductCount()); 51 | repository.save(product); 52 | } else if (order.getStatus().equals(OrderStatus.ROLLBACK) && !order.getSource().equals(SOURCE)) { 53 | product.setReservedItems(product.getReservedItems() - order.getProductCount()); 54 | product.setAvailableItems(product.getAvailableItems() + order.getProductCount()); 55 | repository.save(product); 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /ms-stock/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application.name: ms-stock 3 | jpa: 4 | open-in-view: false 5 | hibernate: 6 | ddl-auto: create-drop 7 | show-sql: true 8 | datasource: 9 | url: jdbc:mysql://127.0.0.1:33066/commerce 10 | username: root 11 | password: 1234 12 | 13 | spring.kafka: 14 | bootstrap-servers: 127.0.0.1:39092, 127.0.0.1:29092 15 | consumer: 16 | key-deserializer: org.apache.kafka.common.serialization.LongDeserializer 17 | value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer 18 | properties: 19 | spring.json.trusted.packages: "*" 20 | producer: 21 | key-serializer: org.apache.kafka.common.serialization.LongSerializer 22 | value-serializer: org.springframework.kafka.support.serializer.JsonSerializer 23 | 24 | spring.output.ansi.enabled: ALWAYS 25 | 26 | logging.pattern.console: "%clr(%d{HH:mm:ss.SSS}){blue} %clr(---){faint} %clr([%15.15t]){yellow} %clr(:){red} %clr(%m){faint}%n" 27 | -------------------------------------------------------------------------------- /ms-stock/src/test/java/com/zatribune/spring/ecommerce/stock/StockApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.zatribune.spring.ecommerce.stock; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class StockApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.zatribune.spring 8 | ecommerce 9 | pom 10 | 1.0-SNAPSHOT 11 | 12 | An E-commerce Microservices Application that demonstrates Event-driven architecture using Kafka. 13 | 14 | 15 | 16 | commons 17 | ms-orders 18 | ms-payment 19 | ms-stock 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-parent 24 | 2.6.3 25 | 26 | 27 | 28 | 29 | 30 | org.projectlombok 31 | lombok 32 | true 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-maven-plugin 41 | 42 | 43 | org.apache.maven.plugins 44 | maven-release-plugin 45 | 46 | 47 | install 48 | 49 | 50 | 51 | 52 | 53 | --------------------------------------------------------------------------------