Skip to content

InvoiceFlow

Invoice generation, client billing, and payment tracking in one streamlined dashboard

InvoiceFlow began as a practical answer to a daily annoyance: invoice generation, client billing, and payment tracking in one streamlined dashboard. What started as a scratch solution grew into a full product because the problems it solved kept recurring — for me first, and then for everyone who saw it in action.

A production-grade SaaS platform designed from the ground up around a real workflow. The project started as a question — 'why is this still done in spreadsheets?' — and grew into a full product with authentication, billing-ready features, admin tooling, and an architecture that survives real users. Built with the modular MVC structure from this portfolio's architecture articles: thin routes, service layers for business rules, and repositories behind every database call.

Features

- Role-based access with owner, admin, and member permissions on every resource - Full-text search across entities with filterable, sortable, paginated tables - Server-side rendered pages with progressive enhancement for instant interactivity - CSRF-protected mutations, rate-limited auth, and audit logging on sensitive actions - Responsive glassmorphism UI shared with the rest of the portfolio's design system - Dark mode with a persisted theme preference and reduced-motion support

Architecture

The codebase follows the five-layer pattern documented in the blog: routes stay thin and declarative, services own business rules, repositories own all database access, and views receive only pre-shaped data. Every module is scaffolded from the same template, which means a new resource type — say, client statements — is a repository, a service, and a thin route following an existing shape.

Key Technology Decisions

|Decision|Choice|Alternative Considered|Why This Won| |---|---|---|---| |Data store|MongoDB with Mongoose|PostgreSQL|Document shape matches screen shape; schema evolves weekly| |Rendering|Server-side EJS|React SPA|Instant first paint; content-first product; minimal JS| |Styling|Tailwind component layer|CSS modules|One glass design system across three portfolio products| |Auth|Session cookies + CSRF|JWT|Server-owned sessions with HttpOnly cookies; revocation by default| |Deployment|PM2 cluster + Nginx|Serverless functions|Zero-downtime reloads on one well-understood box|

undefined

Real-World Impact

The platform's most demanding moment came during a shared-account stress test: forty concurrent users hammering the same workspace while a background job recalculated team metrics. The indexed queries and the caching decorator absorbed the load without a single timeout, and the latency percentiles barely moved. The incident report from that day is two lines long — because nothing broke, and the monitoring data proved it.

Measured Results

- Dashboard queries optimized from 40 per render to 1 indexed query per table - P95 page render under 400ms on a mid-range phone, 4G connection - Zero-downtime deploys on every release via PM2 rolling reloads - Audit trail covering every sensitive action with request IDs

Frequently Asked Questions

What makes this production-ready rather than a demo?

Everything an operator needs exists: health endpoints, structured JSON logs, rate limits, CSRF protection, audit trails, encrypted backups, and a CI pipeline that builds, tests, and deploys without human steps.

Why MongoDB instead of a relational database?

The screens are document-shaped: every page renders one entity plus its embedded summaries. The schema evolves weekly, and the versioned-migration pattern handles evolution without downtime. PostgreSQL would be chosen the day complex relational reporting becomes the product.

How does the design system stay consistent?

The Tailwind component layer — glass-card, glass-panel, glass-chip, glass-input — is shared across the portfolio's products, so every page is assembled from the same vocabulary by construction, not by discipline.

What was the hardest part to build?

The permission model. Every resource is owned, shared, or public, and ownership checks must run server-side on every route. The solution is a small middleware that verifies ownership before handlers run, and it has been bug-free since the third week.

Is the codebase structured for growth?

Yes — new modules copy an existing template, which is why the architecture article calls it 'survivable growth'. Each resource type is an independent module that can be deleted without touching its neighbors.

Conclusion

InvoiceFlow is a live demonstration of everything this portfolio preaches: modular architecture, indexed queries, server rendering, and deployment pipelines that make shipping boring. The patterns are public in the blog articles — steal them, adapt them, and build something that outgrows its prototype without collapsing.

Gallery