├── .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 | [](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 |
6 |
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 |
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 |
--------------------------------------------------------------------------------