Payroll split — current, planned, migrating
One system three times: Payroll inside one application on a shared database, Payroll as its own service with its own database, and the state in between with @migration_target / @planned.
Current (index.krs)
Section titled “Current (index.krs)”// payroll-split/index.krs: the CURRENT architecture.// The second subject of the first-run guide (#3601). Compare it with planned.krs// (the target) and migrating.krs (the state in between).//// Attendance and Payroll are separate domains, but both live in one// Workforce Application and share one Workforce Database. The Payroll Team// owns the Payroll domain without owning any service.
system Workforce { label "Workforce Platform"
user Employee [human] { label "Employee" description "Records hours and checks payslips" } user PayrollStaff [human] { label "Payroll staff" description "Reviews pay runs and transfers" }
service WorkforceApp { label "Workforce Application" description "One deployable that holds attendance and payroll"
domain Attendance { label "Attendance" usecase RecordHours { label "Record hours" resource WorkforceDB.Timesheets { operations create } } usecase ApproveTimesheet { label "Approve a timesheet" resource WorkforceDB.Timesheets { operations update } }
Attendance -> Payroll "Hand over closed timesheets" } domain Payroll { label "Payroll" usecase CalculatePay { label "Calculate pay" resource WorkforceDB.Payslips { operations create } resource WorkforceDB.Timesheets { operations read } } usecase TransferPay { label "Transfer pay" resource WorkforceDB.Transfers { operations create } } } }
service ReminderWorker { label "Reminder Worker" description "Sends timesheet reminders in the background"
domain Reminders { label "Reminders" usecase SendReminder { label "Send a reminder" resource WorkforceDB.ReminderQueue { operations update } } } }
service BankTransfer [external] { label "Bank Transfer" description "Salary payments, run by a bank" }
database WorkforceDB { label "Workforce Database" description "Shared by every part of the platform" table Timesheets { label "Timesheets" } table Payslips { label "Payslips" } table Transfers { label "Transfers" } table ReminderQueue { label "Reminder queue" } }
Employee -> WorkforceApp "Record hours and check payslips" PayrollStaff -> WorkforceApp "Review pay runs" WorkforceApp -> ReminderWorker "Schedule reminders" WorkforceApp -> BankTransfer "Send transfers"}
organization Corporate { label "Corporate Engineering"
team workforce { label "Workforce Team" description "Builds the employee portal and the attendance flow" owns WorkforceApp owns Attendance }
team payroll { label "Payroll Team" description "Owns the pay rules, but ships them inside the Workforce Application" owns Payroll }
team platform { label "Platform Team" description "Runs the shared database and the background workers" owns ReminderWorker owns Reminders }}Planned (planned.krs)
Section titled “Planned (planned.krs)”// payroll-split/planned.krs: the TARGET architecture.// Payroll runs as its own service with its own database, and the team// that owns the pay rules owns the service that runs them.// Compare it with index.krs (the current architecture).
system Workforce { label "Workforce Platform"
user Employee [human] { label "Employee" description "Records hours and checks payslips" } user PayrollStaff [human] { label "Payroll staff" description "Reviews pay runs and transfers" }
service WorkforceApp { label "Workforce Application" description "Employee portal and attendance"
domain Attendance { label "Attendance" usecase RecordHours { label "Record hours" resource WorkforceDB.Timesheets { operations create } } usecase ApproveTimesheet { label "Approve a timesheet" resource WorkforceDB.Timesheets { operations update } } } }
service PayrollService { label "Payroll Service" description "Pay runs and transfers, own release cycle"
domain Payroll { label "Payroll" usecase CalculatePay { label "Calculate pay" resource PayrollDB.Payslips { operations create } } usecase TransferPay { label "Transfer pay" resource PayrollDB.Transfers { operations create } } } }
service ReminderWorker { label "Reminder Worker" description "Sends timesheet reminders in the background"
domain Reminders { label "Reminders" usecase SendReminder { label "Send a reminder" resource WorkforceDB.ReminderQueue { operations update } } } }
service BankTransfer [external] { label "Bank Transfer" description "Salary payments, run by a bank" }
database WorkforceDB { label "Workforce Database" description "Timesheets and reminders" table Timesheets { label "Timesheets" } table ReminderQueue { label "Reminder queue" } }
database PayrollDB { label "Payroll Database" description "Owned by payroll alone" table Payslips { label "Payslips" } table Transfers { label "Transfers" } }
Employee -> WorkforceApp "Record hours" Employee -> PayrollService "Check payslips" PayrollStaff -> PayrollService "Review pay runs" WorkforceApp -> PayrollService "Hand over closed timesheets" WorkforceApp -> ReminderWorker "Schedule reminders" PayrollService -> BankTransfer "Send transfers"}
organization Corporate { label "Corporate Engineering"
team workforce { label "Workforce Team" description "Builds the employee portal and the attendance flow" owns WorkforceApp owns Attendance }
team payroll { label "Payroll Team" description "Owns the pay rules and the service that runs them" owns PayrollService owns Payroll }
team platform { label "Platform Team" description "Runs the shared database and the background workers" owns ReminderWorker owns Reminders }}Migrating (migrating.krs)
Section titled “Migrating (migrating.krs)”// payroll-split/migrating.krs: the state IN BETWEEN.// Payroll Service is running, but it still reads and writes the shared// Workforce Database: its own Payroll Database is planned, not built.// Moving the data is the step that decides when the split is done.
system Workforce { label "Workforce Platform"
user Employee [human] { label "Employee" description "Records hours and checks payslips" } user PayrollStaff [human] { label "Payroll staff" description "Reviews pay runs and transfers" }
service WorkforceApp { label "Workforce Application" description "Employee portal and attendance"
domain Attendance { label "Attendance" usecase RecordHours { label "Record hours" resource WorkforceDB.Timesheets { operations create } } usecase ApproveTimesheet { label "Approve a timesheet" resource WorkforceDB.Timesheets { operations update } } } }
service PayrollService @migration_target(from: WorkforceApp) { label "Payroll Service" description "Split out, still on the shared database"
domain Payroll { label "Payroll" usecase CalculatePay { label "Calculate pay" resource WorkforceDB.Payslips { operations create } resource WorkforceDB.Timesheets { operations read } } usecase TransferPay { label "Transfer pay" resource WorkforceDB.Transfers { operations create } } } }
service ReminderWorker { label "Reminder Worker" description "Sends timesheet reminders in the background"
domain Reminders { label "Reminders" usecase SendReminder { label "Send a reminder" resource WorkforceDB.ReminderQueue { operations update } } } }
service BankTransfer [external] { label "Bank Transfer" description "Salary payments, run by a bank" }
database WorkforceDB { label "Workforce Database" description "Payroll tables have not moved yet" table Timesheets { label "Timesheets" } table Payslips @deprecated { label "Payslips" } table Transfers @deprecated { label "Transfers" } table ReminderQueue { label "Reminder queue" } }
database PayrollDB @planned { label "Payroll Database" description "Where the payroll tables will move" table Payslips { label "Payslips" } table Transfers { label "Transfers" } }
Employee -> WorkforceApp "Record hours" Employee -> PayrollService "Check payslips" PayrollStaff -> PayrollService "Review pay runs" WorkforceApp -> PayrollService "Hand over closed timesheets" WorkforceApp -> ReminderWorker "Schedule reminders" PayrollService -> BankTransfer "Send transfers"}
organization Corporate { label "Corporate Engineering"
team workforce { label "Workforce Team" description "Builds the employee portal and the attendance flow" owns WorkforceApp owns Attendance }
team payroll { label "Payroll Team" description "Runs Payroll Service; depends on the Platform Team for its data until the move" owns PayrollService owns Payroll }
team platform { label "Platform Team" description "Runs the shared database and the background workers" owns ReminderWorker owns Reminders }}© 2026 Hiroki Kondo · Licensed underApache-2.0