994066b856d70b3f5e374e07e25d614992d300d4
Recipe API
A reactive REST API for managing recipes. I started this project after finishing my course on Java Backend Development with Spring Boot at Hyperskill. It's main purpose is to serve as a more practical place for learning and applying furthers aspects of backend web development with Spring Boot and Java.
Learning
Goals
- Understand and implement asynchronous programming principles with Project Reactor and Spring
- Acquire more knowledge about common testing strategies and apply them with Spring, JUnit & AssertJ
- Practice database design and SQL queries with PostgresSQL
- Gain some experience with Spring Security
- Identify the strengths, weaknesses, idioms, and common pitfalls of programming in Java
- Try out project Lombok
Thoughts and Observations
Reactive web development with Spring WebFlux, R2DBC and Project Reactor
- main expected advantage is performance, primarily due to non-blocking I/O
- out of the box reactive operators seem strong (e.g. buffering/timeouts)
- i didn't feel the urge to use them yet, as this project doesn't have real-world workloads.
- seems like a good fit for event/message based application parts
- implementing a servlet web application instead would probably make initial development faster
- currently better integration within the ecosystem e.g. with Spring Data JPA
- might not be as scalable in the long run
- migrating a complex monolithic servlet application to reactive stack seems like a hard task
- microservice approach could be helpful in practice
- event-based and performance critical sections in reactive web app
- remaining code in servlet web app
- allows for future replacements with implementations in other programming languages/frameworks (when performance/cost concerns arise)
- current project idea doesn't have use-cases where event-based programming was necessary
- code for complex reactive chains can quickly become convoluted
- reactive streams integrate asynchronous code through functional composition
- no syntax-based conveniences in Java (such as
async/awaitin other languages)
- documentation for the reactive stack still feels less comprehensive in some areas compared to servlet-based applications.
Testing practices
- no declarative transaction support for reactive tests
- tests should also be reactive to support all test cases and transaction rollback
- helper classes can be used to accumulate more than two variables for assertions
- makes test implementation more straightforward
- using tuples with more than two variables gets unwieldy quickly due to code repetition
- all objects of the reactive result chain need to be valid publishers (e.g.
Mono/Flux)- mocks require specification of when cases for all participating methods
- adding facades for certain parts of the application allows more code to stay testable
- extracting
UserPrincipalfrom a request from a facade instead of@AuthenticationPrincipal - obtaining current time from a facade instead of using
Instant.now()
- extracting
Database layer with Postgres
- choosing Postgres offers a wide range of built-in functions and extensions.
- keeping data generation within the application code simplifies integration with derived queries
- Spring R2DBC has some limits
@Querycan only be used with builtin positional arguments ($1,$2)- no easy support of composite keys in
ReactiveCrudRepository- m-to-n relationship entities additionally need their own keys
Spring Security
- implementing custom authorization has a steep learning curve
- no architecture explanation for reactive applications within the Spring Security reference documentation
- gaining manual understanding through API documentation is more time-consuming
- there is room for improvement in understanding all potential consequences of different authentication approaches (JWT, OAuth, OpenID)
Programming in Java
- prefer composition over inheritance
- doesn't require type "linearization"
- use interfaces to combine different functionalities
- remains more understandable when complexity rises
- separation of concerns whenever possible
- interfaces will be simpler
- potentially independent components stay hidden otherwise
- enables modularity and code reuse
- reduces complexity and cognitive burden
- generics in Java are type-erased
- classes unfortunately can't implement the same interface with different generic types
- workaround: generic base interface, manual extension of this interface with concrete types
- fluent APIs provide nicer developer experience in most cases
- while they can take time and careful thought to implement
- builder pattern helps when class creation gets more complex
Project Lombok
- not writing default getters and setters manually saves time and makes meaningful code more visible
- experimental
@Delegateannotation- can easily reduce the amount of required delegation code
- has limits due to generics, different visibilities and missing recursive support
Architecture
- Reactive Streams: Built with Spring WebFlux for non-blocking, event-driven IO
- RESTful API: Follows REST principles for simple, scalable and flexible service development
- Security: Spring Security integration for authentication and authorization
- Database: Uses PostgreSQL with R2DBC for reactive database access
Prerequisites
- Java 21 or later
- Gradle 8.0 or later
- Docker (uses PostgreSQL 13 or later)
Getting Started
Local Development
- Clone the repository
git clone <repository-url> cd recipe-api - Start the database
docker-compose up -d - Build and Run
The application will be available at
./gradlew bootRunhttp://localhost:8080
API Documentation
Testing
- Run the test suite
./gradlew test - Check test results
Open
build/reports/tests/test/index.htmlin your browser
License
This project is licensed under the terms of the MIT license. See the LICENSE file for details.
Description
Languages
Java
99.8%
HTML
0.2%