Back to portfolio
Live in production2024 — Present

SponsorOne — Child Education Sponsorship Platform for JJT

Small education NGOs track child sponsorships in spreadsheets and WhatsApp threads and reconcile donations by hand. The failure mode isn't inefficiency — it's that when a board member or a donor asks where a specific payment went, nobody can answer with confidence. SponsorOne records every payment against an append-only ledger. Sponsors see their own money trail. Admins get reconciliation and a system-wide audit log. Zakat is tracked separately from general donations, because the accounting rules differ and combining them is a genuine problem for the organizations I built this for.

Visit sponsorone.appFull-Stack Engineer · Sole Builder — requirements to production
THE PROBLEM

Sponsorship money was moving with no audit trail.

Education NGOs typically run child sponsorship programs on spreadsheets, WhatsApp threads, and paper receipts. Sponsors have no visibility into where their money actually goes, reconciliation is done by hand at month end, and there's no defensible record to show a board, a donor, or a regulator when they ask a hard question.

JJT needed to replace that entirely — for payments, donations, and Zakat, which carries its own compliance obligations under Islamic finance rules. The organization needed every transaction traceable, every sponsor able to see their own impact, and every action in the system auditable after the fact.

ARCHITECTURE & APPROACH

Design the invariants first, write code second.

Before a line of code was written, the domain model, database architecture, user roles and permissions, and financial tracking rules were designed and documented in a BRD and a set of architecture diagrams. That sequencing mattered: for a system handling other people's money, the rules that can never be broken have to be decided before the framework that will enforce them.

Clean Architecture

The domain layer is framework-free — no JPA annotations, no Spring types, no HTTP concerns inside business logic. Persistence and web layers are adapters around the domain, not the other way around. That's what let the same core survive decisions about hosting, database tooling, and deployment target without a rewrite.

Append-only financial ledger

Every payment, donation, and Zakat transaction is written once and never mutated. Immutability is enforced twice — once in the domain model, which exposes no update or delete path for ledger entries, and again at the PostgreSQL level, so even a direct database session can't quietly edit history.

Security by design

Access tokens live in memory only, never in local storage; refresh tokens rotate and are stored hashed. Every query is claims-scoped, so a sponsor's session simply cannot resolve another sponsor's or organization's data — parameter manipulation isn't a risk to patch later, because there's no path to the data in the first place.

Multi-tenant & compliance-ready

Organization scoping is enforced on every table, not bolted on with a filter in the service layer. Combined with a system-wide, immutable audit trail, the platform is built to support multiple NGOs on shared infrastructure without any risk of cross-tenant data leakage.

SYSTEM LAYOUT

Three surfaces, one ledger underneath.

PUBLIC SITE

5-step sponsorship wizard

A donor can commit to sponsoring a child without creating an account first — reducing the drop-off that comes from forcing signup before someone has decided to give.

ADMIN CONSOLE

18 operational modules

Payments, donations, Zakat, funds, and the audit log all live behind role-gated screens, so NGO staff can run day-to-day operations without ever touching the database directly.

SPONSOR PORTAL

Claims-scoped ledger view

Every sponsor sees a progress view and transaction history scoped to exactly their own contributions — the same claims-based access control that protects the backend also drives what's rendered in the UI.

CHALLENGES

The hard decisions, and why they went that way.

Making "immutable" actually mean immutable

Claims-scoped access without a performance tax

Session security without breaking UX

Shipping a one-person production platform to AWS

DEPLOYMENT

Built on AWS, moved to Heroku when the bill stopped making sense.

The first production deployment was planned and executed end to end on AWS — Spring Boot and Angular containerized with Docker, running on ECS/Fargate so there was no server fleet to patch, with a CI/CD pipeline running the Testcontainers-backed integration suite before anything reached production. Flyway owns every schema change, so migrations are versioned, ordered and reviewable rather than run ad hoc against a live database.

It now runs on Heroku. The AWS setup was the better platform and the wrong economics for a system funded by an NGO — the monthly bill was buying resilience the current load didn't need. Because the domain layer is framework-free and the services were already containerized, moving hosts was a deployment change rather than a rewrite, which is the entire argument for keeping those boundaries clean. I'd make the same call again; I'd just run the cost numbers before picking the more interesting infrastructure rather than after.

Java 17Spring Boot 3.2PostgreSQL 16Angular 19FlywayDockerTestcontainersAWS ECS/FargateHeroku
WHAT'S NEXT

Where the platform goes from here.

Extending Zakat and regulatory reporting to cover additional jurisdictions as JJT's donor base grows.
A sponsor-facing mobile app on top of the existing claims-scoped API, reusing the domain layer as-is.
Additional payment gateway integrations to widen how donors can give.
Exportable audit packages formatted for institutional donor and board review.

Want a system like this built right the first time?