AI Development GuideFoundations
Domain Models
Core data models for HRMS flexible org structure
Domain Models
This document defines the core data models for the HRMS system, with a focus on flexible organizational structures.
Core Principles
PRINCIPLE 1: Every manager is an employee
└── Single source of truth: Employee table
└── No separate "Manager" table
PRINCIPLE 2: Any employee can have multiple managers
├── primaryManagerId (0..1) — main reporting line
├── dottedLineManagerIds (0..N) — matrix relationships
└── additionalManagerIds (0..N) — secondary real managers
PRINCIPLE 3: Any employee can belong to multiple groups
├── departments (0..N)
├── teams (0..N)
└── roles (0..N)
PRINCIPLE 4: Support all org types
├── Classic hierarchy
├── Matrix organizations
├── Tribes/squads (Spotify model)
├── Cross-functional teams
└── Startup flat structures
PRINCIPLE 5: Minimal constraints
├── No limit on managers
├── No limit on teams/departments
├── No limit on roles
├── Self-management blocked (employeeId != managerId)
└── Circular management ALLOWED by default (startup flexibility)EmployeeCore
WHO the person is - their identity and employment information:
interface EmployeeCore {
id: string
tenantId: string
// Identity
firstName: string
lastName: string
email: string
phone?: string
pictureUrl?: string
// Job Information
jobTitle: string
jobFamily?: string
jobLevel?: string
// Employment
employmentType: 'FULL_TIME' | 'PART_TIME' | 'CONTRACTOR' | 'INTERN'
workMode: 'ONSITE' | 'REMOTE' | 'HYBRID'
status: 'ACTIVE' | 'INACTIVE' | 'ON_LEAVE' | 'TERMINATED'
hireDate: Date
terminationDate?: Date
// Metadata
customFields: Record<string, any>
createdAt: Date
updatedAt: Date
}EmployeeOrgRelations
HOW the employee fits in the organization:
interface EmployeeOrgRelations {
employeeId: string
// Manager Relations
primaryManagerId?: string // 0..1 direct manager
dottedLineManagerIds: string[] // 0..N matrix reporting
additionalManagerIds: string[] // 0..N secondary real managers
// Group Memberships
teamIds: string[] // 0..N teams
departmentIds: string[] // 0..N departments
roleIds: string[] // 0..N business roles
updatedAt: Date
}Team
interface Team {
id: string
tenantId: string
name: string
description?: string
type: 'PERMANENT' | 'PROJECT' | 'SQUAD' | 'GUILD' | 'TRIBE'
// Hierarchy
parentTeamId?: string // SaaS → Backend → API
// Leadership
leadId?: string // Lead is an employee
status: 'ACTIVE' | 'INACTIVE' | 'ARCHIVED'
customFields: Record<string, any>
createdAt: Date
updatedAt: Date
}Department
interface Department {
id: string
tenantId: string
name: string
code?: string
description?: string
// Hierarchy
parentDepartmentId?: string // Engineering → Backend
// Leadership
headId?: string // Head is an employee
status: 'ACTIVE' | 'INACTIVE'
customFields: Record<string, any>
createdAt: Date
updatedAt: Date
}Role (Business Role)
Business roles, NOT system roles (SystemRole is separate):
interface Role {
id: string
tenantId: string
name: string
category?: string // "Leadership", "Technical", "Sales"
description?: string
createdAt: Date
updatedAt: Date
}Valid Relationship Examples
Classic Hierarchy
CEO → CTO → Dev Lead → DevelopersMulti-Hierarchy (Developer has two real managers)
Developer: {
primaryManagerId: "dev-lead-id",
additionalManagerIds: ["cto-id"]
}Dotted Line (CTO also responsible for sales)
CTO: {
primaryManagerId: "ceo-id",
dottedLineManagerIds: ["cfo-id"]
}Multi-Team Membership
Developer: {
teamIds: ["saas-team", "web-development-team"]
}Multi-Role
CTO: {
roleIds: ["chief-technology-officer", "sales-manager"]
}Cross-Department
Developer: {
departmentIds: ["engineering", "product"]
}Full Startup Example
// CTO with multiple responsibilities
const cto = {
primaryManagerId: "ceo-id",
additionalManagerIds: [],
dottedLineManagerIds: ["cfo-id"], // sales responsibility
roleIds: ["cto", "sales-manager"],
teamIds: ["leadership", "sales"],
departmentIds: ["technology"]
}
// Developer in multiple teams
const developer = {
primaryManagerId: "dev-lead-id",
additionalManagerIds: ["cto-id"],
dottedLineManagerIds: [],
roleIds: ["senior-developer"],
teamIds: ["saas", "web"],
departmentIds: ["engineering", "product"]
}Operations API
Manager Operations
// Primary manager (0..1)
Org.assignPrimaryManager(employeeId: string, managerId: string): Promise<void>
Org.removePrimaryManager(employeeId: string): Promise<void>
// Dotted line managers (0..N)
Org.addDottedLineManager(employeeId: string, managerId: string): Promise<void>
Org.removeDottedLineManager(employeeId: string, managerId: string): Promise<void>
// Additional managers (0..N)
Org.addAdditionalManager(employeeId: string, managerId: string): Promise<void>
Org.removeAdditionalManager(employeeId: string, managerId: string): Promise<void>Team Operations
Org.addEmployeeToTeam(employeeId: string, teamId: string, role?: string): Promise<void>
Org.removeEmployeeFromTeam(employeeId: string, teamId: string): Promise<void>
Org.updateTeamRole(employeeId: string, teamId: string, role: string): Promise<void>Department Operations
Org.addEmployeeToDepartment(employeeId: string, departmentId: string, isPrimary?: boolean): Promise<void>
Org.removeEmployeeFromDepartment(employeeId: string, departmentId: string): Promise<void>
Org.setPrimaryDepartment(employeeId: string, departmentId: string): Promise<void>Role Operations
Org.assignRole(employeeId: string, roleId: string, isPrimary?: boolean): Promise<void>
Org.removeRole(employeeId: string, roleId: string): Promise<void>
Org.setPrimaryRole(employeeId: string, roleId: string): Promise<void>Org Chart Generator
Input Options
interface OrgChartOptions {
rootEmployeeId?: string // Start from specific employee
depth?: number // Max depth (default: 5)
includeMatrixRelations: boolean // Show additional managers
includeDottedLines: boolean // Show dotted line relations
includeTeams: boolean // Show team memberships
}Output Format
interface OrgGraphNode {
employeeId: string
firstName: string
lastName: string
jobTitle?: string
pictureUrl?: string
// Manager relations
primaryManagerId?: string
dottedLineManagerIds: string[]
additionalManagerIds: string[]
// Group memberships
teamIds: string[]
departmentIds: string[]
roleIds: string[]
// Hierarchy
directReports: OrgGraphNode[] // Primary reports
dottedLineReports?: OrgGraphNode[] // People with dotted line to this person
additionalReports?: OrgGraphNode[] // People with additional manager = this
}Graph Edges
type OrgEdgeType = 'primary' | 'dotted' | 'additional'
interface OrgGraphEdge {
from: string // Manager ID
to: string // Report ID
type: OrgEdgeType
}Rules
RULE 1: Manager must be an employee
└── Validated on every assignment
RULE 2: Self-management blocked
└── employeeId !== managerId
RULE 3: Circular management ALLOWED by default
└── CTO ↔ CFO managing each other is valid
└── Can be disabled per tenant: allowCircular = false
RULE 4: No limits
├── Unlimited managers per employee
├── Unlimited teams per employee
├── Unlimited departments per employee
└── Unlimited roles per employee
RULE 5: Deletion cascades
└── When employee deleted, remove from all relations