- Spring Boot – Main framework for building the backend REST API.
- Spring Security – Secures endpoints via JWT-based authentication and role-based authorization.
- Spring Data JPA – ORM layer for database access using repositories.
- PostgreSQL – Relational database with schema-level multitenancy.
- JWT (JSON Web Tokens) – Stateless token-based authentication.
- Flyway – SQL-based DB migration tool for setting up tenant schemas.
- Schema-per-tenant: Each tenant has its own schema, dynamically resolved at runtime.
publicschema holds shared entities (likeuser,tenant,tenant_permission).tenant_*schemas hold isolated tenant data (e.g.,user_tenant,role,contract, etc.).
- Model – Entities mapped to tables using JPA annotations.
- Repository – Interfaces extend Spring Data JPA, with custom queries for tenant-aware access.
- Service – Business logic layer, often transactional and reusable.
- Controller – REST endpoints exposed via
@RestController, returning DTOs. - Middleware – Custom filters for:
SchemaRoutingFilter– sets schema based on tenantJwtAuthenticationFilter– handles token validation and user authenticationAuthorizationFilter– applies role-permission checks for RBAC
To support fine-grained authorization within each tenant, the system implements role-permission logic based on four key tables:
role– Defines roles likeOWNER,MANAGER,WORKERfor that tenant.user_role_table– Associatesuser_tenantrecords with one or more roles.role_permission– Maps roles to one or more global permissions, and optionally targets specific roles (e.g. “MANAGER can edit WORKER”).tenant_permission(inpublicschema) – Defines all available permissions globally (e.g.,POST /api/v1/tenant/user-tenant).
These tables are used by the AuthorizationFilter middleware to determine whether a request should be allowed, based on:
- The authenticated user's roles (
user_role_table) - The permissions linked to those roles (
role_permission) - The HTTP verb and path (
tenant_permission) - Whether the permission is scoped to a target role (
target_role_id)
This setup supports:
- Global permission definitions (shared across tenants)
- Per-tenant role-to-permission mappings
- Optional cross-role restrictions for controller logic (e.g. worker-to-worker updates denied)
The backend is designed following RESTful principles, exposing resource-based endpoints using standard HTTP verbs (GET, POST, PUT, DELETE).
To reduce boilerplate and enforce consistency, the system includes a generic BaseController pattern for CRUD operations.
BaseController<T, ID>– A generic abstract controller providing default CRUD endpoints:GET /resource– Retrieve all entitiesGET /resource/{id}– Retrieve an entity by IDPOST /resource– Create a new entityPUT /resource/{id}– Update an existing entityDELETE /resource/{id}– Delete an entity
- Domain-specific controllers (e.g.,
JobListingController,ContractController) extendBaseControllerto inherit the CRUD endpoints without duplication. - For tenant-secured models, a separate
BaseUserSpecificControlleradds role-based filtering logic usingtarget_rolesextracted from the authorization layer.
/api/v1/tenant/job-listing→ usesBaseControllerfor standard CRUD/api/v1/tenant/role-based/job-listing/role-filtered→ usesBaseUserSpecificControllerto enforce role-scoped logic
This structure improves maintainability, reduces repetition, and keeps controller logic focused on extensions and overrides when needed.


