An inventory management application with a Spring Boot REST backend and a simple Thymeleaf frontend for quick browser-based usage.
This project is built for the Software Engineering Lab requirement and focuses on clean backend architecture plus a beginner-friendly Thymeleaf UI layer.
- Domain: Inventory management
- Backend: Spring Boot 3.2.5, Java 21, Spring Data JPA, Spring Security, Validation
- Database: PostgreSQL
- API Style: REST + DTO-based responses
- Frontend: Thymeleaf templates with a minimal multi-page UI
- Testing: JUnit 5, Mockito, Spring Boot Test, MockMvc
- Containerization: PostgreSQL via Docker Compose
- CI: GitHub Actions (
.github/workflows/ci.yml) runs Maven tests on pushes and PRs - CD: GitHub Actions (
.github/workflows/cd.yml) triggers Render deploy onmain
Status legend: Done
| Requirement | Status | Notes |
|---|---|---|
| Authentication and Authorization | Done | Role-based access is enforced with SecurityConfig + @PreAuthorize; login/logout UI flow is implemented. |
| REST API Design (>=3 controllers, CRUD for >=2 entities) | Done | 6 controllers implemented. Full CRUD exists for Product and Category. |
| PostgreSQL + >=4 tables + relationships | Done | 6 entities with 1:N, N:1, 1:1, and N:N relationships. |
| Testing (>=15 unit + >=3 integration) | Done | Requirement is satisfied; latest local Surefire report shows 53 total tests (0 failures). |
Dockerization (Dockerfile + compose app+db) |
Done | Dockerfile and compose.yaml include both app and PostgreSQL services. |
GitHub workflow strategy (main/develop/feature, protected main, PR review) |
Done | Branching strategy and PR workflow are in use for development and release flow. |
| CI/CD (build + test + deploy from main) | Done | CI (ci.yml) runs tests; CD (cd.yml) triggers Render deploy from main. |
| Deployment on Render + public URL | Done | Live app is available on Render (see deployment URL below). |
| Documentation (README with architecture, ERD, API, run steps, CI/CD) | Done | This README documents the implemented architecture, APIs, setup, testing, and deployment. |
Layered architecture is used:
- Controller layer: REST endpoints and HTTP status handling
- Service layer: business rules and validations
- Repository layer: Spring Data JPA data access
- Entity/DTO layer: persistence models + API-safe response models
- Exception layer: centralized global exception handling
src/main/java/com/example/inventorymanagement
|- config/
| |- SecurityConfig.java
|- controller/
| |- CategoryController.java
| |- HomeController.java
| |- PageController.java
| |- ProductController.java
| |- ProductDetailController.java
| |- StockLogController.java
| |- SupplierController.java
| |- UserController.java
|- dto/
| |- CategoryDTO.java
| |- ProductDTO.java
| |- ProductDetailDTO.java
| |- StockLogDTO.java
| |- SupplierDTO.java
| `- UserDTO.java
|- entity/
| |- Category.java
| |- Product.java
| |- ProductDetail.java
| |- StockLog.java
| |- Supplier.java
| `- Users.java
|- exception/
| |- ErrorDetails.java
| |- GlobalExceptionHandler.java
| `- ResourceNotFoundException.java
|- repository/
| |- CategoryRepository.java
| |- ProductRepository.java
| |- ProductDetailRepository.java
| |- StockLogRepository.java
| |- SupplierRepository.java
| `- UserRepository.java
`- service/
|- CategoryService.java
|- ProductService.java
|- ProductDetailService.java
|- StockLogService.java
|- SupplierService.java
`- UserService.java
erDiagram
USERS ||--o{ STOCK_LOG : creates
PRODUCT ||--o{ STOCK_LOG : tracked_in
CATEGORY ||--o{ PRODUCT : contains
PRODUCT ||--|| PRODUCT_DETAIL : has
PRODUCT }o--o{ SUPPLIER : supplied_by
Entities implemented:
UsersCategoryProductProductDetailSupplierStockLog
SecurityConfig uses URL-based authorization, method security (@PreAuthorize), HTTP Basic (API tools), and form login/logout (browser UI).
- Public:
GET /GET /loginGET /error- Static assets (
/css/**,/js/**,/images/**)
- API access:
GET /api/products/**,GET /api/categories/**,GET /api/product-details/**:ADMIN/SELLER/BUYERGET /api/suppliers/**:ADMIN/SELLERGET /api/logs/**:ADMIN- Other
/api/**: authenticated, then method-level rules apply
- UI access:
/ui/**: authenticated- Per-page role checks are enforced with
@PreAuthorize
Password encryption is handled using BCryptPasswordEncoder in UserService.registerUser.
The project includes a basic server-rendered UI using Thymeleaf and @Controller endpoints.
PageControllerserves UI routes under/ui- It fetches data through existing services, so REST APIs are not modified
GET /login- login page forADMIN/SELLER/BUYERGET /ui/dashboard- summary counts (products/categories/suppliers)GET /ui/products- product listGET /ui/categories- category list (buyers can access by URL; navbar intentionally hides menu item for buyer)GET /ui/suppliers- supplier listGET /ui/register- user registration form (ADMIN only)POST /ui/register- submit registration form (ADMIN only)POST /logout- logout current user and redirect to login page
src/main/resources/templates/dashboard.htmlsrc/main/resources/templates/products.htmlsrc/main/resources/templates/categories.htmlsrc/main/resources/templates/suppliers.htmlsrc/main/resources/templates/register.htmlsrc/main/resources/templates/fragments/navbar.html(shared navbar fragment)
POST /api/users/register- register user (ADMIN only)GET /api/users/{username}- get user by username
GET /api/categories- list categoriesPOST /api/categories- create categoryGET /api/categories/{id}- get category by idPUT /api/categories/{id}- update categoryDELETE /api/categories/{id}- delete category
GET /api/products- list productsPOST /api/products?categoryId={id}- create productPUT /api/products/{id}/stock?newQuantity={n}&username={user}- update stock and write stock logDELETE /api/products/{id}- delete product
GET /api/product-details/{id}- get details by idPOST /api/product-details- create details
GET /api/suppliers- list suppliersPOST /api/suppliers- create supplier
GET /api/logs- list stock transaction logs
GlobalExceptionHandler maps exceptions into consistent JSON error payloads:
ResourceNotFoundException-> 404IllegalArgumentException-> 400- Generic
Exception-> 500
Test stack:
- JUnit 5
- Mockito
@WebMvcTest+ MockMvc for controller integration-style tests- Service-layer unit tests with mocked repositories/dependencies
Current suite (latest local Surefire report):
- Unit tests (service layer): 28
- Controller tests (MockMvc/Web layer): 22
- Integration/other tests: 3
- Total: 53 tests
Run tests locally:
./mvnw clean testFor Windows PowerShell:
.\mvnw.cmd clean test- Java 21
- Maven (or use Maven Wrapper)
- Docker + Docker Compose
- PostgreSQL (or containerized Postgres from compose)
Create/update .env:
DB_USER=admin
DB_PASSWORD=adminpassword123
ADMIN_USERNAME=admin
ADMIN_PASSWORD=change-this-in-productionADMIN_PASSWORD is used for first-time admin bootstrapping. In production, if you rely on auto-creation of the initial admin user, you should set a strong ADMIN_PASSWORD via environment variables (for example, in Render).
On first startup, if ADMIN_USERNAME/ADMIN_PASSWORD are set and no ADMIN exists yet, the app auto-creates an ADMIN user with those credentials. If they are not set, the app still starts but you must create an admin user manually.
docker compose up -d./mvnw spring-boot:runPowerShell:
.\mvnw.cmd spring-boot:runThe app reads DB settings from src/main/resources/application.yaml.
Open the simple UI in your browser after starting the app:
http://localhost:8081/
GitHub Actions workflow: .github/workflows/ci.yml
What it does:
- Triggers on push and pull request
- Starts PostgreSQL service container
- Sets
DB_USERandDB_PASSWORDenv vars for tests - Sets up JDK 21
- Runs
./mvnw -B clean test
GitHub Actions workflow: .github/workflows/cd.yml
What it does:
- Triggers on push to
main - Reads deploy hook from GitHub Secret
RENDER_DEPLOY_HOOK_URL - Sends a
POSTrequest to trigger Render deploy
If RENDER_DEPLOY_HOOK_URL is not configured, the workflow fails with a clear message.
Implemented:
Dockerfilefor Spring Boot app (multi-stage build)compose.yamlwith bothappandpostgres- Environment-variable based DB credentials
- CI workflow (
.github/workflows/ci.yml) and CD workflow (.github/workflows/cd.yml)
Run full stack locally with:
docker compose up --buildLive:
- Public URL: https://inventory-management-tbvp.onrender.com
- Long-lived branches:
main,develop - Short-lived branches:
feature/* - Open PR from feature -> develop, then develop -> main
- Protect
mainand require at least one approval