Organizational Structure Model
A design reference for the company / branch / department / designation structure and its role as the base data model other modules extend
1. Requirements
1.1 Functional requirements
- Represent a Legal Entity — an organizational unit with its own chart of accounts, base currency, and default settings — organized into a parent/subsidiary structure.
- Represent Department as a hierarchy scoped to a Legal Entity: a department can have sub-departments, and every non-root department declares which Legal Entity it belongs to.
- Provide Branch and Designation as simple, reusable, named labels any Legal Entity can assign to staff — a site name and a job title, respectively.
- Provide Employee as the base identity record every other module (attendance, payroll, projects, sales-team assignment, this plan’s own fleet/driver master) attaches to: Legal Entity, department, designation, branch, and a manager reference, plus contact, joining, and exit details.
- Maintain a reporting chain among employees, independent of the department hierarchy.
- Capture education history, prior external employment, and internal transfer history (department/branch/designation/date-range changes within the same organization) as append-only records.
- Allow employees to be collected into named Employee Group distribution lists other subsystems can address as a unit.
- Maintain a reusable Holiday List calendar master — dated holidays and weekly-off days, optionally auto-populated from a country/subdivision calendar — that a Legal Entity can nominate as default and an employee can override.
1.2 Non-functional requirements
- Structural integrity of each hierarchy independently: the Legal Entity tree and the Department tree each maintain their own nested-set bookkeeping so ancestor/descendant queries stay cheap as the org chart grows.
- Reuse over duplication: Branch, Designation, and Holiday List are declared once and referenced by many employees and Legal Entities rather than re-entered per record.
- Non-destructive history: internal/external work history and education rows are simple dated child records with no lifecycle of their own — written once, read back, never recomputed.
- Loose coupling to consuming subsystems: the calendar and grouping masters are designed to be read by unrelated modules without those modules needing to understand HR-specific structure.
1.3 Constraints
- This document covers the organizational skeleton and its master data only. Payroll, attendance capture, and leave entitlement are handled by logic outside this codebase; the employee record exposes an extension point an external module can override to supply its own calendar resolution, but no such module ships here — only the base fallback logic does.
- Branch and Designation carry no state machine and no company scoping at all: flat, organization-wide labels, not branches of a tree despite the name.
- Holiday List is not scoped to any Legal Entity — a free-standing, shareable calendar a Legal Entity or an employee points to, not a child of either.
2. High-Level Design
2.1 Component diagram
2.2 What is actually a hierarchy, and what is not
This is the load-bearing finding of this document: “organizational hierarchy” describes at most two of these structures, not all of them, and the two real hierarchies do not connect to each other.
- Legal Entity is a genuine tree: each record can name a parent, carries the standard nested-set bookkeeping, and can be flagged as a group node purely for structural grouping.
- Department is also a genuine tree, with its own independent nested-set bookkeeping — but its root node is a single record shared across every Legal Entity, not one root per entity. Every non-root department declares exactly one owning Legal Entity, but that field is a plain reference, not a tree-scoping rule: nothing stops a department from naming a parent that belongs to a different Legal Entity than its own.
- Branch and Designation are not hierarchies at all. Each is a bare, uniquely-named label with no company/entity field and no behavior beyond guaranteeing the name is unique — one created for one Legal Entity is visible and assignable to every other one.
- The employee reporting chain is a third, independent nested-set tree, keyed on each employee’s manager reference, with no structural relationship to the Department tree: an employee’s manager can sit in a different department, branch, or even a different Legal Entity, and nothing checks or prevents that.
So “company → branch → department → designation” reads as one hierarchy but is really two unrelated real trees (Legal Entity, Department) that only coincidentally sound like levels of the same thing, two flat global label sets (Branch, Designation), and a third tree (the reporting chain) living entirely inside the employee record, ignoring the other two.
3. Deep Dive
3.1 Data model
Legal Entity The top-level scoping unit, established in earlier documents in this set. Relevant here: it is a nested-set tree (self-referencing parent field plus left/right tree-position integers), can be marked as a non-leaf grouping node, and on first creation seeds a starter set of departments for itself if none exist yet.
Department A nested-set tree scoped by a required Legal Entity field, with its own parent self-reference and tree-position integers. Distinct from Legal Entity’s tree: it has one shared root common to every entity, and its record name is generated by appending the owning entity’s short code to the department name — which is what lets two entities each have a same-named department without collision. A department can itself be a group node and can be disabled.
Branch / Designation Each is a single required, unique name field and nothing else — no entity scope, no parent field, no status, empty controller body. They exist to be selected on an Employee (Designation is also referenced by the approval-authorization rules covered elsewhere in this plan) and to guarantee spelling is reused rather than re-typed.
Employee The base record. Beyond personal/contact fields it carries: a required Legal Entity link; optional department, designation, and branch links; a manager reference that doubles as the reporting-chain’s tree-parent field; a default/override holiday-list link; and the fields other modules key off (status, joining/exit dates, a linked login account). The employee record is itself a nested-set tree — the tree is the reporting chain, not the org chart.
Employee history (grouped) Three timeline-shaped child tables attach to Employee: education (school/qualification/level/year, free text), external work history (prior employer, designation, salary, contact — all free text, since the prior employer is outside the system), and internal work history (a date range paired with a branch, department, and designation — the only one of the three that links back into this document’s other master data, recording past internal assignments). None of the three is read anywhere else in the codebase beyond display — a record of history, not an input to any calculation.
Employee Group A named list of employees (a child table carrying the employee reference, name, and login account) with no owning Legal Entity and no lifecycle. It is read outside the HR area as a way to address many employees at once — resolving a group name to its members’ login-linked addresses for message/call routing — a general-purpose “named set of people” utility, not an HR-specific construct.
Holiday List / Holiday A named, dated, unscoped master (no owning Legal Entity) with a from/to date range and a child table of Holiday rows (date, description, weekly-off flag, half-day flag). Two population helpers exist: generating every occurrence of a chosen weekday within the date bounds, and pulling a country/subdivision’s public holidays for the covered years from a bundled calendar library. A Legal Entity can nominate one as default; an Employee can override it.
3.2 Tree mechanics and consistency reality
Both real trees (Legal Entity, Department) and the reporting chain use the same nested-set pattern: a parent self-reference plus tree-position integers recalculated whenever a parent changes, with recalculation skipped in specific bulk-load contexts (initial setup, wizard-driven department seeding) and run once afterward instead.
What is validated, and what is not. Parent-must-exist and root-protection rules apply within each tree separately. Across trees, and across the Legal Entity boundary, the only guard found anywhere is client-side: the employee form’s department field is queried with a filter restricting choices to departments owned by the employee’s already-selected Legal Entity. That filter runs in the desk form only — the Department tree’s own parent-department picker filters only on “is this a group node,” not matching entity — and no controller-level check on either record would catch a department assigned to an employee of a different entity, or a department whose parent belongs to a different entity, if written through the API, an import, or a bulk edit. Branch and Designation have no entity field to check against in the first place.
3.3 How dependent subsystems consume this master data
- Department, Branch, Designation are consumed purely as reference values: transactional records store the link and display the name; no downstream logic branches on which was picked, beyond the approval-authorization rules elsewhere in this plan, which can key a threshold off Designation.
- Holiday List is consulted as a pure lookup — “is this date a holiday for this calendar” — by more than one unrelated subsystem: project task scheduling walks a proposed date forward past holidays before committing it, and a support-ticket service-level calculation separately walks working time forward skipping non-working days, using its own locally-written holiday check against a supplied date list rather than the shared lookup — a second implementation of the same idea, not a shared call.
- Employee Group is a resolver: given a group name, look up member rows and project each member’s login address to build a recipient list for a subsystem with no other concept of “employee.”
3.4 Error handling
- Legal Entity abbreviation/currency guards: a short code must be unique across entities, and default currency cannot change once transactions exist (covered elsewhere in this plan; noted here because Department’s generated name depends on the abbreviation).
- Department root protection: the shared root is read-only in the desk form and cannot be renamed.
- Reporting-chain guard: an employee cannot report to themselves.
- Exit guard: status cannot move to a terminal “left” state while other active employees still report to them; a relieving date is required when it does.
- Holiday List guards: every holiday row must fall within the list’s own date range; duplicate dates in one list are rejected on save.
- Absence of a cross-entity guard: nothing raises an error for a department, or a manager, that crosses a Legal Entity boundary — a gap, not an undiscovered feature, confirmed by reading every validation method on both records.
4. Scale and Reliability
- Three independent nested-set trees, three independent maintenance costs. Legal Entity, Department, and the reporting chain each recompute their own tree-position integers on structural change; none is triggered by changes in the other two, so a large reorganization only pays the reporting-chain’s recomputation cost.
- A single shared Department root is a cross-tenant bottleneck in the largest deployments. Every Legal Entity’s department tree hangs off one common root record, so any operation near the root touches a structure shared by every entity in the installation.
- Branch, Designation, and Holiday List have no entity scope to shard by. As Legal Entities multiply, these lists grow as one global pool; list/search views show every entity’s labels together.
- Holiday List lookups are simple existence checks against a date-indexed child table, cheap regardless of how many unrelated subsystems call them.
5. Trade-off Analysis
| Decision | Trade-off |
|---|---|
| Department is a real nested-set tree; Branch and Designation are flat labels | Departments support genuine sub-grouping; Branch and Designation stay simple and need no tree maintenance — but an org with real branch or job-title hierarchies has nowhere to express that without misusing Department. |
| Department’s root is shared across every Legal Entity, not one root per entity | Simpler bootstrap and a single tree view spans every entity’s departments — at the cost of a tree that structurally crosses entity boundaries with only a field-level, not tree-level, scoping rule underneath. |
| Cross-entity consistency enforced only in the desk-form UI, not in any controller | Fast to build and invisible to the common case — at the cost of a real, exploitable gap for any programmatic write path (API, import, bulk edit), which sees no filter and no validation. |
| Holiday List and Employee Group are unscoped, general-purpose masters rather than HR-owned records | Lets unrelated subsystems depend on them without importing HR-specific concepts, and keeps one list reusable across entities — at the cost of no per-entity access boundary: any list is visible and assignable everywhere. |
| Employee history (education, external/internal work) is captured but consumed nowhere else | Zero risk of another module depending on its shape — but the capture effort produces no automated value today; purely a record for a human reader. |
| The reporting chain is its own nested-set tree inside Employee, independent of Department | The management chain can differ freely from the department chart — at the cost of no single query answering “does this person’s manager sit in their own department,” since the two trees share no linkage. |
6. What to Revisit as the System Grows
- Close the cross-entity consistency gap at the controller level, not just the desk-form query filter, so a department (or its parent) can never silently cross a Legal Entity boundary via the API, an import, or a bulk edit.
- Give large multi-entity installations a way to partition Branch, Designation, and Holiday List, or document that these remain organization-wide pools by design.
- Reconsider the shared Department root once Legal Entities grow numerous enough that the shared root becomes a real contention point for tree-rebuild operations.
- Decide whether Branch or Designation should ever become real hierarchies — today’s flat-label model has no growth path toward sub-branches or a job-title ladder without a schema change.
- Reconcile the two independent holiday-check implementations before either one changes behavior and the two drift apart.