This is a Spring Boot server application developed with Spring Boot 2.2.6 and Java OpenJDK 14.0.1.
- Java OpenJDK 14.0.1
- Gradle 6.3
- Docker
- MySQL
- RabbitMQ
For local development you'll need to set some environment variables. Change their values according to your MySQL and RabbitMQ setup.
$ export SPRING_DATASOURCE_URL="jdbc:mysql://localhost/bootdb?createDatabaseIfNotExist=true&autoReconnect=true"
$ export SPRING_DATASOURCE_USERNAME=root
$ export SPRING_DATASOURCE_PASSWORD=root
$ export SPRING_DATASOURCE_PLATFORM=mysql
$ export SPRING_RABBITMQ_HOST=localhost
$ export SPRING_RABBITMQ_PORT=5672
$ export SPRING_RABBITMQ_USERNAME=root
$ export SPRING_RABBITMQ_PASSWORD=root
$ export REST_USER_INFO_URL=https://user-info.herokuapp.com
$ export EXCHANGE_VOTING_RESULT_NAME=VotingResultsExchange
$ export QUEUE_VOTING_RESULT_NAME=VotingResultsQueue
$ docker container run --name mysqldb -ti -p 3306:3306 -e MYSQL_ROOT_HOST=% -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=bootdb -d mysql
$ docker run --name rabbitmq -ti -p 5672:5672 -p 15672:15672 -e RABBITMQ_DEFAULT_USER=root -e RABBITMQ_DEFAULT_PASS=root --hostname my-rabbitmq -d rabbitmq:management-alpine
$ ./gradlew build
$ java -jar build/libs/voting-service-0.0.1-SNAPSHOT.jar
# For deploying
$ docker-compose up
# For shutting down
$ docker-compose down
Make requests to http://localhost:8080/
or http://127.0.0.1:8080/
.
If some JDBC connection failure logs are displayed, the app container will restart by itself and it'll try to reconnect again. It happens because the MySQL container will take a minute or two until it's ready. So no need to worry because eventually the app will connect to the MySQL container successfully.
Open you browser and navigate to http://localhost:8080/swagger-ui.html
or
http://127.0.0.1:8080/swagger-ui.html
.
It's feature-first based, which means the most external packages are related to the application features. They act like boxes to avoid mixing code accross business or domain-specific topics. These packages may provide a "provider class" (like a Facade) to use inside other packages via dependency injection. Each package may be divided into more specific ones like:
contract
: provides REST controller classes. This package may contain version packages inside it.dao
: provides access methods to persistent data. This package contains the respective type-object class also.dto
: contains type-object classes for returning as response in the controller classes.exception
: contains domain-specific runtime exception classes.integration
: contains type-object classes for receiving as request in the controller classes or as response from external services.mapper
: provides mapper class for mapping type-object classes among packages.model
: contains type-object classes for most general use accross packages.service
: provides business or domain-specific classes.
There is plenty of space to use concurreny (multithreading) to perform some independent searching, mapping and filtering, but it'd make the code a little harder to read and to debug which wouldn't be a good trade for such small web service application. Something that would also be nice to have is to having more complex SQL queries. That would decrease the application processing and memory usage, but it'd add more complexity to the application and it wouldn't take advantage of some code reusability.
Currently only closing sessions on the next minute the Scheduled class is running are monitored to send result messages to the queue. It may be a way to reduce memory usage by querying only the closing sessions on the next minute, but the previous closed sessions won't be captured. To prevent that it'd be required to create a results table to store for every session result after its closing time. That could possibly explode memory consumption. The only way to prevent that would be paginating the select queries.
- Tests (JUnit & Mockito)