├── 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 | --------------------------------------------------------------------------------