├── README.md
├── docker-compose.yml
├── pom.xml
├── src
└── main
│ ├── java
│ └── com
│ │ └── lostsys
│ │ └── sample
│ │ └── hexagonal
│ │ ├── Application.java
│ │ ├── application
│ │ ├── CustomerUseCase.java
│ │ ├── MessageBrokerUseCase.java
│ │ └── OrderUserCase.java
│ │ ├── domain
│ │ ├── Customer.java
│ │ └── Orders.java
│ │ └── infra
│ │ ├── inputadapter
│ │ ├── http
│ │ │ ├── CustomerAPI.java
│ │ │ └── OrderAPI.java
│ │ └── message
│ │ │ └── KafkaMessage.java
│ │ ├── inputport
│ │ ├── CustomerInputPort.java
│ │ ├── MessageBrokerInputPort.java
│ │ └── OrderInputPort.java
│ │ ├── outputadapter
│ │ ├── postgresrepository
│ │ │ └── PostgresRepository.java
│ │ └── redisrepository
│ │ │ └── RedisRepository.java
│ │ ├── outputport
│ │ ├── CommandRepository.java
│ │ └── QueryRepository.java
│ │ └── utils
│ │ └── ConversionUtils.java
│ └── resources
│ ├── application.properties
│ └── schema.sql
├── testReadCQRS.sh
├── testReadNormal.sh
└── testWriter.sh
/README.md:
--------------------------------------------------------------------------------
1 | # java-springboot-cqrs-architecture-sample
2 | CQRS architecture sample using Java and Springboot
3 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.5'
2 |
3 | services:
4 | zookeeper:
5 | container_name: zookeeper
6 | image: docker.io/bitnami/zookeeper:3.7
7 | ports:
8 | - "2181:2181"
9 | environment:
10 | - ALLOW_ANONYMOUS_LOGIN=yes
11 | kafka:
12 | container_name: kafka
13 | image: docker.io/bitnami/kafka:2
14 | ports:
15 | - "9092:9092"
16 | - "29092:29092"
17 | environment:
18 | - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,EXTERNALPLAINTEXT://:29092
19 | - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNALPLAINTEXT://localhost:29092
20 | - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=PLAINTEXT:PLAINTEXT,EXTERNALPLAINTEXT:PLAINTEXT
21 | - KAFKA_INTER_BROKER_LISTENER_NAME=EXTERNALPLAINTEXT
22 | - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181
23 | - ALLOW_PLAINTEXT_LISTENER=yes
24 | depends_on:
25 | - zookeeper
26 | postgres:
27 | container_name: postgres
28 | image: postgres:11
29 | environment:
30 | POSTGRES_USER: postgres
31 | POSTGRES_PASSWORD: postgres
32 | POSTGRES_DB: postgresdb
33 | PGDATA: /data/postgres
34 | ports:
35 | - "5432:5432"
36 | command: [ "postgres", "-c", "wal_level=logical", "-c", "max_wal_senders=1" , "-c", "max_replication_slots=1" ]
37 | restart: unless-stopped
38 |
39 | debezium:
40 | container_name: debezium
41 | image: debezium/connect
42 | environment:
43 | GROUP_ID: 1
44 | CONFIG_STORAGE_TOPIC: my-connect-configs
45 | OFFSET_STORAGE_TOPIC: my-connect-offsets
46 | BOOTSTRAP_SERVERS: kafka:9092
47 | ADVERTISED_HOST_NAME: debezium
48 | ports:
49 | - "8083:8083"
50 | depends_on:
51 | - kafka
52 |
53 | redis:
54 | container_name: redis
55 | image: redis:7.0.5-alpine
56 | ports:
57 | - "6379:6379"
58 | command: [ "redis-server", "--requirepass", "SUPER_SECRET_PASSWORD" ]
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.lostsys
6 | sample-cqrs-architecture
7 | 1.0.0-SNAPSHOT
8 | jar
9 |
10 | Sample CQRS Architecture
11 |
12 |
13 |
14 | 11
15 | UTF-8
16 | UTF-8
17 | 2.7.3
18 | 1.18.24
19 | 42.4.2
20 | 2.13.4
21 |
22 |
23 |
24 |
25 |
26 | org.springframework.boot
27 | spring-boot-dependencies
28 | ${springboot.version}
29 | pom
30 | import
31 |
32 |
33 |
34 |
35 |
36 |
37 | org.springframework.boot
38 | spring-boot-starter-web
39 |
40 |
41 | org.springframework.boot
42 | spring-boot-starter-jdbc
43 |
44 |
45 | org.springframework.boot
46 | spring-boot-starter-data-redis
47 |
48 |
49 | org.springframework.kafka
50 | spring-kafka
51 |
52 |
53 | org.projectlombok
54 | lombok
55 | ${lombok.version}
56 | true
57 |
58 |
59 | org.postgresql
60 | postgresql
61 | runtime
62 |
63 |
64 | com.fasterxml.jackson.core
65 | jackson-databind
66 | ${jackson.version}
67 |
68 |
69 | redis.clients
70 | jedis
71 |
72 |
73 |
74 |
75 |
76 |
77 | true
78 | src/main/resources
79 |
80 |
81 |
82 |
83 | org.apache.maven.plugins
84 | maven-resources-plugin
85 | 3.2.0
86 |
87 |
88 | org.springframework.boot
89 | spring-boot-maven-plugin
90 | ${springboot.version}
91 |
92 |
93 |
94 | org.projectlombok
95 | lombok
96 |
97 |
98 |
99 |
100 |
101 |
102 | repackage
103 |
104 |
105 |
106 |
107 |
108 | org.apache.maven.plugins
109 | maven-compiler-plugin
110 | 3.8.1
111 |
112 | ${java.version}
113 | ${java.version}
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/src/main/java/com/lostsys/sample/hexagonal/Application.java:
--------------------------------------------------------------------------------
1 | package com.lostsys.sample.hexagonal;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 | import org.springframework.data.redis.connection.RedisConnectionFactory;
8 | import org.springframework.data.redis.core.RedisTemplate;
9 |
10 | @SpringBootApplication
11 | @Configuration
12 | public class Application {
13 |
14 | public static void main(String[] args) {
15 | SpringApplication.run(Application.class, args);
16 | }
17 |
18 | @Bean
19 | public RedisTemplate redisTemplate( RedisConnectionFactory connectionFactory ) {
20 | RedisTemplate template = new RedisTemplate<>();
21 | template.setConnectionFactory( connectionFactory );
22 | return template;
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/lostsys/sample/hexagonal/application/CustomerUseCase.java:
--------------------------------------------------------------------------------
1 | package com.lostsys.sample.hexagonal.application;
2 |
3 | import java.util.List;
4 | import java.util.UUID;
5 |
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.stereotype.Component;
8 |
9 | import com.lostsys.sample.hexagonal.domain.Customer;
10 | import com.lostsys.sample.hexagonal.infra.inputport.CustomerInputPort;
11 | import com.lostsys.sample.hexagonal.infra.outputport.CommandRepository;
12 |
13 | @Component
14 | public class CustomerUseCase implements CustomerInputPort {
15 |
16 | @Autowired
17 | CommandRepository entityRepository;
18 |
19 | @Override
20 | public Customer createCustomer(String name, String country) {
21 | Customer customer = Customer.builder()
22 | .id( UUID.randomUUID().toString() )
23 | .name( name )
24 | .country( country )
25 | .build();
26 |
27 | return entityRepository.save( customer );
28 | }
29 |
30 | @Override
31 | public Customer getById(String customerId) {
32 | return entityRepository.getById( customerId, Customer.class );
33 | }
34 |
35 | @Override
36 | public List getAll() {
37 | return entityRepository.getAll( Customer.class );
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/lostsys/sample/hexagonal/application/MessageBrokerUseCase.java:
--------------------------------------------------------------------------------
1 | package com.lostsys.sample.hexagonal.application;
2 |
3 | import java.util.List;
4 | import java.util.Map;
5 |
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.stereotype.Component;
8 |
9 | import com.lostsys.sample.hexagonal.domain.Customer;
10 | import com.lostsys.sample.hexagonal.domain.Orders;
11 | import com.lostsys.sample.hexagonal.infra.inputport.MessageBrokerInputPort;
12 | import com.lostsys.sample.hexagonal.infra.outputport.QueryRepository;
13 |
14 | @Component
15 | public class MessageBrokerUseCase implements MessageBrokerInputPort {
16 |
17 | @Autowired
18 | QueryRepository queryRepository;
19 |
20 | Map> classes = Map.of(
21 | "customer", Customer.class,
22 | "orders", Orders.class
23 | );
24 |
25 | @Override
26 | public void deleteReg(String table, Map reg) {
27 | queryRepository.delete( (String) reg.get("id"), classes.get( table ) );
28 | }
29 |
30 | @Override
31 | public void updateReg(String table, Map reg) {
32 | queryRepository.save( reg, classes.get( table ) );
33 | }
34 |
35 | @Override
36 | public void insertReg(String table, Map reg) {
37 | queryRepository.save( reg, classes.get( table ) );
38 | }
39 |
40 | @Override
41 | public List