Heads up: the demo runs on a free-tier server, so the first load can take around 30
to 50
seconds to wake up.
Creating a client, logging a job, and raising an invoice, with GST and totals calculated
on the
server.
The problem
Small trade businesses run on admin that a generic tool rarely fits: who the client is, which jobs
are on,
and what has been invoiced, with GST handled correctly. I built TradieFlow as a focused tool for
exactly
that workflow, and as a way to practise building and shipping a production-grade full-stack
application from
scratch.
What it does
Client to job to invoice
A clean relational model that cascades from each client down through their jobs to individual
invoice
line items.
Automatic GST
Australian GST at 10 percent and invoice totals are calculated and validated on the server,
not in the
browser.
Status tracking
Jobs move through Quoted, Scheduled, In progress, and Completed. Invoices move through Draft,
Sent, and
Paid.
RESTful API
A documented Web API with input validation and standards-based error responses.
Architecture
The frontend is a React and TypeScript single-page app, served as static files over HTTPS from AWS
S3 and
CloudFront on a custom domain. It calls a .NET 9 Web API running in a Docker container on Render,
which
persists data to a managed PostgreSQL database on Neon through Entity Framework Core.
Inside the API I kept a layered Clean Architecture. The Domain layer holds the entities and business
rules,
including the GST and invoice calculator, with no framework dependencies. Application defines the
service
contracts, Infrastructure implements them with EF Core, and the API layer exposes the controllers. I
deliberately kept it a single deployable service rather than microservices, which is the right level
of
complexity for a focused app and far easier to operate.
API · ASP.NET Core controllers
Infrastructure · EF Core, service implementations
Application · interfaces & DTOs
Domainentities, enums, and the GST / invoice calculator, with
no framework
dependencies
Engineering highlights
The GST and invoice calculator lives in the Domain layer as pure logic, covered by unit tests.
Integration tests spin up a real PostgreSQL instance in a container with Testcontainers, so they
run
against the real database engine rather than a mock.
Every push runs the full test suite in GitHub Actions before anything ships.
The whole API is containerised with Docker and deploys automatically.
The database is seeded on first run, so the live demo always opens with sample data.
Challenges and what I learned
The container crashed on startup with a segmentation fault that never happened locally. Reading
the
stack trace, I traced it to a .NET runtime memory-protection feature on the host and fixed it
with a
runtime flag.
I wired Entity Framework migrations to run on startup and configured CORS for the deployed
frontend, so
the schema and cross-origin access are set up automatically on every deploy.
When I learned the free database expires monthly, I moved it to a persistent free Postgres on
Neon and
kept the API URL stable, so the frontend and everything downstream needed no changes.
The deployment and operations problems taught me as much as the application code did.