A backend service with a REST API.
Target: To mange Locker infrastructure and package slot allocation via REST API.
Language: Java17 , Framework: Spring Boot
Deployable artefact: JAR, Build application tool: Maven , Dependency-management: spring-boot-starter, library: Lombok to reduce boiler plate codes.
Test: Swagger-ui , Git-bash terminal
Database: postgresql, Mapper: MyBatis, Database migration tool: Flyway.To clarify the database table relations UML-ER diagram is added.
Deployment: For gitlab ci-cd automation an example gitlab-ci.yml file added
Project variables in .env file
Core: Java 17 · Spring Boot · PostgreSQL · MyBatis
Client: React + TypeScript
Website: Live link https://okihai-hokan.vercel.app
API URL: https://package-allocation.onrender.com
In this project 2 main domain are separated. For the Smart Locker allocation service in a Residential Building, first part, the locker and slot relation has real-word dependency.Another part is to handle package allocation.
Initially, to structure the project monolitic architechture can be easily implemented but for later with increased slot installation, cover more residential area and packages. These require to keep the project loosely coupled.Therefore the project code base is structured in modular monolitic with 3 sections: locker,package,shared. This ensure minimul complexity and dependency in future scaling.
To design the database, currently 1 shared schema is used.For a residential building or in stations the locker-slot relation is defined and fixed. Therefore 2 separate tables lockers,slots have been created with the relation using a ForeignKey(locker_id).With a physical dependency in residential area a separated mapping table is not used.But in later on to serve in large scale by using separate schema or database; a separated mapping table would solve relational dependency between lockers and slots. Package domain has separated packages table.Because packages entries will be more frequent and keeping old history in volume may change priority.To keep the packages allocation in a slot relational mapping tables locker_slot_package_mapping table is created. To be noted the slots and packages has separated attributes like occupied_at,released_at, state and status etc to keep them independent of other table.So that in future they can be deployed separately. Frequently used features are added as Generated colums like slots.volume to reduce complexity in query execution,calculation.In addition, tables are indexed with columns based on current requirements. To keep data intedrity in tables unique constains are added like (locker_id,grid cordinates) and more. In databse package can freely move by release and stored in different slots if required.
In this project MyBatis is used to map sql queries. This is choosen to keep the total control over databse queries and reduce the constrains of concurrent access, data overload and complex database query execution and response latency.
Following is the ER diagram:

