Java
Simple reactive REST API with Spring Webflux and MongoDB using Domain Driven Design

Simple reactive REST API with Spring Webflux and MongoDB using Domain Driven Design

In the last time I’m taking course of reactive programming with Webflux. It’s great technology, that will be winning on popularity in the future. Why ? Because is much faster than classic (thread-per-request) approach.Today I will present you simple apllication which shows basics of Webflux, reactive MongoDB, Domain Driven Design. In the future blog posts I will deep deeper in individual topics. I’m begginer in the most of the them also we will learn together !

If you are proffesional developer and you find mistakes, or (oh no !) bugs in code – let me know in comment.

List of contents

  1. What is reactive programming ?
  2. What is Spring Webflux ?
  3. What is MongoDB ?
  4. What is domain driven design ?
  5. About project
  6. Domain layer
  7. Infrastructure layer
  8. Application layer
  9. Web layer

What is reactive programming?

It’s programming paradigm based on asynchronous data streams. What does it mean ? In our old, good Spring applications each request blocks one thread (thread by request approach) which is blocked till the response comes back. It’s not effective in many cases.

In reactive approach whole interaction is asynchronous. It is possible with help of event loop, which is continuously running non blocking thread that handles incoming requests. When client sends request it will be subscribed in event loop. Clients’s thread is unblocked again and when server sends response after finishing the task, it goes back through event loop which redirects it to client.

In the classic thread-per-request approach the thread is blocked till the whole set of data is processed.

What is Spring Webflux ?

