Example project applying hexagonal architecture and CQRS in kotlin.
CQRS stands for Command Query Responsibility Segregation. It's a pattern that I first heard described by Greg Young. At its heart is the notion that you can use a different model to update information than the model you use to read information. For some situations, this separation can be valuable, but beware that for most systems CQRS adds risky complexity.
In CQRS, a Command represents the intention to perform an operation on our system that ends up modifying its state.
It is often said that you should not do CQRS if what you are going to set up is a blog, but like everything in life, it depends. If you are going to build or manage a blog with a lot of traffic, CQRS fits very well within your application, since in terms of performance it gives us the ability to perform asynchronous tasks for free (using an asynchronous implementation of the CommandBus). It is also very good for us to work with this pattern when we have large teams, since by having query intentions separated we will save ourselves a lot of merge hell's :)
Normally, if someone tells us Query, SQL comes to mind. In the context of CQRS this is not the case. In CQRS, a Query represents the intention to request data from our system without altering its status.
If we have a system with a high volume of traffic, we will be able to take advantage of the advantages it brings us. Not having side-effects is very good for us to be able to search them.
Domain
Concepts that are in our context (User, Product, Cart, etc), and business rules that are determined exclusively by us ( domain services),
Application
The application layer is where the use cases of our application live (register user, publish product, add product to cart, etc).
Infrastructure
Code that changes based on external decisions. In this layer will live the implementations of the interfaces that we will define a domain level. That is, we will rely on the SOLID DIP to be able to decouple from external dependencies.
./gradlew check
./gradlew run
curl --location --request GET 'http://0.0.0.0:8081/healthcheck'
POST
curl --location --request POST 'http://0.0.0.0:8081/v1/product' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "product name",
"price": 120
}'
GET
Get all products
curl --location --request GET 'http://0.0.0.0:8081/v1/product/all'
Get product by id
curl --location --request GET 'http://0.0.0.0:8081/v1/product/9e6fcea9-237e-4055-ab31-95f90aac2f80'
Update product
curl --location --request PUT 'http://0.0.0.0:8081/v1/product' \
--header 'Content-Type: application/json' \
--data-raw '{
"id": "9e6fcea9-237e-4055-ab31-95f90aac2f80",
"name": "new product name",
"price": 120
}'
- Ktor for server: Documentation
- Koin for dependency injection: Documentation
- Kotlin Serializations: Documentation