A bunch of ways of doing CQRS with various Spring tools.
These instructions will get you and overview of how to synchronize two different datasources. We will do so by separating command and queries in a simple CQRS app. Each module represents a different way of introducing this pattern. Also, each module is a standalone Spring Boot application.
What things you need to run the software:
Sample applications are based on a simple domain that serves credit cards. There are two usecases:
The important is that:
After a successful Withdraw command, a withdrawal should be seen in a result from list of withdrawals query.
Hence there is a need for some synchronization that makes state for commands and queries consistent.
Let's agree on a color code for commands, queries and synchronization. It will make our drawings consistent.
Code can be found under in-one-class module.
Running the app:
mvn spring-boot:run
A sample Withdraw command:
curl localhost:8080/withdrawals -X POST --header 'Content-Type: application/json' -d '{"card":"3a3e99f0-5ad9-47fa-961d-d75fab32ef0e", "amount": 10.00}' --verbose
Verifed by a query:
curl http://localhost:8080/withdrawals?cardId=3a3e99f0-5ad9-47fa-961d-d75fab32ef0e --verbose
Expected result:
[{"amount":10.00}]
Architecture overview:
Automatic E2E test for REST API can be found here:
@Test
public void shouldSynchronizeQuerySideAfterSendingACommand() {
// given
UUID cardUUid = thereIsCreditCardWithLimit(new BigDecimal(100)); //HTTP POST
// when
clientWantsToWithdraw(TEN, cardUUid); //HTTP GET
// then
thereIsOneWithdrawalOf(TEN, cardUUid);
}
Code can be found under explicit-with-dto module. Same version, but with JPA entities as results of a query can be found here.
Running the app:
mvn spring-boot:run
A sample Withdraw command:
curl localhost:8080/withdrawals -X POST --header 'Content-Type: application/json' -d '{"card":"3a3e99f0-5ad9-47fa-961d-d75fab32ef0e", "amount": 10.00}' --verbose
Verifed by a query:
curl http://localhost:8080/withdrawals?cardId=3a3e99f0-5ad9-47fa-961d-d75fab32ef0e --verbose
Expected result:
[{"amount":10.00}]
Architecture overview:
Automatic E2E test for REST API can be found here:
@Test
public void shouldSynchronizeQuerySideAfterSendingACommand() {
// given
UUID cardUUid = thereIsCreditCardWithLimit(new BigDecimal(100)); //HTTP POST
// when
clientWantsToWithdraw(TEN, cardUUid); //HTTP GET
// then
thereIsOneWithdrawalOf(TEN, cardUUid);
}
Code can be found under with-application-events module.
There is also a version with immutable domain module which just returns events. It Can be found here.
Running the app:
mvn spring-boot:run
A sample Withdraw command:
curl localhost:8080/withdrawals -X POST --header 'Content-Type: application/json' -d '{"card":"3a3e99f0-5ad9-47fa-961d-d75fab32ef0e", "amount": 10.00}' --verbose
Verifed by a query:
curl http://localhost:8080/withdrawals?cardId=3a3e99f0-5ad9-47fa-961d-d75fab32ef0e --verbose
Expected result:
[{"amount":10.00}]
Architecture overview:
Automatic E2E test for REST API can be found here:
@Test
public void shouldSynchronizeQuerySideAfterSendingACommand() {
// given
UUID cardUUid = thereIsCreditCardWithLimit(new BigDecimal(100)); //HTTP POST
// when
clientWantsToWithdraw(TEN, cardUUid); //HTTP GET
// then
thereIsOneWithdrawalOf(TEN, cardUUid);
}
Code can be found under trigger module.
Running the app:
mvn spring-boot:run
A sample Withdraw command:
curl localhost:8080/withdrawals -X POST --header 'Content-Type: application/json' -d '{"card":"3a3e99f0-5ad9-47fa-961d-d75fab32ef0e", "amount": 10.00}' --verbose
Verifed by a query:
curl http://localhost:8080/withdrawals?cardId=3a3e99f0-5ad9-47fa-961d-d75fab32ef0e --verbose
Expected result:
[{"amount":10.00}]
Architecture overview:
Automatic E2E test for REST API can be found here:
@Test
public void shouldSynchronizeQuerySideAfterSendingACommand() {
// given
UUID cardUUid = thereIsCreditCardWithLimit(new BigDecimal(100)); //HTTP POST
// when
clientWantsToWithdraw(TEN, cardUUid); //HTTP GET
// then
thereIsOneWithdrawalOf(TEN, cardUUid);
}
Synchronization done by listening to database's transaction log, which is a log of transactions accepted by a database management system.
Code can be found under with-log-tailing module.
Additional components:
Running the app, remember to be in root of the project:
ADVERTISED_LISTENERS=PLAINTEXT://YOUR_HOST_IP:9092
docker-compose up
curl -i -X POST -H "Accept:application/json" -H "Content-Type:application/json" http://localhost:8083/connectors/ -d @source.json --verbose
A sample Withdraw command:
curl localhost:8080/withdrawals -X POST --header 'Content-Type: application/json' -d '{"card":"3a3e99f0-5ad9-47fa-961d-d75fab32ef0e", "amount": 10.00}' --verbose
Verifed by a query:
curl http://localhost:8080/withdrawals?cardId=3a3e99f0-5ad9-47fa-961d-d75fab32ef0e --verbose
Expected result can be seen below. Remember that it takes time to read transaction log and create a withdrawal. Hence a withdrawal might be not immedietly seen:
[{"amount":10.00}]
Architecture overview:
Since it is problematic (or immposible) to test transaction log tailing, there is no E2E test that verifies commands and queries. But we can test if a message arrival in Kafka's topic results in a proper withdrawal created. The code is here:
@Test
public void shouldSynchronizeQuerySideAfterLogTailing() {
// given
String cardUUid = thereIsCreditCardWithLimit(new BigDecimal(100));
// when
creditCardUpdateReadFromDbTransactionLog(TEN, cardUUid);
// then
thereIsOneWithdrawalOf(TEN, cardUUid);
}
Synchronization done by sending a domain event after succesfully handling a command.
Code can be found under events module. It has 2 further modules, architecture is fully distributed. There is a source (deals with commands) and sink (deals with queries).
Additional components:
Running the app, remember to be in root of the project:
docker-compose up
A sample Withdraw command:
curl localhost:8080/withdrawals -X POST --header 'Content-Type: application/json' -d '{"card":"3a3e99f0-5ad9-47fa-961d-d75fab32ef0e", "amount": 10.00}' --verbose
Verifed by a query (notifce a different port: 8888!):
curl http://localhost:8888/withdrawals?cardId=3a3e99f0-5ad9-47fa-961d-d75fab32ef0e --verbose
Expected result can be seen below. Remember that it takes time to publish and read domain events from Kafka. Hence a withdrawal might be not immedietly seen:
[{"amount":10.00}]
Architecture overview:
Since it is not recommended to test 2 microservices in one test, there is no E2E test that verifies commands and queries. But we can test if a message arrival in Kafka's topic results in a proper withdrawal created. The code is here:
@Test
public void shouldSeeWithdrawalAfterGettingAnEvent() {
//when
anEventAboutWithdrawalCame(TEN, cardID);
//then
thereIsOneWithdrawalOf(TEN, cardID);
}
Also it is possible to test if a successful withdrawal is followed eventually by a proper domain event publication. The code is here.
@Test
public void shouldEventuallySendAnEventAboutCardWithdrawal() throws IOException {
// given
UUID cardUUid = thereIsCreditCardWithLimit(new BigDecimal(100));
// when
clientWantsToWithdraw(TEN, cardUUid);
// then
await().atMost(FIVE_SECONDS).until(() -> eventAboutWithdrawalWasSent(TEN, cardUUid));
}
Take a look here
Prooph Service Bus PHP 7.1+ lightweight message bus supporting CQRS and Micro Services Important This library will receive support until December 31, 2019 and will then be deprecated. Messaging API pr
CQRS is just a concept that we can seperate the application to two parts: the write part and the read part, that's all; Command: A command represents what user want to do, user can send a command to t
Business value of the events, the value of having a log, the fact that the Event Store is additive only. The biggest advantage of this architecture is that it allows you to design your model to meet s
我从接触ddd到学习cqrs有6年多了, 其中也遇到了不少疑问, 也向很多的前辈牛人请教得到了很多宝贵的意见和建议. 偶尔的机会看到国外有个站点专门罗列了ddd, cqrs和事件溯源的常见问题. 其中很多也是我一路过来都曾遇到过的. 这是原站地址http://www.cqrs.nu/Faq. 在ENODE群中不少新学习cqrs的朋友都会遇到一些类似的入门问题, 作为群管理员的我也想为群里朋友做
原文地址:CQRS, Task Based UIs, Event Sourcing agh! Many people have been getting confused over what CQRS is. They look at CQRS as being an architecture; it is not. CQRS is a very simple pattern that enabl
Android Things 是 Google 推出的平台,旨在帮助 Android 开发者打造物联网 (IoT) 设备。这个平台还支持强大的应用,例如视频与音频处理以及通过 TensorFlow 进行板载机器学习。 它继承Brillo的功能外,还增加了Android API和Google服务的支持,以及一些Android Things的支持库。 下面这张架构图展示了Android Things的
AliOS Things 是 AliOS 家族旗下的、面向IoT领域的、高可伸缩的物联网操作系统。 AliOS Things将致力于搭建云端一体化IoT基础设施,具备极致性能、极简开发、云端一体、丰富组件、安全防护等关键能力,并支持终端设备连接到阿里云Link,可广泛应用在智能家居、智慧城市、新出行等领域。 AliOS Things 特性 极简开发 基于Linux平台,提供MCU虚拟化环境,开发者
React Things Коллекция материалов для изучения ES2015, React, Redux, Webpack, Babel и всего остального. Думаешь, что подборка не полная? Присылай Pull Request с недостающими материалами! Содержание: E
GTG 是一款非常易用的任务管理工具。它的任务编辑器可以通过非常简单的语法自动识别元数据,例如标签,子任务等等。
如果Pred(Elem)为List中的所有元素Elem返回true,则返回true,否则返回false。 语法 (Syntax) all(Pred,lst) 参数 (Parameters) Pred - 将应用于字符串的谓词函数。 Lst - 值列表。 返回值 (Return Value) 如果Pred(Elem)为List中的所有元素Elem返回true,则返回true,否则返回false。
我编写了一个简单的本体来为我的项目做一些简单的测试,但是当我使用ProtégéGUI中提供的OntoGraph显示本体的图形时,我会自动得到奇怪的关联: 我得到的是:(代码链接在消息的末尾) 为什么?? 这里是OWL文件和图形的快照