Skip to content
Projects
2 min read

HR Management System (Implementation)

Working Next.js + NextAuth + Prisma implementation of the HR specification, with a 69-model schema, 18 feature modules, a generic approval-workflow engine, role-based access, and audit trails.

Role
Full-stack engineer
Year
2026
Status
beta
full-stackenterprise

Overview

The implementation track of the HR Specification project. A Next.js 16 + NextAuth + Prisma application that turns the operating-model spec into a deployable system: a 69-model normalized schema across 11 schema files, 18 feature modules (employees, leave, attendance, payroll, recruitment, performance, training, onboarding, offboarding, announcements, documents, helpdesk, cases, org-chart, analytics, reports, settings, admin), strict role-based access, audit trails on every mutation, and an opinionated form pipeline.

Problem

Translating a 300-page operating model into a deployable system requires more than picking a stack. Every persona's permission boundary, every workflow's approver chain, and every audit requirement has to be enforced at the data layer, not just the UI.

Approach

Use NextAuth for session management with strict role checks. Encode RBAC at the Prisma query layer so a UI bug can't accidentally expose data outside a user's scope. Use react-hook-form + zod for input validation aligned to the spec's field-level rules. Log every mutation through a unified audit pipeline.

Architecture

  • Auth: NextAuth 5 (beta) with role-aware sessions and middleware-enforced route guards.
  • Data: Prisma 6 over PostgreSQL with migrations driving the data model.
  • Server state: TanStack Query.
  • Client state: Zustand for cross-page state.
  • Forms: react-hook-form + zod, validation aligned to the spec.
  • UI: shadcn/ui primitives over Tailwind CSS 4.
  • Logging: Pino for structured logs across server boundaries.

Tech stack

  • Frontend / backend: Next.js 16, React 19, TypeScript
  • Auth: NextAuth 5 (beta)
  • ORM and DB: Prisma 6, PostgreSQL
  • State: Zustand, TanStack Query, TanStack Table
  • Forms: react-hook-form, zod
  • UI: shadcn/ui, Tailwind CSS 4
  • Observability: Pino

Engineering highlights

  • RBAC at the data layer: every Prisma query goes through scope-aware helpers enforcing five permission scopes (own, team, department, branch, company); a developer can't accidentally bypass access rules.
  • Audit-everything pipeline: every mutation produces an audit row with actor, target, before/after, and reason. EmploymentHistory separately tracks 15 employment-change types with before/after snapshots.
  • Polymorphic approval engine: ApprovalRequest plus ApprovalStep support any entity requiring approval (leave, reimbursement, resignation) with sequential or parallel chains and escalation.
  • Comprehensive notifications: a single Notification model covers 20+ event types (leave, approvals, document/contract expiry, probation, reviews, training, onboarding, payslips, anniversaries).
  • Spec-aligned forms: input validators map directly to the spec's field rules, so changes to the spec propagate as code changes, not Slack messages.
  • Beta-by-design: NextAuth 5 was still beta during implementation, so adapters and migrations were chosen for forward-compatibility.

Outcome

Production-ready foundation, in beta with a controlled rollout. Building on the specification means there's no ambiguity about what "done" looks like for each module.

Lessons

  • Implementing a spec written by the same author is unusually fast: the engineering decisions slot directly into the operating-model decisions.
  • Auth at the data layer beats auth at the route layer every time.
  • Audit logs only matter if they're queryable. Schema them like first-class data, not stringly-typed events.

Want to dig deeper?

Ask my AI agent anything about how this was built, what tradeoffs I made, or how it could fit your team.

Ask my AI →