docs: add README
This commit is contained in:
133
README.md
Normal file
133
README.md
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# 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](https://hyperskill.org/courses/12).
|
||||||
|
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`/`await` in 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`UserPrincipal` from a request from a facade instead of `@AuthenticationPrincipal`
|
||||||
|
- obtaining current time from a facade instead of using `Instant.now()`
|
||||||
|
|
||||||
|
**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
|
||||||
|
- `@Query` can 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 `@Delegate` annotation
|
||||||
|
- 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
|
||||||
|
1. **Clone the repository**
|
||||||
|
```bash
|
||||||
|
git clone <repository-url>
|
||||||
|
cd recipe-api
|
||||||
|
```
|
||||||
|
2. **Start the database**
|
||||||
|
```bash
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
3. **Build and Run**
|
||||||
|
```bash
|
||||||
|
./gradlew bootRun
|
||||||
|
```
|
||||||
|
The application will be available at [`http://localhost:8080`](http://localhost:8080)
|
||||||
|
|
||||||
|
## API Documentation
|
||||||
|
- [Scalar registry](https://registry.scalar.com/@tobias-haenel/apis/recipe-api/)
|
||||||
|
- [OpenAPI YAML](openapi/recipe-api.yaml).
|
||||||
|
- [OpenAPI YAML (bundled)](openapi/recipe-api-bundled.yaml).
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
1. **Run the test suite**
|
||||||
|
```bash
|
||||||
|
./gradlew test
|
||||||
|
```
|
||||||
|
2. **Check test results**
|
||||||
|
Open [`build/reports/tests/test/index.html`](build/reports/tests/test/index.html) in your browser
|
||||||
|
|
||||||
|
## License
|
||||||
|
This project is licensed under the terms of the MIT license. See the [LICENSE](LICENSE) file for
|
||||||
|
details.
|
||||||
Reference in New Issue
Block a user