The Deadline That Made Me Rethink My Architecture: Building Timesheetflow with Domain Thinking
Source: Dev.to
That one question changed how I approached what I initially thought was “just Excel processing.”
I was building Timesheetflow—a small system to automate monthly employee timesheets that are still submitted as Excel files (because, realistically, many teams still live in spreadsheets).
The Real Workflow (What the Business Actually Does)
Timesheetflow’s workflow is simple—and very real:
- Admin sets a processing deadline (usually month‑end).
- Employees upload their own monthly timesheet to a Google Drive folder before the deadline (submissions are for the current month only).
- At the deadline, the system processes timesheets one by one.
- Approvers review and approve one, many, or all timesheets.
- Late submissions are allowed but clearly marked Late.
- Admin downloads an Excel salary summary for that month.
When I wrote it down like this, I realized something:
This isn’t an “Excel automation” problem.
This is a monthly payroll close problem.
What I Thought the Problem Was
My first approach was very implementation‑driven:
- “There’s a Drive folder.”
- “There are Excel files.”
- “I need to parse and export.”
So my architecture naturally became a pipeline:
- Drive client downloads files
- A parser extracts rows
- A validator checks columns
- An exporter builds a consolidated report
It looked clean… but the code kept reading like:
“update tables, move files, generate output”
instead of:
“close the month, enforce the deadline, approve submissions, finalize payroll”
That difference sounds subtle, but it changes everything—especially when requirements evolve.
What I Realized Afterward
Good architecture isn’t just about layers and patterns.
It’s about meaning.
The moment I treated the month‑end workflow as a domain (instead of a batch job), the design got clearer:
- A deadline isn’t a timestamp field—it’s a rule that changes system behavior.
- “Late” isn’t a warning label—it affects approval visibility and auditability.
- “Latest submission wins” isn’t a query trick—it’s a policy the business depends on.
The Ubiquitous Language I Started Using
Once I stopped thinking in “files” and started thinking in “business”, the vocabulary became obvious:
| Term | Meaning |
|---|---|
| Monthly Run / Payroll Period | The month being closed |
| Deadline | The cutoff time |
| Timesheet Submission | One employee’s monthly entry |
| Late Submission | Allowed, but flagged |
| Latest Submission Wins | Policy when employees upload multiple versions |
| Approval | Approve/reject; bulk approve valid |
| Salary Summary Export | The admin deliverable |
When your code uses this language, you don’t need long comments to explain what it does.
Before vs. After (Pipeline Thinking vs. Domain Thinking)
Before: “Process files”
// Pipeline mindset: process whatever is in the folder
var files = drive.ListXlsxFiles(folderId);
foreach (var file in files)
{
var rows = ParseExcel(file);
Validate(rows);
Save(rows);
}
ExportSalarySummary();
It works—but it hides the real rules:
- Which month is this?
- Is the deadline enforced?
- Which file wins if an employee uploads twice?
- What happens after approval?
After: “Close a monthly run”
// Domain mindset: close a payroll period with explicit rules
run.LockAtDeadline(now);
run.RefreshSubmissionsFromDrive(); // latest submission wins
run.FlagLateSubmissions(); // uploaded after deadline
foreach (var submission in run.ActiveSubmissions())
{
submission.ValidateCurrentMonthOnly();
}
approvals.ApproveAllValid(run); // late is a badge, invalid blocks approval
var report = payroll.ExportSalarySummary(run); // Excel for admin
Now the code reads like the business process:
- lock the period
- evaluate submissions under known rules
- approve what’s valid
- export the payroll summary
That shift made everything easier to maintain and explain.
Key Domain Rules (That Matter More Than Excel)
- Current month only – Employees can submit timesheets for the active month only.
- Deadline locks the run – At the deadline, the run transitions into a “locked” phase for processing.
- Late submissions are allowed, but flagged – After the deadline, submissions are marked Late and stay visible in approval/export.
- Latest submission wins – If an employee uploads multiple timesheets for the same month:
- the system uses the most recent file (by Drive timestamps)
- older ones are marked superseded
- Invalid beats late – If a timesheet is invalid, it’s invalid—whether it’s late or not.
- Late is a badge.
- Invalid is a blocker.
- Salary export is the admin deliverable – The end result is not “processed rows”; it’s a monthly Excel summary that payroll/admin can use.
Implementation Notes (Tech Stack)
Timesheetflow is built with:
- ASP.NET Core (.NET 8) – Web + API
- Hangfire – Deadline scheduling and background processing
- ClosedXML – Excel parsing/export
- Google Drive – Submission channel (Workspace folder + service‑account access)
Even with a simple stack, modeling the domain clearly made the system feel “product‑like” instead of “script‑like”.
Key Takeaways
- This wasn’t really an Excel problem—it was a month‑end payroll close problem.
- Deadlines, late flags, and “latest submission wins” are domain rules, not just utility logic.
- Building a ubiquitous language around those rules yields code that reads like the business process, making maintenance and future changes far easier.
When Code Mirrors the Business Language
- When code mirrors the business language, it becomes easier to read, test, and extend.
- The best architecture doesn’t just run—it communicates intent.
Closing Thoughts
I started Timesheetflow thinking I was building an Excel automation tool.
The real value came when I stopped designing around files and started designing around the workflow the business lives by every month.
If you’ve ever had a moment where a “simple automation” turned out to be a domain problem in disguise, I’d love to hear about it.
Connect with Me
- GitHub:
- LinkedIn:
If you’d like, I can also share:
- A minimal domain model (
MonthlyRun,TimesheetSubmission,Approval) for .NET 8 - A Hangfire job layout for the deadline + processing pipeline
- An example
Salary_Summary.xlsxstructure (ByEmployee / Details / Exceptions)
Just let me know whether you want the article to include code that compiles (full sample classes) or keep it at conceptual snippets like this post.