!DOCTYPE html> Zoe's Project

Full-stack case study

TradieFlow

A job, client, and invoice manager for Australian tradies. Designed, built, tested, and deployed end to end.

Heads up: the demo runs on a free-tier server, so the first load can take around 30 to 50 seconds to wake up.

Demo of TradieFlow creating a client, a job, and an invoice with live GST totals.
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.

User's browser AWS · S3 + CloudFront React + TypeScript SPA, static over HTTPS .NET 9 Web API · Render ASP.NET Core in a Docker container PostgreSQL Neon, managed and persistent loads app (HTTPS) REST / JSON (HTTPS) EF Core SQL (SSL)

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
Domain entities, 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.

Tech stack

  • React
  • TypeScript
  • Vite
  • .NET 9
  • ASP.NET Core
  • C#
  • PostgreSQL
  • EF Core
  • Docker
  • xUnit
  • Testcontainers
  • GitHub Actions
  • AWS (S3 + CloudFront)
  • Render
  • Neon

See it in action

← Back to all projects