System Overview
OptimalHR is a multi-tenant Human Capital Management (HCM) SaaS platform built on AWS serverless architecture. It serves multiple customer organizations through a shared pool model with IAM-enforced data isolation at the DynamoDB level.
Request Flow
The lifecycle of an API request from a client application through authentication, authorization and finally the backend service.
Multi-Tenancy Model
Pool model with shared DynamoDB tables. Data isolation enforced at the IAM policy level using TenantId as the partition key.
★ System Admin Tenant
Platform owner. Cross-tenant visibility and operations.
- Cognito User Pool (system)
- Cognito Identity Pool (system)
- SystemAdminRole + SystemAdminPolicy
- SystemSuperuserRole
- SystemSupportRole + SystemSupportPolicy
■ Customer Tenant
Individual customer organization. Isolated to own data.
- Cognito User Pool (own)
- Cognito Identity Pool (own)
- TenantAdminRole + TenantAdminPolicy
- TenantUserRole + TenantUserPolicy
Data Isolation — Pool Model
All tenants share the same DynamoDB tables. Row-level isolation is enforced by IAM policies that restrict access to items matching the authenticated tenant's TenantId.
| TenantId (PK) | EntityItem (SK) | Example |
|---|---|---|
TENANT#acme | EMP#001 | Employee record |
TENANT#acme | LEAVE#2024-001 | Leave request |
TENANT#globex | EMP#042 | Employee record (different tenant) |
TENANT#globex | PAY#2024-03 | Payroll run |
dynamodb:LeadingKeys condition
restricting operations to rows where PK matches their TenantId.
System admin roles omit this condition for cross-tenant access.
IAM Model
AWS IAM roles and policies provisioned per tenant type.
| Tenant Type | IAM Role | IAM Policy | Scope |
|---|---|---|---|
| System Admin | SystemAdminRole | SystemAdminPolicy | All tenants |
| SystemSuperuserRole | (inherits admin) | All tenants | |
| SystemSupportRole | SystemSupportPolicy | All tenants | |
| Customer Tenant | TenantAdminRole | TenantAdminPolicy | Own tenant only |
| TenantUserRole | TenantUserPolicy | Own tenant only |
Authentication
Cognito-based authentication with per-tenant user pools. A single user credential works across Dev, Staging and Prod environments.
tenantId, tenantRole, environment, and other tenant-specific metadata.Bearer token in the Authorization header for every API call.JWT Token Structure
| Claim Type | Claim | Description |
|---|---|---|
| Standard | sub | Cognito user UUID |
| Standard | email | User's email |
| Standard | iss | Cognito user pool issuer URL |
| Standard | exp | Token expiration |
| Custom | custom:tenantId | Tenant identifier |
| Custom | custom:tenantRole | Role within the tenant (admin, user, etc.) |
| Custom | custom:environment | Target environment (Dev, Staging, Prod) |
Authorization — Amazon Verified Permissions
Fine-grained authorization using AVP. Each tenant has a dedicated policy store with schema, policy templates, and policies integrated with their Cognito User Pool and the API Gateway.
DENY → 403 Forbidden
Per-Tenant Policy Store Components
| Component | Description |
|---|---|
| Schema | Defines entity types, actions, and resource types. Integrated with the tenant's Cognito User Pool and API Gateway API. |
| Policy Templates | Reusable authorization rule templates parameterized by principal and resource. |
| Policies | Concrete policies instantiated from templates, granting or denying specific actions to specific principals on specific resources. |
Backend Microservices
OpenAPI-based Express.js applications deployed as AWS Lambda functions. Each service owns its domain and exposes RESTful endpoints via API Gateway.
Storage
Dual storage strategy: DynamoDB for structured tenant data with IAM-enforced isolation, S3 for object/document storage.
🗃 Amazon DynamoDB
Primary data store. Shared tables with tenant-scoped access.
- Pool model — all tenants share tables
- Composite key:
TenantId(PK) +EntityItem(SK) - IAM policy-level data isolation
- Environment-suffixed tables (Dev/Prod)
📦 Amazon S3
Object storage for documents and files.
- Employee documents & attachments
- Payslips & reports (generated)
- Profile photos
- Tenant-prefixed key structure
Key DynamoDB Tables
| Table | Environment Variants | Used By |
|---|---|---|
Employment | Employment[Dev|Prod] | Employment service, JobPicker |
TenantJob | TenantJob[Dev|Prod] | System Jobs (general scheduled jobs) |
WorkSchedule | WorkSchedule[Dev|Prod] | Work Schedule service, JobPicker |
PIM | PIM[Dev|Prod] | PIM service |
Leave | Leave[Dev|Prod] | Leave service |
TimeAttendance | TimeAttendance[Dev|Prod] | T&A service |
Payroll | Payroll[Dev|Prod] | Payroll service |
System Jobs
Scheduled background jobs for automated processing. EventBridge triggers a JobPicker Lambda every minute, which scans DynamoDB for pending jobs and dispatches them to specialized runner functions.
Job Execution Pipeline
Job Scope
| Runner Lambda | Scope | Example Operations |
|---|---|---|
AIML-SaaS-Job-Runner | General | Contract start/end processing |
AIML-SaaS-AttGen-System | System-wide | Generate attendance records across all tenants |
AIML-SaaS-AttGen-Tenant | Per-tenant | Generate attendance records for a specific tenant |
AIML-SaaS-Employee-Earn-AL | Per-tenant | Annual leave accrual calculations |
AIML-SaaS-CRUD-Item | Per-tenant | Update a single entity item at tenant level |
AIML-SaaS-Tenant-Notification-Dispatcher | Per-tenant | Send scheduled notifications (clock-in/out alerts) |
Reports & Analytics (BI)
Business intelligence and reporting capabilities.
Tenant Accounts & Subscription Management
Centralized tenant lifecycle and subscription management. Currently in development.
Architectural Suggestions
Recommended additions and improvements for the OptimalHR platform based on common SaaS patterns.
/v1/, /v2/) at the API Gateway level.
Enables non-breaking evolution of microservice APIs across tenant upgrade cycles.
Environment Matrix
Resource naming conventions and environment breakdown.
| Resource | Dev | Staging | Prod |
|---|---|---|---|
| EventBridge Schedule | AIML-SaaS-Scheduler-Dev |
AIML-SaaS-Scheduler-Staging |
AIML-SaaS-Scheduler-Prod |
| DynamoDB Tables | *Dev suffix |
*Staging suffix |
*Prod suffix |
| User Access | Single user credential works across all environments (env determined at pre-auth) | ||