Oksa Satya.

July 11, 2026 · 8 min read

HRIS & Payroll Application Architecture: Lessons from Building Dexova

HRISPayrollGoArchitecture

Calculating salaries looks like arithmetic: hours times rate, subtract tax, done. Until you actually build it. This article distills the most important architectural decisions I made building the HRIS & Payroll module in Dexova — and the mistakes I was lucky to catch early.

Why payroll must not be computed from live data

The first mistake almost every amateur payroll system makes: computing salaries directly from an attendance table that can still change. An employee files a correction to their clock-in, HR approves it — and a payslip that was already paid suddenly no longer matches the data.

The fix is a locked period. In Dexova, the monthly attendance recap locks the attendance data as a snapshot; a payroll run can only be generated from a period that is already locked. Corrections after the lock flow into the next period as adjustments — not by rewriting history.

Regulatory rules as configuration, not if-else

Overtime in Indonesia is tiered (PP 35/2021): the first hour and subsequent hours have different multipliers, and holidays are different again. Writing that as a chain of if-else means every regulatory or company-policy change is a new deploy.

In Dexova, overtime rates, the daily-rate divisor, rounding strategy, and work-week type are per-company configuration. The code only knows how to evaluate a rule; the rule's values belong to data. A nice side effect: testing a calculation becomes a matter of preparing a config + attendance input, then comparing the output.

  • Tiered overtime rates for the 1st hour / subsequent hours / holidays — configuration.
  • Cutoff and payday are separated; payday shifts automatically on bank holidays.
  • Salary components and per-employee rates (pro-rata for new hires) — data, not code.
  • One-off adjustments (bonuses, THR, severance) live on the run, not as permanent components.

The payroll run as an async job

Computing salaries for hundreds of employees is not a single-HTTP-request job. A payroll run in Dexova executes as an async job with progress monitoring: HR presses one button, watches the progress, and gets a result they can review before finalizing.

The same pattern powers bulk employee import from Excel — any heavy work touching many rows must not block the UI, and must be trackable.

Attendance: guard validity at the source

Payroll is only as good as its attendance data. That is why validation happens at the point of entry: check-in via a PWA with geofencing (the app detects the nearest office and rejects clock-ins outside the radius), WFH via an approved two-address model, and attendance corrections that always go through approval — not free edits in the database.

Cleaning up dirty data later is always more expensive than rejecting it up front.

Summary

If it has to fit in one sentence: separate the rules from the code, lock the data before computing, and make heavy work a trackable job. Those three are what make payroll auditable — and what makes sleeping easier on payday.

Building something similar?

I take on projects and technical consulting around business systems.