Test project made for job interview
com.eternal_search.football_manager
- Root app package.controller
- Controllers (endpoints implementation).service
- Services (business-logic layer).model
- Data model classes.model.enumeration
- All enumerations used by DTO and JPA entities.model.entity
- JPA entities.model.dto
- DTO classes.model.mapper
- Entity-to-DTO and DTO-to-entity mappers.model.repository
- Database access layer.exception
- Custom exception classessrc/test
- Unit and integration tests
GET /team/list
- List all teams (sorting and pagination available)POST /team
- Create a new teamDELETE /team/{id}
- Delete a team (BONUS)
Please access http://localhost:8080/swagger-ui/index.html to have more info
(expected parameters etc.) or look into TeamController
class.
First you need Java 17 to run the app.
Second you need to create an empty Postgres database for the app.
Default database URL and credentials are defined in src/main/resources/application.properties
.
If you want to change them you can either change them in this file or by passing environment variables
(e.g. to override spring.datasource.url
you can use SPRING_DATASOURCE_URL
environment variable).
Database needed both for normal app operation and for running tests.
Project uses Gradle build system. Some useful commands:
./gradlew bootRun
- Run an app./gradlew bootJar
- Builds JAR of the app located inbuild/libs
. This JAR will contain all dependencies and can be used without any other project files../gradlew test
- Run all tests
On Windows system ./gradlew
should be replaced with gradlew.bat
. Also, you can install Gradle globally
and just use gradle
command. Finally you can open the project in IntelliJ IDEA and run it using
IDE.
- I've chosen PostgreSQL as a relational database system, because on one hand it is a mature scalable DB, on other hand I have the most experience with it. The second choice could be Oracle.
- I've chosen Flyway migrations. The second choice could be Liquibase. I prefer Flyway, because in my opinion it is more natural to write migrations in pure SQL rather than in XML/JSON/YAML. Also, anyway most of the projects use some database vendor-specific stuff to gain more performance, and it is easier with SQL migrations. On other hand database engine change is not very probable during project lifetime.
- I'm using Lombok to reduce boilerplate code and speed up the development.
- I'm using
BIGINT
as a column type for team budget. I've found that the richest football club in the world in 2023 is "Manchester City" and it has a budget of approximately 707 M€. It is just 4 times less thanINTEGER
maximum value, so I'd prefer to useBIGINT
to be safe. The second point is that our app is not bank or accounting software and I don't think that football manager needs cents to compare teams. In case if we need more precision we could useDECIMAL(13,2)
to handle up to 9.999 trillion € with a precision up to 1 cent. And we shouldn't useREAL
to store money because of variable precision. - I'm using enumerated value for player position column type, because it is
quite unlikely that football rules would change adding/removing positions.
I store enumerate values as
VARCHAR
and not as a number to simplify manual database queries and to have simple possibility to extend enumeration in future software updates (I also could use Postgres custom enumerated type e.g.CREATE TYPE player_position AS ENUM(...)
, but native DB enumerated type isn't supported by Hibernate out of the box without extra libraries and annotations). In case if we would expect frequent changes of football rules (and don't want for user to wait a software update), we could use an extra table for player positions instead of enumeration. - I created an index on
team_id
field of players table, because we need to be able to query players by teams and also because of cascading deletion of teams to players. - I've populated tables with 16 football teams and some players for Nice and
Toulouse football clubs for demonstrative purposes using
V2__initData.sql
migration. Due to limited time I haven't found what does acronym mean for football team, so I use the closest airport IATA code to home city of a club. - I use MapStruct for generate mappers Entity-to-DTO and DTO-to-Entity to speed up the development.
- I use
EntityGraph
to avoid "N + 1 problem" when fetching linked players while querying teams. Propertyspring.jpa.show-sql
is used to verify that we don't have N + 1 problem. Should be turned off in production, because of too much logging. - I use Swagger to document API and easily test endpoints from browser: http://localhost:8080/swagger-ui/index.html
- I use Hibernate Validation to check all request parameters for validity.
- I write JavaDocs for all functions with body, but I don't write JavaDocs for DTO and entity fields, because on one hand I have very limited domain knowledge according to the task and on other hand they are quite self-explanatory. Finally, I have quite limited time budget for the project.
- Because there is no business-logic, I've written unit tests of validation
for
PageRequestDTO
. - For integration tests I'm testing a use-case of creating a team with players and then deleting it.
If it was a real project with more time budget, there are few things which could be done:
- It is good to add teardown step for our insert-and-delete integration test which will cleanup database in case of test failure. But on other hand we could assume that tests are executed in temporary environment and database will be deleted anyway.
- Add full CRUD for teams and players: endpoints for update teams (now you can only create and delete them) and separate players inside the team
- Add an endpoint for transfer player from one team to another
- Add an endpoint for query teams without players (for better performance when we don't need team details)
- Add an endpoint for query one team with players by its id
- Add integration tests for all endpoints
- Use Java 14 record type in place of Lombok @Data
- Handle UNIQUE constraint errors while creating a team and translate it to 409 Conflict HTTP error code
- Add an authentication (authenticated method to be defined)
- Write a Dockerfile for Docker deployment of the app
- It is possible to create a Docker Compose configuration or HELM chart to simplify deployment of both app and DB
- If we need more performance we could use
spring-boot-starter-webflux
to create asynchronous endpoints