├── README.md
├── product-command-service
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── javatechie
│ │ │ ├── ProductCommandServiceApplication.java
│ │ │ ├── controller
│ │ │ └── ProductCommandController.java
│ │ │ ├── dto
│ │ │ └── ProductEvent.java
│ │ │ ├── entity
│ │ │ └── Product.java
│ │ │ ├── repository
│ │ │ └── ProductRepository.java
│ │ │ └── service
│ │ │ └── ProductCommandService.java
│ └── resources
│ │ ├── application.properties
│ │ └── application.yml
│ └── test
│ └── java
│ └── com
│ └── javatechie
│ └── ProductCommandServiceApplicationTests.java
└── product-query-service
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── javatechie
│ │ ├── ProductQueryServiceApplication.java
│ │ ├── controller
│ │ └── ProductQueryController.java
│ │ ├── dto
│ │ └── ProductEvent.java
│ │ ├── entity
│ │ └── Product.java
│ │ ├── repository
│ │ └── ProductRepository.java
│ │ └── service
│ │ └── ProductQueryService.java
└── resources
│ ├── application.properties
│ └── application.yml
└── test
└── java
└── com
└── javatechie
└── ProductQueryServiceApplicationTests.java
/README.md:
--------------------------------------------------------------------------------
1 | # cqrs-design-pattern
2 |
3 | ### CreateProduct Event
4 |
5 | ```
6 | curl --location 'http://localhost:9191/products' \
7 | --header 'Content-Type: application/json' \
8 | --data '{
9 | "type": "CreateProduct",
10 | "product": {
11 | "name": "Books",
12 | "description": "KK publication",
13 | "price": 999
14 | }
15 | }'
16 | ```
17 | ### UpdateProduct Event
18 |
19 | ```
20 | curl --location --request PUT 'http://localhost:9191/products/1' \
21 | --header 'Content-Type: application/json' \
22 | --data '{
23 | "type": "UpdateProduct",
24 | "product": {
25 | "id": 1,
26 | "name": "Watch",
27 | "description": "Samsung latest model",
28 | "price": 58000.0
29 | }
30 | }'
31 | ```
32 |
--------------------------------------------------------------------------------
/product-command-service/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 3.1.2
9 |
10 |
11 | com.javatechie
12 | product-command-service
13 | 0.0.1-SNAPSHOT
14 | product-command-service
15 | Demo project for Spring Boot
16 |
17 | 17
18 |
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter-data-jpa
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-starter-web
27 |
28 |
29 | org.springframework.kafka
30 | spring-kafka
31 |
32 |
33 | com.mysql
34 | mysql-connector-j
35 | runtime
36 |
37 |
38 | org.projectlombok
39 | lombok
40 | true
41 |
42 |
43 | org.springframework.boot
44 | spring-boot-starter-test
45 | test
46 |
47 |
48 |
49 |
50 |
51 |
52 | org.springframework.boot
53 | spring-boot-maven-plugin
54 |
55 |
56 |
57 | org.projectlombok
58 | lombok
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/product-command-service/src/main/java/com/javatechie/ProductCommandServiceApplication.java:
--------------------------------------------------------------------------------
1 | package com.javatechie;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class ProductCommandServiceApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(ProductCommandServiceApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/product-command-service/src/main/java/com/javatechie/controller/ProductCommandController.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.controller;
2 |
3 | import com.javatechie.dto.ProductEvent;
4 | import com.javatechie.entity.Product;
5 | import com.javatechie.service.ProductCommandService;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.web.bind.annotation.*;
8 |
9 | @RestController
10 | @RequestMapping("/products")
11 | public class ProductCommandController {
12 |
13 | @Autowired
14 | private ProductCommandService commandService;
15 |
16 | @PostMapping
17 | public Product createProduct(@RequestBody ProductEvent productEvent) {
18 | return commandService.createProduct(productEvent);
19 | }
20 |
21 | @PutMapping("/{id}")
22 | public Product updateProduct(@PathVariable long id, @RequestBody ProductEvent productEvent) {
23 | return commandService.updateProduct(id, productEvent);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/product-command-service/src/main/java/com/javatechie/dto/ProductEvent.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.dto;
2 |
3 | import com.javatechie.entity.Product;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | @Data
9 | @AllArgsConstructor
10 | @NoArgsConstructor
11 | public class ProductEvent {
12 |
13 | private String eventType;
14 | private Product product;
15 | }
16 |
--------------------------------------------------------------------------------
/product-command-service/src/main/java/com/javatechie/entity/Product.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.entity;
2 |
3 | import jakarta.persistence.Entity;
4 | import jakarta.persistence.GeneratedValue;
5 | import jakarta.persistence.Id;
6 | import jakarta.persistence.Table;
7 | import lombok.AllArgsConstructor;
8 | import lombok.Data;
9 | import lombok.NoArgsConstructor;
10 |
11 | @Entity
12 | @Table(name = "PRODUCT_COMMAND")
13 | @Data
14 | @AllArgsConstructor
15 | @NoArgsConstructor
16 | public class Product {
17 | @Id
18 | @GeneratedValue
19 | private Long id;
20 | private String name;
21 | private String description;
22 | private double price;
23 | }
24 |
--------------------------------------------------------------------------------
/product-command-service/src/main/java/com/javatechie/repository/ProductRepository.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.repository;
2 |
3 | import com.javatechie.entity.Product;
4 | import org.springframework.data.jpa.repository.JpaRepository;
5 |
6 | public interface ProductRepository extends JpaRepository {
7 | }
8 |
--------------------------------------------------------------------------------
/product-command-service/src/main/java/com/javatechie/service/ProductCommandService.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.service;
2 |
3 | import com.javatechie.dto.ProductEvent;
4 | import com.javatechie.entity.Product;
5 | import com.javatechie.repository.ProductRepository;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.kafka.core.KafkaTemplate;
8 | import org.springframework.stereotype.Service;
9 |
10 | @Service
11 | public class ProductCommandService {
12 |
13 | @Autowired
14 | private ProductRepository repository;
15 |
16 | @Autowired
17 | private KafkaTemplate kafkaTemplate;
18 |
19 | public Product createProduct(ProductEvent productEvent){
20 | Product productDO = repository.save(productEvent.getProduct());
21 | ProductEvent event=new ProductEvent("CreateProduct", productDO);
22 | kafkaTemplate.send("product-event-topic", event);
23 | return productDO;
24 | }
25 |
26 | public Product updateProduct(long id,ProductEvent productEvent){
27 | Product existingProduct = repository.findById(id).get();
28 | Product newProduct=productEvent.getProduct();
29 | existingProduct.setName(newProduct.getName());
30 | existingProduct.setPrice(newProduct.getPrice());
31 | existingProduct.setDescription(newProduct.getDescription());
32 | Product productDO = repository.save(existingProduct);
33 | ProductEvent event=new ProductEvent("UpdateProduct", productDO);
34 | kafkaTemplate.send("product-event-topic", event);
35 | return productDO;
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/product-command-service/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
2 | spring.datasource.url = jdbc:mysql://localhost:3306/javatechie
3 | spring.datasource.username = root
4 | spring.datasource.password = Password
5 | spring.jpa.show-sql = true
6 | spring.jpa.hibernate.ddl-auto = update
7 | spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect
8 | server.port=9191
9 |
--------------------------------------------------------------------------------
/product-command-service/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | kafka:
3 | producer:
4 | bootstrap-servers: localhost:9092
5 | key-serializer: org.apache.kafka.common.serialization.StringSerializer
6 | value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
--------------------------------------------------------------------------------
/product-command-service/src/test/java/com/javatechie/ProductCommandServiceApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.javatechie;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class ProductCommandServiceApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/product-query-service/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 3.1.2
9 |
10 |
11 | com.javatechie
12 | product-query-service
13 | 0.0.1-SNAPSHOT
14 | product-query-service
15 | Demo project for Spring Boot
16 |
17 | 17
18 |
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter-data-jpa
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-starter-web
27 |
28 |
29 | org.springframework.kafka
30 | spring-kafka
31 |
32 |
33 | com.mysql
34 | mysql-connector-j
35 | runtime
36 |
37 |
38 | org.projectlombok
39 | lombok
40 | true
41 |
42 |
43 | org.springframework.boot
44 | spring-boot-starter-test
45 | test
46 |
47 |
48 |
49 |
50 |
51 |
52 | org.springframework.boot
53 | spring-boot-maven-plugin
54 |
55 |
56 |
57 | org.projectlombok
58 | lombok
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/product-query-service/src/main/java/com/javatechie/ProductQueryServiceApplication.java:
--------------------------------------------------------------------------------
1 | package com.javatechie;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class ProductQueryServiceApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(ProductQueryServiceApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/product-query-service/src/main/java/com/javatechie/controller/ProductQueryController.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.controller;
2 |
3 | import com.javatechie.dto.ProductEvent;
4 | import com.javatechie.entity.Product;
5 | import com.javatechie.service.ProductQueryService;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.web.bind.annotation.GetMapping;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.bind.annotation.RestController;
10 |
11 | import java.util.List;
12 |
13 | @RequestMapping("/products")
14 | @RestController
15 | public class ProductQueryController {
16 |
17 | @Autowired
18 | private ProductQueryService queryService;
19 |
20 | @GetMapping
21 | public List fetchAllProducts(){
22 | return queryService.getProducts();
23 | }
24 |
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/product-query-service/src/main/java/com/javatechie/dto/ProductEvent.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.dto;
2 |
3 | import com.javatechie.entity.Product;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | @Data
9 | @AllArgsConstructor
10 | @NoArgsConstructor
11 | public class ProductEvent {
12 |
13 | private String eventType;
14 | private Product product;
15 | }
16 |
--------------------------------------------------------------------------------
/product-query-service/src/main/java/com/javatechie/entity/Product.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.entity;
2 |
3 | import jakarta.persistence.Entity;
4 | import jakarta.persistence.GeneratedValue;
5 | import jakarta.persistence.Id;
6 | import jakarta.persistence.Table;
7 | import lombok.AllArgsConstructor;
8 | import lombok.Data;
9 | import lombok.NoArgsConstructor;
10 |
11 | @Entity
12 | @Table(name = "PRODUCT_QUERY")
13 | @Data
14 | @AllArgsConstructor
15 | @NoArgsConstructor
16 | public class Product {
17 | @Id
18 | @GeneratedValue
19 | private Long id;
20 | private String name;
21 | private String description;
22 | private double price;
23 | }
24 |
--------------------------------------------------------------------------------
/product-query-service/src/main/java/com/javatechie/repository/ProductRepository.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.repository;
2 |
3 | import com.javatechie.entity.Product;
4 | import org.springframework.data.jpa.repository.JpaRepository;
5 |
6 | public interface ProductRepository extends JpaRepository {
7 | }
8 |
--------------------------------------------------------------------------------
/product-query-service/src/main/java/com/javatechie/service/ProductQueryService.java:
--------------------------------------------------------------------------------
1 | package com.javatechie.service;
2 |
3 | import com.javatechie.dto.ProductEvent;
4 | import com.javatechie.entity.Product;
5 | import com.javatechie.repository.ProductRepository;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.kafka.annotation.KafkaListener;
8 | import org.springframework.stereotype.Service;
9 |
10 | import java.util.List;
11 | import java.util.Optional;
12 |
13 | @Service
14 | public class ProductQueryService {
15 |
16 | @Autowired
17 | private ProductRepository repository;
18 |
19 | public List getProducts() {
20 | return repository.findAll();
21 | }
22 |
23 | @KafkaListener(topics = "product-event-topic",groupId = "product-event-group")
24 | public void processProductEvents(ProductEvent productEvent) {
25 | Product product = productEvent.getProduct();
26 | if (productEvent.getEventType().equals("CreateProduct")) {
27 | repository.save(product);
28 | }
29 | if (productEvent.getEventType().equals("UpdateProduct")) {
30 | Product existingProduct = repository.findById(product.getId()).get();
31 | existingProduct.setName(product.getName());
32 | existingProduct.setPrice(product.getPrice());
33 | existingProduct.setDescription(product.getDescription());
34 | repository.save(existingProduct);
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/product-query-service/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
2 | spring.datasource.url = jdbc:mysql://localhost:3306/javatechie
3 | spring.datasource.username = root
4 | spring.datasource.password = Password
5 | spring.jpa.show-sql = true
6 | spring.jpa.hibernate.ddl-auto = update
7 | spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect
8 | server.port=9292
9 |
10 |
--------------------------------------------------------------------------------
/product-query-service/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | kafka:
3 | consumer:
4 | bootstrap-servers: localhost:9092
5 | key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
6 | value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
7 | properties:
8 | spring:
9 | json:
10 | trusted:
11 | packages: com.javatechie.dto
--------------------------------------------------------------------------------
/product-query-service/src/test/java/com/javatechie/ProductQueryServiceApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.javatechie;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class ProductQueryServiceApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------