Skip to main content

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)

  1. User clicks "Sign in with Microsoft" → redirected to /oauth2/authorization/azure
  2. Azure AD authenticates the user and returns an OAuth2 token
  3. OAuth2LoginSuccessHandler validates the token with Microsoft
  4. The handler matches the Azure profile's email to an employee record
  5. A signed JWT is issued and returned to the frontend
  6. All subsequent API requests carry the JWT in the Authorization: Bearer header
  7. JwtAuthenticationFilter validates the JWT on every request and populates SecurityContext

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
  • DevDataSeeder auto-populates employees, org structures, leave balances, and timesheets on startup

JWT Claims​

The JWT contains:

  • sub — internal employee id (never the public UUID)
  • permissions — list of permission strings granted to the user
  • exp — expiration timestamp (configured in application.yml)

Dual-Identifier Pattern​

Every persistent entity has two identifiers:

IdentifierTypeUsage
idBIGSERIALInternal: foreign keys, joins, JWT subject
public_idUUIDExternal: 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.

ExceptionHTTP Status
ResourceNotFoundException404 NOT_FOUND
ForbiddenException403 FORBIDDEN
ValidationException / BadRequestException400 BAD_REQUEST
DataExistsException409 CONFLICT

All error responses follow the same JSON envelope, making frontend error handling predictable.


Configuration & DevOps​

Central Configuration (application.yml)​

SettingDescription
DatabaseConnection URL, credentials, HikariCP pool parameters
JWTSecret key, token expiration
CORSAllowed origins (frontend dev server + production domain)
Microsoft GraphAzure credentials, SharePoint site/drive IDs
Scheduled JobsCron 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):

VariablePurpose
DB_HOST, DB_NAME, DB_USERNAME, DB_PASSWORDDatabase connection
JWT_SECRETJWT signing key (min 32 chars)
AZURE_AD_ENABLED, AZURE_AD_TENANT_ID, AZURE_AD_CLIENT_ID, AZURE_AD_CLIENT_SECRETAzure AD SSO
MS_TENANT_ID, MS_CLIENT_ID, MS_CLIENT_SECRETMicrosoft Graph (SharePoint + Calendar)
SP_MOCK_ENABLEDtrue = mock SharePoint data; false = real SharePoint

Docker Setup​

FilePurpose
docker-compose.dev.ymlLocal PostgreSQL (port 5432) + pgAdmin
DockerfileMulti-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​

DocumentDescription
Domain ModulesPer-module breakdown of functionality
Database SchemaFull table reference and ERD
ADRsArchitectural decisions behind this design
Microsoft IntegrationProduction Azure setup guide