This repository is intended to be used as a bootstrap for implementing Clean Architecture compliant APIs with state-of-the-art Spring Boot and Kotlin. In order to achieve such an ambition, your feedback (insight, criticism and suggestions) is highly welcome!
In order to illustrate the Clean Architecture principles, this skeleton implements a minimalistic and fictional use case of a Course enrollment platform with the given business rules:
- A Student may enrol him/herself to a Course if
- The given Course is not already full (i.e. less than 5 Students are already enrolled to that Course)
- The Student is not already enrolled to the given Course
Example API exposes a POST /enrol-student-to-course
endpoint with 2 integer parameters:
studentId
courseId
- Dependencies
- Start-up
- Output
- Tests
- Directory Structure
- Design decisions
- Appendix : Import project into IntelliJ IDEA Community
Run the following command:
gradle clean bootRun
In the command above, gradle
might be replaced by ./gradlew
For the sake of the example, the application runs on a in-memory database which is populated on start-up with 6 Students (with IDs from 1 to 6) and 5 Courses (with IDs from 1 to 5).
You may thus for instance try the API using Postman as shown here below : ![](docs/postman.jpeg | width = 100px)
{
"message": "Student #3 successfully enrolled to class #4"
}
{
"message": "Cannot enrol student #3 to course #12 : course not found",
"code": "ERR-1"
}
{
"message": "Cannot enrol student #12 to course #4 : student not found",
"code": "ERR-2"
}
{
"message": "Cannot enrol student #6 to course #4 : course is full",
"code": "ERR-3"
}
{
"message": "Cannot enrol student #3 to course #4 : student is already enrolled",
"code": "ERR-4"
}
{
"error": "Bad Request",
"status": 400,
"path": "/enrol-student-to-course",
"timestamp": "2018-06-27 13:42:02.699",
"message": "Failed to convert 'hello' to required type 'long' for argument 'studentId'",
"code": "BAD-REQUEST-ERR-1"
}
{
"error": "Bad Request",
"status": 400,
"path": "/enrol-student-to-course",
"timestamp": "2018-06-27 13:41:37.262",
"message": "Required long parameter 'studentId' is not present",
"code": "BAD-REQUEST-ERR-2"
}
Run the unit/integration tests using:
gradle clean test
Generate the pitest
mutation testing report using:
gradle pitest
An HTML report will be issued under the ./build/reports/pitest
directory.
In the commands above, gradle
might be replaced by ./gradlew
|-src
|--main
| |--kotlin
| |--api
| |--domain # contains domain entities and repositories interfaces
| |--infrastructure # contains technical implementations (db repo, spring boot)
| | |--http
| | |--routing # contains HTTP endpoint mapping and error handling mecanisms
| |--usecases # contains orchestration mecanisms
|--test # contains unit- and integration tests
- Persistence mapping is done using boilerplate entities and conversions in order to keep domain free of external libraries/framework dependencies
- Non-nominal execution flows are handled using
Exception
throwing andException handlers
fromControllerAdvice
- Naming relative to the verb "to Enrol" are spelled UK-style :)
And if you found this repository useful, please give it a star :)
- Clone or download the sources into a directory
- Open IntelliJ IDEA Community
- In the wizard press "Open":
- In the next dialog window select the options as show here:
- Let IntelliJ some time to synch with Gradle
- At this point you should be all set. To make sure of that, go to
./test/kotlin/api/domain/CourseTest.kt
test class and run it from within IntelliJ using the green arrow in the left margin: