Skip to content
All work

Case study · Government Payroll

ePayroll

National payroll platform covering employee validation, payroll data, payslip generation and reporting for the public sector.

Payslip generation turnaround
~7 days → ~24 hours
Role
Backend Engineering · System Design · Technical Leadership
Team
Co-led ~4 engineers
Domain
Government Payroll

Overview

ePayroll supports auxiliary payroll activities for government employees: validating that employees are in the right state, maintaining payroll entries, generating payslips and producing reports. It is split into three applications — PayAdmin for master records and payroll data, PayVerify for validation by management units, and PayServices for employee-facing services such as payslips.

The problem

The previous process took roughly a week to produce payslips after payroll data arrived, and reports only became available hours after validation had finished. Validation, administration and employee services needed to share data reliably while operating on more than twenty million records.

My role

  • Built and maintained APIs for PayAdmin, PayVerify and PayServices.
  • Designed and implemented the payslip generation service.
  • Wrote the synchronization service moving validated data from PayVerify into PayAdmin.
  • Wrote the report queries behind operational and management reporting.
  • Built the service that keeps user identities synchronized with Keycloak.
  • Advised the frontend team on API contracts and implementation.
  • Co-led a team of around four engineers and mentored junior developers.

Architecture

  1. Applications

    • Administration portal Master records and payroll entries
    • Validation portal Management-unit validation
    • Employee services Payslips and self-service
  2. Services

    • Domain APIs One per application
    • Payslip generation Background service
    • Validation sync Verify → Admin
    • Identity sync Users → Keycloak
  3. Platform

    • PostgreSQL 20M+ records
    • Keycloak Identity & access
    • Azure AKS Containerised workloads
Conceptual view. Service boundaries are simplified and internal names omitted. Areas I built or led

Scale

Records
20M+
Employee, payroll and validation history on PostgreSQL.
Applications
3
Administration, validation and employee services sharing one domain.
Engineers co-led
~4

Engineering challenges

Working with 20M+ records

Payroll history, validation results and employee master data grow every cycle. Queries that are harmless on a development dataset become the slowest part of the system in production, and reporting queries compete with transactional work for the same tables.

Generating payslips at volume

Payslips have to be produced for every employee in a cycle, reflect validated data only, and be re-generated safely if a run is interrupted. Doing this inside a request/response API is not practical at this scale.

Keeping three applications consistent

Validation happens in one application, administration in another, and employees consume the results in a third. Data has to move between them without being lost, duplicated or applied out of order.

Identity synchronization

Access is controlled through Keycloak, but the people who need access are defined by payroll and organizational data. The two have to stay aligned as staff join, move or leave.

Approach

  • Background processing for heavy work. Payslip generation runs as a dedicated background service that processes work in batches, so a run can be monitored, resumed and retried without blocking user-facing APIs.
  • The right data-access tool for each path. Entity Framework handles most transactional workflows; Dapper and hand-tuned SQL handle bulk reads and reporting, where control over the query shape matters.
  • Reporting designed around query plans. Report queries were written and indexed against production-sized data so they can be run on demand instead of being pre-computed hours later.
  • Explicit synchronization services. Moving validated data and identity changes are separate, idempotent services with clear ownership, which makes them safe to re-run.
  • Containerised deployment. Services run on Azure Kubernetes Service, giving the team consistent environments and independent scaling for background workloads.

Impact

  • ~7 days → ~24 hours

    Payslip generation

    Generation runs as a background process instead of a manual, sequential job.

  • Hours → on demand

    Report availability

    Reports are generated when required rather than hours after validation completes.

Technologies

  • .NET
  • C#
  • PostgreSQL
  • Entity Framework
  • Dapper
  • Azure AKS
  • Kubernetes
  • Docker
  • Keycloak

Lessons

  1. 01 Batch work on large tables is mostly a data-access problem. Choosing where to use EF Core and where to drop to Dapper and hand-written SQL mattered more than any application-level optimization.
  2. 02 Synchronization between applications needs explicit ownership of each record and a way to re-run safely. Treating sync as a first-class service made failures recoverable instead of manual.
  3. 03 Identity data drifts. Keeping Keycloak in step with the system of record is easier as a continuous process than as a one-off migration.

Let’s build reliable systems.

I’m interested in teams working on hard backend problems — data-heavy platforms, integrations and systems that have to stay correct under load. Open to remote roles as a:

  • Senior Backend Engineer
  • Senior .NET Engineer
  • Senior Software Engineer
  • Backend-focused Full-Stack Engineer