Backend Architecture
This page describes the structural and security architecture of the BonardaHR backend — how the code is organized, how requests are authenticated and authorized, how errors are handled, and how the application is configured and deployed.
Package Structure​
The backend follows a package-by-feature (domain-driven) layout. Each domain package is self-contained and owns its controllers, services, repositories, DTOs, and entities.
com.bonardahr.backend/
├── config/ # Spring Security, CORS, JWT filter, async executor
├── exception/ # GlobalExceptionHandler & custom exceptions
└── domain/ # Business Domain Modules
├── admin/ # RBAC management & custom role admin
├── common/ # Shared base entities & utilities
├── dashboard/ # Dashboard aggregation metrics
├── document/ # Document folders & SharePoint integration
├── employee/ # Profile management, dynamic fields, CSV import
├── esign/ # E-signature documents & signing requests
├── event/ # Company events & regional holidays
├── feedback/ # Employee surveys, performance & 1-on-1s
├── notification/ # In-app notifications & email dispatcher
├── onboarding/ # Onboarding templates, wizards & checklist tracking
├── organization/ # Departments, offices, teams & org chart tree
├── reports/ # HR metrics & Bradford Factor absenteeism engine
├── timeoff/ # Leave policies, balances, accruals & approvals
├── timesheet/ # Clock in/out & weekly timesheet submissions
└── workflow/ # Automated HR workflow engine & oversight
See Domain Modules for a detailed breakdown of each package.
Security & Authentication​
Dual Authentication Architecture​
BonardaHR supports two authentication modes, selected via the Spring profile:
1. Production Mode — Azure AD SSO (spring.profiles.active=prod)
- User clicks "Sign in with Microsoft" → redirected to
/oauth2/authorization/azure - Azure AD authenticates the user and returns an OAuth2 token
OAuth2LoginSuccessHandlervalidates the token with Microsoft- The handler matches the Azure profile's email to an employee record
- A signed JWT is issued and returned to the frontend
- All subsequent API requests carry the JWT in the
Authorization: Bearerheader JwtAuthenticationFiltervalidates the JWT on every request and populatesSecurityContext
2. Development Mode (spring.profiles.active=dev)
- Enabled via
DevAuthController - Displays a dropdown of pre-seeded employees on the login page
- Single-click authentication — no password or Azure AD required
DevDataSeederauto-populates employees, org structures, leave balances, and timesheets on startup
JWT Claims​
The JWT contains:
sub— internal employeeid(never the public UUID)permissions— list of permission strings granted to the userexp— expiration timestamp (configured inapplication.yml)
Dual-Identifier Pattern​
Every persistent entity has two identifiers:
| Identifier | Type | Usage |
|---|---|---|
id | BIGSERIAL | Internal: foreign keys, joins, JWT subject |
public_id | UUID | External: the only ID ever returned via the REST API |
The mapping between public and internal IDs happens exclusively in the service layer. Controllers and DTOs only work with public_id. See ADR-012.
Global Exception Handling​
GlobalExceptionHandler (a @RestControllerAdvice) catches all domain exceptions and converts them to consistent JSON responses, preventing internal details from leaking to clients.
| Exception | HTTP Status |
|---|---|
ResourceNotFoundException | 404 NOT_FOUND |
ForbiddenException | 403 FORBIDDEN |
ValidationException / BadRequestException | 400 BAD_REQUEST |
DataExistsException | 409 CONFLICT |
All error responses follow the same JSON envelope, making frontend error handling predictable.
Configuration & DevOps​
Central Configuration (application.yml)​
| Setting | Description |
|---|---|
| Database | Connection URL, credentials, HikariCP pool parameters |
| JWT | Secret key, token expiration |
| CORS | Allowed origins (frontend dev server + production domain) |
| Microsoft Graph | Azure credentials, SharePoint site/drive IDs |
| Scheduled Jobs | Cron expressions (e.g., weekly timesheet reminder: 0 0 9 * * MON) |
Environment Variable Overrides​
All secrets are injected via environment variables (.env file in dev, container env in prod):
| Variable | Purpose |
|---|---|
DB_HOST, DB_NAME, DB_USERNAME, DB_PASSWORD | Database connection |
JWT_SECRET | JWT signing key (min 32 chars) |
AZURE_AD_ENABLED, AZURE_AD_TENANT_ID, AZURE_AD_CLIENT_ID, AZURE_AD_CLIENT_SECRET | Azure AD SSO |
MS_TENANT_ID, MS_CLIENT_ID, MS_CLIENT_SECRET | Microsoft Graph (SharePoint + Calendar) |
SP_MOCK_ENABLED | true = mock SharePoint data; false = real SharePoint |
Docker Setup​
| File | Purpose |
|---|---|
docker-compose.dev.yml | Local PostgreSQL (port 5432) + pgAdmin |
Dockerfile | Multi-stage production build; packages the Spring Boot JAR with Eclipse Temurin Java 21 runtime |
CI/CD​
.gitlab-ci.yml defines build, unit test execution, and deployment pipeline stages.
Request Flow​
The following diagram shows how a client request travels through the backend from authentication to the database and optional Microsoft services:
Further Reading​
| Document | Description |
|---|---|
| Domain Modules | Per-module breakdown of functionality |
| Database Schema | Full table reference and ERD |
| ADRs | Architectural decisions behind this design |
| Microsoft Integration | Production Azure setup guide |