Webflux is the Spring’s answer for need of reactive non-blocking frameworks. Instead of classic blocking Tomcat server which is used in Spring Boot by default, in Webflux non blocking Netty server helps us achieve all great capabilities of reactivity. The Spring Webflux is based on Project Reactor (https://projectreactor.io/), reactive library which implements Reactive Streams specification.

Webflux supports two programming models:

  • Annotation based reactive components, which is similar to classic SpringMVC style.
  • Functional routing and handling, which is based on router and handler functions (this approach is similar to routing in Node.js in design)

I prefer functional approach, it looks more modern for me, so i will use it in this project.

What is MongoDB ?

MongoDB is non relational database based on JSON documents (analogical to row in SQL Table). These Documents are the part of collection (table in SQL). The Mongo databases are very flexible and have great scalability – it’s working great with big amount of unstructured data.

I won’t go dive deep into Mongo internals in this post, because we don’t must. Spring Data offers great support for the main features and when you have some experience with Spring Data JPA it will be no problem for you.

What is domain driven design?

It’s approach in software development which is helpful by building large applications with complex business logic. The main idea is to design applications matching domain as most it is possible. DDD is set of ideas, patterns, techniques and best practices introduced in 2004 by Eric J. Evans in his famous book “Domain-Driven Design: Tackling Complexity in the Heart of Software”.

Everything in application (from database to domain objects) should be named with language specific for the business (in DDD called ubiquitous language), as less technical as it is possible. It means that the understanding of the code without knowing the domain is impossible.

In projects where DDD is used the developers are working in team together with domain experts (often without technical knowledge) which are active members of development process.

The core domain should be free from any frameworks or external dependencies and in theory is technology independent.

Modeling of the domain is the base by projecting of application in this approach. The quality of modelling will be crucial in the future, when the application grows and become more complex.

DDD introduces some patterns which help us in proper modelling of the domain. The most of them are not used in this project, because it is really simple.

The most important pattern is called bounded context. It splices our application into independent parts of domain entities that have boundary around common business cases.

Another patterns of DDD are at example:

  • Aggregate root
  • Domain entity
  • Value object

I don’t describe it here, because details of domain driven design approach are not the main target of this post. I will cover some interesting topics from domain driven design area in the next future. Be aware ! 🙂

About project

Today I will code very simple reactive timesheet with time entries of user which belongs to team. I don’t want cover complex business rules here – my target is to show how you can implement relations in MongoDB (unfortunately for correct results of cascading we must make it manually) and how design reactive Rest API with Spring Webflux.

In this post I won’t paste whole codebase, only some examples. Whole code you can find on GitHub – https://github.com/szymon-sawicki/reactive-timesheet-app

I use Java 17, Spring Boot, WebFlux, Reactive MongoDB and Lombok. That are the dependencies needed today:

At first i will describe application’s name in <build> section of POM.XML. It’s create friendly name of JAR package, it makes creating docker container little bit easier.

Our app will be started in docker container together with instance of MongDB. At first the Dockerfile need to be created:

docker-compose.yml

Yet it’s time for some configuration, let’s look into application.yml

Let make our hands dirty with some code. I’m starting with package structure, I use Domain Driven Design approach with three main layers:

  • application – depends on domain and infrastructure layers. We are implementing here most of our business logic.
  • domain –  doesn’t depend on any layer. Contains the heart of the app – domain objects with some utility classes, dto’s and repository interfaces. Any kind of framework is here not allowed.
  • infrastructure – depends on domain layer. Here takes place whole interaction with database (reactive MongoDB in our case)
  • web – depends on application layer. Contains handlers and routing for reactive REST API.

That is the package structure of app:

Domain layer

In next step i will create heart of our application – domain objects. In this case only three – User, Team and TimeEntry. All member fields of our domain objects have package-private visibility so we can achieve immutability and encapsulation, for the extern access to some fields I create utility class in the same package with Function taking object and returning needed field

To make modification of some fields possible I created methods returning objects with new values. Example:

Examples of the mappers, made with the help of builder design pattern

To determine operations which be needed in our business logic I create repository interfaces in each domain package. They all have some common methods, this methods I will extract to separate class – CrudRepository, it can be found in configs package and each specific repository interface extends it.

Example of the repository:

Infrastructure layer

After that i will go to infrastructure layer and dive into persistence part. All domain classes are represented by entities, which are decorated with Spring Data MongoDB annotations. Each of them have also own mapper that converts entity into domain object. Example:

The DAO is the lowest layer in persistence, it is responsible for direct communication with database. Each DAO interface extends Spring’s ReactiveMongoRepository, which is similar to classic JpaRepository and provide rich set ready to use methods, we have also possibility to create own implementations using natural language or custom queries Criteria, or JSON Queries. In my project I use some generated method using natural language.

On the top of DAOs I will introduce implementations of earlier created repositories, which are separating db of our business logic and will be used in services. The DAOs are injected here and implementations of repository methods are converting entities into our domain objects. Whole data is wrapped in Webflux data types and conversion takes place i Inside flatMap method, so we keeping all benefits of reactive programming. Remember, that the most conversion and asynchronous operations should be done in flatMap() method, because map() breaks the flow and works synchronous (blocking thread).

Application layer

Ok, our db layer is ready and waiting for the job. Yet is time for may favorite part with lot of meat – services, which are called in handlers in web layer. I don’t want cover all CRUD operations here – for our purposes I will create some basic functionalities like creating, deleting finding by name and id methods. We have some relationships between domain objects and I will show you have we can implement it with the MongoDB in functional manner using Webflux. In each service I used @Slf4j annotation from Lombok for automatic logger generation. Data access provides repositories, which are injected with constructor (also generated with Lombok).

In service layer data is also wrapped into reactive types, but here inside reactive streams you can find only Data Transfer Objects, which are used in communication with the web layer. I’m starting with UserService – it will containing 4 public methods – findById, findByUsername, addUser and deleteUser. Two first methods are simple – after call of repository we are mapping result into dto, in this case I use map() method, because I don’t make any further operations and the result will be showed to the user. For the exception handling I use switchIfEmpyMethod() which be called if the result of the earlier operations is empty (not found in db). I’m generating here exception of my custom type wrapped in Mono.

By creating of the user at first we must check, if the user with the given username already exists in the DB. If user with given username were found I log message on the error debugging level and return existing user with that username.If result previous operation is empty, we can insert out user into db – I call switchIfEmpty method where I save new user and the map the result to dto.

The next service will be much more interesting, because here comes first relationship. Our team have list of members i must have one user with lead role. In MongoDb for proper implementation of relationships we must solve it manually like in old good times before ORM in SQL 🙂 Good practice is to have object on one side of relationship and id on the other. Our team have list of user objects and user have team id. Both classes have inside utility method which are creating new object with updated list of members by team or teamId by user.

After two boring findBy… methods (equal to methods from user) I am creating addTeam() method, which also like by user at first checks if team with given name exists and then (if result is empty) calls private helper method “createTeamWithMembers()”. Here we have some fun, at first new team must be inserted into db, then all new members must be saved (with saveAll() method) into db with new generated id of the team. After that we are updating list with members of team and save team with actual members data into db. All that is made inside flatMap method.

The last method in this service id deleteTeam(), here also we must take care of proper cascading to achieve consistency of data. At first I get list of all team’s member and then with helper methods change teamId to null. After that all updated members are saved in and team is deleted.

The last service care about time entries, here I will implement some simple methods for getting time entries and one for creating these. We don’t have here many extraordinary things, the time entry collision check I will implement in the next future. It doesn’t works properly.

Web layer

Our application layer is ready, we can go to web layer, which is really interesting in functional approach of Webflux. The endpoints are defined in router functions, which can be nested. The paths and other properties are build in builder style with RequestPredicates class (in examples below I used static import, to achieve good readability). With RequestPredicates.nest() method we can define main path in first argument and with chain of routing functions this path’s all endpoints.

All handlers have common parts and I extracted it to generic utility class GlobalRoutingHandler, which takes action (Mono) and http status as arguments. This class generates proper response with HTTP status and implements simple error handling.

Each chained endpoint is new router function, in first argument we are creating endpoint properties (http method with path and variable, accepted data type), second argument is handler function which processes the request. We can implement it here with lambda function, but good practice in real life projects is to create separate classes with handlers. I looks much better, because we cann call handler with simple method reference.

Handler function accepts ServerRequest as argument and returns Mono with result of our action. In this method we resolve path variables, query parameters, or request body and call right service to do action. Result is wrapped into response and mono.

Ok, reactive timesheet app looks ready. But it works ? Let ask mr. Postman ! In GitHub repository you can find postman collection with some prepared requests. But remember – in some cases you must pass the proper id in header!

Response after creating new team with two new members
Response after creating new team with two new members
Response after deleting of the user
After deleting of the user will succesfully be delted from list of the members in team

As we see everything works fine, we can look for next challenges in fascinating world of reactive programming !

What next ?

Our adventure with reactive timesheet app is not finished. Every good application must be covered with tests ! In the next post I will show how you can write integration and unit tests in Webflux.

4 thoughts on “Simple reactive REST API with Spring Webflux and MongoDB using Domain Driven Design

Leave a Reply

Your email address will not be published. Required fields are marked *