Full Stack Case Study March 2026

Building Win Stacking:
A Full Stack Case Study

How I designed and built a complete full stack web application using React frontend to ASP.NET Core API, SQLite database, Docker containerisation, and live deployment, and why I mand every decision along the way.

⚛️

Frontend

React, TypeScript and Tailwind

⚙️

Backend

ASP.NET Core 10 and C#

🗄️

Database

SQLite and Entity Framework

🚀

Deployed

Vercel, Render and Docker

What is Win Stacking, you ask?

Win Stacking is a daily habit tracker idea that I had a few years ago built around one idea: You log your wins every day, build streaks, and earn medals as your consistency grows. Users can log three types of wins: Simple, Great, and Outstanding which are visualised as a stack of coins on the screen. The longer the streak, the bigger the stack.

The goal wasn't to build the next big SaaS product. It was to demonstrate end-to-end full stack capability across a real, working application, with authentication, a database, business logic, and live deployment. I also wanted to build a project I had some passion for, plus something that I would actually use myself in real life.

The Frontend: React, TypeScript and Tailwind

The frontend is built with React 19, TypeScript, and Tailwind CSS v3, scaffolded with Vite. The choice of this stack reflects how I have worked in professional settings at Buzz Interactive.

The UI is component-driven throughout. Key components include:

All API calls are centralised in a single client.ts file using Axios, with a request interceptor that automatically attaches the JWT token to every authenticated request. Environment variables via Vite's import.meta.env switch the API base URL between local development and production automatically.

The Backend: ASP.NET Core and C#

The API is built with ASP.NET Core 10 and C#. I chose .NET because it's a production-grade, typed backend framework which is the same stack I used in the commercial projects on my CV. It's also a natural pairing with React frontends in many enterprise environments.

The architecture follows a clean separation of concerns:

Authentication uses JWT tokens issued on login. Every protected endpoint uses the [Authorize] attribute, and the user's ID is extracted from the token claims on each request — meaning users can only ever access their own data, by design.

Passwords are hashed with BCrypt before storage and never stored in plain text. CORS is configured to allow only the known frontend origins, both local and production.

Business Logic: Streak & Medal Calculation

The streak logic lives entirely in the backend WinService. It works by pulling all distinct win dates for a user, ordering them descending, and walking backwards from today checking for consecutive days. A streak is considered active if the user logged a win today or yesterday.

Medals are awarded automatically when a streak milestone is reached — 7 days for Bronze, 14 for Silver, 30 for Gold. Each award is stored as a MedalRecord in the database, creating a permanent history. The active display resets if a streak breaks, but the lifetime count and history always remain.

The Database: SQLite + Entity Framework Core

The app uses SQLite as its database, accessed through Entity Framework Core — Microsoft's official ORM for .NET. SQLite was chosen deliberately for this project: it's a single file, zero configuration, and perfectly suited for a portfolio app. For a production SaaS product I'd reach for PostgreSQL, but SQLite is the right tool here.

Entity Framework Core handles schema management through migrations. The initial migration creates three tables: Users, Wins, and MedalRecords. On startup, the app runs db.Database.Migrate() automatically — so the database is always up to date with no manual steps needed after deployment.

// The app auto-migrates on every startup using (var scope = app.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService<AppDbContext>(); db.Database.Migrate(); }

Containerisation: Docker

The backend is containerised using Docker, which was required for deployment to Render (the hosting platform that I chose for the API). Docker solves a fundamental problem: "it works on my machine." By building the app inside a container with a known .NET SDK version, the build and run environment is identical everywhere.

The Dockerfile uses a multi-stage build. A separate build stage with the full .NET SDK, then a slim runtime-only image for the final container. This keeps the deployed image small:

# Stage 1: Build with full SDK FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build WORKDIR /src COPY . . RUN dotnet publish -c Release -o /app/out # Stage 2: Run with lightweight runtime only FROM mcr.microsoft.com/dotnet/aspnet:10.0 WORKDIR /app COPY --from=build /app/out . ENTRYPOINT ["dotnet", "WinStacking.API.dll"]

Deployment: Vercel + Render

The frontend and backend are deployed independently, which is standard practice for decoupled full stack apps:

CORS on the backend is configured to allow requests only from the Vercel domain in production and localhost in development. The frontend switches API endpoints automatically using environment-specific .env files.

Reflections

This project covers the full development lifecycle: architecture decisions, frontend component design, backend API and business logic, database schema and migrations, containerisation, and deployment. Every part of the stack was built intentionally, not just wired together from tutorials.

The things that would come next in a production version: automated tests for the streak and medal logic, a PostgreSQL database for scalability, and a CI/CD pipeline to run tests before every deploy.

Try the live app

Sign up, log some wins, and build your streak.

Open Win Stacking
← Back to Blog