Recommanded Docker-desktop to run the project.
For using IDE : Configure the project for Java 17. Version like Java 23,26 may conflict and give dependency error for binding.
For Database connection and setup use the .env folders information while for tools like Dbeaver.
- unzip the project
- use docker-destop (on linux OS)
- To run the application go to the path which have the Dockerfile and open the terminal:
if requres use
cd smart-lock-allocation-sys
- To run the project
docker-compose up --build
- To check if everything is running correctly?
curl http://localhost:8080/actuator/health
- only to stop the application:
docker-compose down
- To clear all the data and stop:
docker-compose down -v
-
Recomand to use the swagger ui http://localhost:8080/swagger-ui/index.html#
-
Git-bash can be used to reduce any authorization or access error in local device.
-
Following is the Git-bash compatable curl command
-
Locker related api
GET /v1/locker/register | 1.1 Register a Locker unit | Register 6 locker unit slots
curl -X 'POST' 'http://localhost:8080/v1/locker/register' -H 'accept: */*' -H 'Content-Type: application/json' -d '{ "locker_name": "lobby-A", "temperature_zone_type": "AMBIENT", "slot_layout_request_list": [ { "grid_row": 1, "grid_col": 2, "width": 1000, "height": 1000, "depth": 1000, "status": "AVAILABLE" } ] }'
curl -X 'POST' 'http://localhost:8080/v1/locker/register' -H 'accept: */*' -H 'Content-Type: application/json' -d '{ "locker_name": "lobby-A", "temperature_zone_type": "AMBIENT", "slot_layout_request_list": [ { "grid_row": 1, "grid_col": 1, "width": 900, "height": 1000, "depth": 1000, "status": "AVAILABLE" } ] }'
curl -X 'POST' 'http://localhost:8080/v1/locker/register' -H 'accept: */*' -H 'Content-Type: application/json' -d '{ "locker_name": "lobby-B", "temperature_zone_type": "FROZEN", "slot_layout_request_list": [ { "grid_row": 2, "grid_col": 2, "width": 1000, "height": 1200, "depth": 1000, "status": "AVAILABLE" } ] }'
curl -X 'POST' 'http://localhost:8080/v1/locker/register' -H 'accept: */*' -H 'Content-Type: application/json' -d '{ "locker_name": "lobby-C", "temperature_zone_type": "AMBIENT", "slot_layout_request_list": [ { "grid_row": 2, "grid_col": 3, "width": 1200, "height": 1100, "depth": 1200, "status": "AVAILABLE" } ] }'
curl -X 'POST' 'http://localhost:8080/v1/locker/register' -H 'accept: */*' -H 'Content-Type: application/json' -d '{ "locker_name": "lobby-C", "temperature_zone_type": "AMBIENT", "slot_layout_request_list": [ { "grid_row": 2, "grid_col": 4, "width": 1200, "height": 1100, "depth": 1200, "status": "AVAILABLE" } ] }'
curl -X 'POST' 'http://localhost:8080/v1/locker/register' -H 'accept: */*' -H 'Content-Type: application/json' -d '{ "locker_name": "lobby-D", "temperature_zone_type": "AMBIENT", "slot_layout_request_list": [ { "grid_row": 1, "grid_col": 4, "width": 1200, "height": 1100, "depth": 1200, "status": "AVAILABLE" } ] }'
- GET /v1/locker/locker-details | 1.2 Show the detail info of Locker units
curl -X 'GET' 'http://localhost:8080/v1/locker/locker-details' -H 'accept: */*'
- GET /v1/locker/check-slot-state | 1.3 Update the state of a slot from enable to disable vise-versa
curl -X GET 'http://localhost:8080/v1/locker/check-slot-state' -H 'Content-Type: application/json' -d '{"locker_name":"lobby-A","grid_row":1,"grid_col":1}'
- Package Related APIs
PUT /v1/package/store-package | 2.1 & 4.1 allocate a best-fit slot, generate 6 digit pickupCode for the package on temperature type, dimension. | allocate 4 packages
curl -X 'PUT' 'http://localhost:8080/v1/package/store-package' -H 'accept: */*' -H 'Content-Type: application/json' -d '{ "width": 1000, "height": 1000, "depth": 1000, "temperature_zone_type": "AMBIENT", "tracking_no": "121-456-789" }'
curl -X 'PUT' 'http://localhost:8080/v1/package/store-package' -H 'accept: */*' -H 'Content-Type: application/json' -d '{ "width": 900, "height": 1100, "depth": 1000, "temperature_zone_type": "AMBIENT", "tracking_no": "122-456-789" }'
curl -X 'PUT' 'http://localhost:8080/v1/package/store-package' -H 'accept: */*' -H 'Content-Type: application/json' -d '{ "width": 1000, "height": 1000, "depth": 1000, "temperature_zone_type": "FROZEN", "tracking_no": "123-456-789" }'
GET /v1/package/{trackingNo}/search-package | 3.2 find the package by tracking number
curl -X 'GET' 'http://localhost:8080/v1/package/123-456-789/search-package' -H 'accept: */*'
GET /v1/package/{trackingNo}/picked-up | 2.2 Update the state of a slot available on pickingUp package.
curl -X 'GET' 'http://localhost:8080/v1/package/123-456-789/picked-up' -H 'accept: */*'
GET /v1/locker/check-slot-state | 3.1 Get slot status; if occupied then return package info
curl -X 'GET' 'http://localhost:8080/v1/locker/check-slot-state' -H 'accept: */*' -H 'Content-Type: application/json' -d '{ "locker_name": "lobby-A", "grid_row": 1, "grid_col": 1 }'
GET /v1/package/{trackingNo}/search-package | 3.2 find the package by tracking number
curl -X 'GET' 'http://localhost:8080/v1/package/122-456-789/search-package' -H 'accept: */*'
GET /v1/manage/pickup-completed | 4.2 validate a package and release the slot by pickup code. For this API call take or save the response of lockerName and pickup code received on PUT /v1/package/store-package API then use that response as payload. Because pickup Code is unique for each package.
curl -X 'PUT' 'http://localhost:8080/v1/manage/pickup-completed' -H 'accept: */*' -H 'Content-Type: application/json' -d '{"locker_name":"lobby-D","pickup_code":781303} '
For other options: Please Use swagger UI's curl description and convert prefered terminal's compatable command.
PostgreSQL is exposed on host port 5433, not the default 5432. It avoids conflicting with any Postgres instance already installed locally. Here, the 5433 mapping reduce the conflict on postgres version; while connecting to an external tool.
For trouble-shooting related to docker and database: Remove all the volume,data and then run again
docker-compose down -v
docker-compose up -d postgres
For trouble-shooting related to in Local IDE and database:
To use IDE like intelIJ enable the DB connection using the jdbc url and change the variables from .env .
To connect the IDE directly to the DB change the Application’s Edit Configurations and In Program arguments enter:
--spring.profiles.active=local
docker-compose --env-file .env.dev up -d
docker-compose --env-file .env.test up -d
docker-compose --env-file .env.prod up -d
Ahammed Protiva