Enterprise-Grade Point of Sale & Inventory Management System
A production-ready fullstack application demonstrating modern software architecture, clean code practices, and advanced Spring Boot 4 / Angular 21 features.
π Live Demo: https://pharmacy.hivens.dev
Created by a Backend Developer who decided to master the full stack β this project showcases end-to-end ownership from database design to pixel-perfect UI.
β¨ Bleeding-Edge Stack β Built with Spring Boot 4.0.1 and Angular 21.2.5 (latest stable releases as of Q1 2026)
ποΈ Production Architecture β Contract-First API design, CQRS patterns, optimistic locking, RBAC security
π§ͺ Test Coverage β Comprehensive JUnit tests with Mockito for business-critical logic
π¦ Docker Ready β Full containerization with multi-stage builds and health checks
π CI/CD Pipeline β Automated deployment to VPS via GitHub Actions on every push
π i18n Support β Bilingual interface (English/Russian) with dynamic language switching
π¨ Professional UI β Built with PrimeNG, responsive design, and modern UX patterns
| Dashboard | POS Terminal |
|---|---|
![]() |
![]() |
| Inventory Management | Sales History |
|---|---|
![]() |
![]() |
- Real-time stock validation β Server-side checks prevent overselling
- Customer discount management β Automatic price calculations
- Receipt generation β Print-ready transaction summaries
- Cart persistence β LocalStorage backup for session recovery
- FEFO Algorithm (First Expired, First Out) β Smart batch rotation for pharmaceutical compliance
- Batch tracking β Full traceability with expiration date monitoring
- Low-stock alerts β Proactive notifications for reordering
- Supplier integration β Manage supply chain relationships
- Sales analytics β Revenue tracking with growth metrics
- Customer insights β Registration trends and loyalty tracking
- Inventory health β Stock status visualization
- Historical reporting β Paginated sales history with date filtering
- JWT Authentication β Stateless token-based security
- Role-Based Access Control β Admin vs Pharmacist permissions
- Password encryption β BCrypt hashing with Spring Security
- CORS configuration β Secure cross-origin request handling
- Zoneless Angular 21 β Signals-based reactivity for optimal performance
- PrimeNG UI Framework β Enterprise-grade components
- Auto-generated API Client β TypeScript SDK from OpenAPI spec
- Responsive Design β Mobile-first approach with PrimeFlex
β Spring Boot 4.0.1 β Virtual Threads, Structured Logging
β Java 21 (LTS) β Records, Pattern Matching, Sequenced Collections
β Spring Data JPA β Database abstraction with Hibernate
β Spring Security 7 β JWT + RBAC implementation
β Flyway 10+ β Versioned database migrations
β MapStruct 1.5.5 β Compile-time DTO mapping
β SpringDoc OpenAPI 2.7 β API documentation generation
β MariaDB 10.11 β Relational database with advanced features
β JUnit 5 + Mockito β Comprehensive unit testingβ Angular 21.2.5 β Zoneless change detection, Signals API
β PrimeNG 21.1.3 β UI component library
β RxJS 7.8 β Reactive programming
β OpenAPI Generator β Auto-generated TypeScript client
β ngx-translate β Internationalization (i18n)
β TypeScript 5.9 β Type safety with strict modeβ Docker Compose β Multi-container orchestration
β GitHub Actions β CI/CD β auto deploy on push to main
β Nginx + Let's Encrypt β Reverse proxy with HTTPS
β Gradle 8.14 β Build automation
β npm 11.7.0 β Frontend package management
β Git β Version control with conventional commitsInstead of manually syncing frontend/backend types, the API specification is the single source of truth.
Benefits:
- Zero type mismatches at runtime
- Breaking changes caught at compile time
- Auto-generated, always up-to-date client SDK
Implementation:
# Backend exposes /v3/api-docs
# Frontend generates client:
npm run generate-apiPharmaceutical compliance requires selling items closest to expiration first.
Query Strategy:
SELECT * FROM inventory
WHERE medicine_id = :id
AND stock_quantity > 0
AND expiration_date >= CURRENT_DATE
ORDER BY expiration_date ASCAtomic Transaction:
@Transactional
public void createSale(SaleCreateDto dto, String username) {
// 1. Fetch valid batches (FEFO)
List<Inventory> batches = inventoryRepo.findValidBatchesForSale(medicineId, LocalDate.now());
// 2. Validate total stock
int totalStock = batches.stream().mapToInt(Inventory::getStockQuantity).sum();
if (totalStock < requestedQty) throw new InsufficientStockException();
// 3. Deduct from batches (oldest first)
for (Inventory batch : batches) {
// Optimistic locking (@Version) prevents race conditions
}
}High-traffic POS scenarios require safe concurrent stock updates.
Implementation:
@Entity
public class Inventory {
@Version
private Long version; // Hibernate automatic versioning
}Behavior: If two cashiers sell the same item simultaneously, the second transaction will fail with OptimisticLockingFailureException, triggering a retry.
Avoid N+1 queries by fetching aggregated data in a single SQL query.
Before (N+1 Problem):
// 1 query for medicines + N queries for stock
for (Medicine m : medicines) {
int stock = inventoryRepo.sumByMedicine(m.getId());
}After (Single Query):
@Query("""
SELECT new MedicineResponseDto(
m.id, m.name, m.price,
COALESCE(SUM(i.stockQuantity), 0L)
)
FROM Medicine m
LEFT JOIN Inventory i ON i.medicine.id = m.id
GROUP BY m.id
""")
List<MedicineResponseDto> findAllSummarized();All schema changes are tracked in migration files.
Example:
backend/src/main/resources/db/migration/
βββ V1__init_schema.sql β Initial tables
βββ V2__add_version_column.sql β Add optimistic locking
βββ V3__create_indexes.sql β Performance optimization
Result: Identical database structure across Dev/Test/Prod environments.
Java 21 JDK β https://adoptium.net/
Node.js 22+ β https://nodejs.org/
Docker & Compose β https://www.docker.com/# Clone repository
git clone https://github.com/Kitty-Hivens/Pharmacy.git
cd Pharmacy
# Start all services (DB + Backend + Frontend)
docker-compose up -d
# Access application
π Frontend β http://localhost
π API Docs β http://localhost:8080/swagger-ui.html
ποΈ Database β localhost:3307 (root/root)Username: admin
Password: Set via ADMIN_INITIAL_PASSWORD environment variable
# Terminal 1: Database
docker-compose up -d db
# Terminal 2: Backend
cd backend
./gradlew bootRun
# Terminal 3: Frontend
cd frontend
npm install
ng serve
# Docker: http://localhost
# Local dev: http://localhost:4200After modifying backend DTOs/Controllers:
cd frontend
npm run generate-apipharmacy-management/
βββ backend/ # Spring Boot 4 Application
β βββ src/main/java/
β β βββ haru/pharmacy/
β β βββ config/ # Security, OpenAPI, CORS
β β βββ controller/ # REST endpoints
β β βββ dto/ # Request/Response objects
β β βββ exception/ # Custom exceptions
β β βββ mapper/ # MapStruct interfaces
β β βββ model/ # JPA entities
β β βββ repository/ # Spring Data repositories
β β βββ service/ # Business logic
β βββ src/main/resources/
β β βββ db/migration/ # Flyway SQL scripts
β β βββ messages.properties # i18n (EN)
β β βββ messages_ru.properties # i18n (RU)
β βββ src/test/java/ # JUnit + Mockito tests
β
βββ frontend/ # Angular 21 Application
β βββ src/app/
β β βββ api/ # Auto-generated OpenAPI client
β β βββ core/ # Guards, Interceptors
β β βββ layout/ # Main layout component
β β βββ pages/ # Feature modules
β β βββ dashboard/
β β βββ pos/ # Point of Sale
β β βββ medicines/
β β βββ inventory/
β β βββ sales-history/
β β βββ customers/
β β βββ suppliers/
β β βββ users/
β βββ public/i18n/ # Translation files
β
βββ .github/workflows/
β βββ deploy.yml # GitHub Actions CI/CD
βββ docker-compose.yml # Multi-container setup
π Swagger UI: https://pharmacy.hivens.dev/swagger-ui.html
POST /auth/login β Authenticate user
GET /api/medicines β List all medicines
POST /api/inventory/restock β Add stock (ADMIN only)
POST /api/sales β Process sale transaction
GET /api/sales?from=&to= β Sales history with filters
POST /api/customers β Register customer
GET /api/users β List employees (ADMIN only)// 1. Login
POST /auth/login
{
"username": "admin",
"password": "your_password"
}
// Response: { "token": "eyJhbGc...", "role": "ADMIN" }
// 2. Use token in subsequent requests
GET /api/medicines
Headers: { Authorization: "Bearer eyJhbGc..." }cd backend
./gradlew test
# Coverage Report
./gradlew test jacocoTestReport
open build/reports/jacoco/test/html/index.htmlTest Examples:
- β Medicine CRUD operations
- β FEFO algorithm correctness
- β Stock validation (insufficient stock scenarios)
- β Optimistic locking conflict handling
- β User authentication & authorization
cd frontend
npm run test:e2e# Backend JAR
cd backend
./gradlew bootJar
# Output: build/libs/Pharmacy-0.0.2-SNAPSHOT.jar
# Frontend (Static Assets)
cd frontend
npm run build
# Output: dist/frontend/browser/# Backend (.env or docker-compose)
DB_URL=jdbc:mariadb://db:3306/pharmacy_db
DB_USERNAME=root
DB_PASSWORD=secure_password
JWT_SECRET=YourVeryLongSecretKey...
APP_CORS_ALLOWED_ORIGINS=https://yourdomain.com
ADMIN_INITIAL_PASSWORD=ChangeMe123!
# Frontend (environment.prod.ts)
apiUrl=https://api.yourdomain.comEvery push to the Central-Workflow branch triggers automatic deployment:
# .github/workflows/deploy.yml
on:
push:
branches: [Central-Workflow]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: appleboy/ssh-action@v1
with:
script: |
cd /root/Pharmacy
git pull
docker compose up --build -d# docker-compose.yml
services:
backend:
image: pharmacy-backend:latest
environment:
- SPRING_PROFILES_ACTIVE=prod
frontend:
image: pharmacy-frontend:latest
nginx:
image: nginx:alpine
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf- Fork the repository
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit changes (
git commit -m 'Add AmazingFeature') - Push to branch (
git push origin feature/AmazingFeature) - Open a Pull Request
Vitalii (Haru)
π GitHub: @Kitty-Hivens
π§ Email: vitalii.vakar@proton.me
πΌ LinkedIn: Vitalii Vakar
This project is licensed under the MIT License - see the LICENSE file for details.
- Spring Team β For the incredible framework and documentation
- Angular Team β For pushing the boundaries of frontend development
- PrimeNG β For the professional UI component library
- OpenAPI Initiative β For standardizing API specifications



