Designing a SaaS Invoicing Platform With NestJS, Prisma, PostgreSQL, and Next.js

Architecture notes from building a multi-tenant invoicing MVP with organization ownership, invoice workflows, manual payments, generated documents, background jobs, and audit visibility.

Production Architecture

Small-business invoicing looks simple until the system has to support teams, business profiles, customers, invoice totals, payment state, generated documents, and audit history. A basic invoice form can be built quickly. A SaaS invoicing system needs stronger boundaries.

Billora was structured as a SaaS product foundation rather than a single-user billing tool. The important design decision was to model ownership early: users operate inside organizations, organizations contain members and invites, and business, customer, invoice, payment, and audit records are scoped through that ownership model.

Core Architecture

The web application uses Next.js and TypeScript for authenticated dashboard, customer, invoice, settings, registration, and invite flows. The backend uses NestJS because the API benefits from modules, services, guards, DTO validation, and dependency injection.

PostgreSQL and Prisma handle the relational model:

  • users
  • organizations
  • organization members
  • invites
  • businesses
  • customers
  • invoices
  • invoice items
  • payments
  • audit logs

Redis-backed jobs were introduced for invoice document generation and email orchestration. Even when email delivery is not connected yet, the boundary matters: generated documents and outbound messages should not become blocking request work.

Invoice Creation Flow

A typical invoice starts in the web app when an authenticated user selects a business and customer, enters line items, tax, discounts, dates, and notes, then submits the invoice.

The API validates the payload through DTOs, checks that the user can access the selected organization, business, and customer, calculates line totals, subtotal, tax amount, discount, and final total, then writes the invoice and invoice items through a transaction.

That transaction boundary matters because an invoice without its items is not useful state. The system should either persist the complete invoice or fail cleanly.

Payment State Is Domain Logic

Manual payments are not just records appended to an invoice. They change the invoice state.

When a payment is recorded, the API verifies ownership, checks the outstanding balance, creates the payment record, recalculates paid amount, updates the invoice status, invalidates old generated documents, and writes an audit entry.

This prevents common billing failures:

  • overpayment against an invoice
  • stale PDF documents after payment changes
  • invoice totals being reduced below already received payments
  • unclear history around who changed billing state

Reliability Decisions

The MVP uses validation and ownership checks as first-class backend concerns. Every create or update path needs to answer three questions:

  1. Is the payload valid?
  2. Does the user own or belong to the required organization scope?
  3. Will this change preserve invoice and payment consistency?

Audit logs are also part of the reliability model. Billing systems need traceability because mistakes are operationally expensive. A payment, invoice update, invite action, or customer change should leave a readable trail.

What Was Deferred

The MVP intentionally defers real payment gateway integration, production email delivery, and full mobile implementation. Those features are important, but they should sit on top of a stable ownership, invoice, payment, and document model.

Adding Stripe or another gateway too early can hide weak internal state. The safer path is to make manual payments, totals, status transitions, audit logs, and generated documents reliable first.

System Shape

textNext.js Web App
  -> Authenticated dashboard, customer, invoice, settings, invite flows

NestJS API
  -> Auth, organizations, businesses, customers, invoices, payments, audit, jobs

PostgreSQL + Prisma
  -> Relational ownership, billing records, invoice items, payments, audit logs

Redis Jobs
  -> Invoice PDF generation and email orchestration boundaries

The result is not a finished billing company. It is a working SaaS invoicing foundation with the correct pressure points visible: tenancy, authorization, invoice consistency, payment state, generated document freshness, background work, and auditability.