<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ahmed Ali</title>
    <description>The latest articles on DEV Community by Ahmed Ali (@hafizsyedahmedali).</description>
    <link>https://dev.to/hafizsyedahmedali</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1468600%2Ff4efcbfa-f20a-4167-a5f8-cd4d87d3427b.jpeg</url>
      <title>DEV Community: Ahmed Ali</title>
      <link>https://dev.to/hafizsyedahmedali</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hafizsyedahmedali"/>
    <language>en</language>
    <item>
      <title>How I Built a Live Football Platform That Doesn't Fall Apart Under Load</title>
      <dc:creator>Ahmed Ali</dc:creator>
      <pubDate>Sun, 31 May 2026 09:33:11 +0000</pubDate>
      <link>https://dev.to/hafizsyedahmedali/how-i-built-a-live-football-platform-that-doesnt-fall-apart-under-load-439e</link>
      <guid>https://dev.to/hafizsyedahmedali/how-i-built-a-live-football-platform-that-doesnt-fall-apart-under-load-439e</guid>
      <description>&lt;p&gt;&lt;em&gt;A walkthrough of the architecture decisions behind Flacron Gamezone a production full-stack app built with Next.js, Express, PostgreSQL, and Redis.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When a client approached me to build a live football match discovery platform, the requirements sounded straightforward on the surface: show live scores, let users subscribe, handle authentication. But the moment you start thinking about &lt;em&gt;how&lt;/em&gt; those pieces connect in production, straightforward gets complicated fast.&lt;/p&gt;

&lt;p&gt;This is the story of how I designed the backend for &lt;a href="https://flacrongamezone.com" rel="noopener noreferrer"&gt;Flacron Gamezone&lt;/a&gt; — what decisions I made, why I made them, and what broke along the way.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The Problem With "Just Building It"&lt;/li&gt;
&lt;li&gt;The Architecture: Four Distinct Layers&lt;/li&gt;
&lt;li&gt;Why This Matters to a Client&lt;/li&gt;
&lt;li&gt;The Bug That Taught Me Something Real&lt;/li&gt;
&lt;li&gt;The Full Stack at a Glance&lt;/li&gt;
&lt;li&gt;What I'd Do Differently&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Problem With "Just Building It" &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The easiest version of this app is a single Express file: one route handler that queries the database, formats the data, and sends a response. I've seen this pattern in tutorials everywhere. It works for demos. It falls apart in production.&lt;/p&gt;

&lt;p&gt;The problems are predictable: you can't test business logic without hitting the database, a change in one feature quietly breaks another, and the moment a second developer joins the codebase, nobody knows where anything lives.&lt;/p&gt;

&lt;p&gt;I wanted to build something I could actually be proud to show an employer or a client. That meant committing to a proper layered architecture from day one, even on a project this size.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architecture: Four Distinct Layers &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The entire Express backend is organized into four layers. Each layer has one job and talks only to the layer directly below it.&lt;/p&gt;

&lt;p&gt;Route → Controller → Service → Repository&lt;/p&gt;

&lt;p&gt;Here's what each one actually does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Routes&lt;/strong&gt; are just maps. They declare that &lt;code&gt;POST /api/v1/subscriptions&lt;/code&gt; exists, attach the auth middleware, and hand off to the controller. No logic lives here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Controllers&lt;/strong&gt; handle the HTTP boundary. They extract data from &lt;code&gt;req.body&lt;/code&gt; or &lt;code&gt;req.params&lt;/code&gt;, call the appropriate service method, and send back a formatted response. They don't know anything about databases. They don't contain business rules. If a request comes in malformed, the controller catches it and returns a 400. That's the full extent of its responsibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Services&lt;/strong&gt; are where the application logic lives. A &lt;code&gt;SubscriptionService&lt;/code&gt; knows that before creating a subscription, it needs to verify the user doesn't already have one, call Stripe to create a customer, and only then persist the record. It coordinates between multiple repositories if needed. It throws typed errors that the controller can catch and translate into HTTP responses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repositories&lt;/strong&gt; are the only layer that touches the database. A &lt;code&gt;UserRepository&lt;/code&gt; has methods like &lt;code&gt;findById&lt;/code&gt;, &lt;code&gt;findByEmail&lt;/code&gt;, &lt;code&gt;create&lt;/code&gt;. It uses Prisma under the hood. Nothing outside this layer writes a SQL query or touches a Prisma client directly.&lt;/p&gt;

&lt;p&gt;The result is that when the client asked me to change how subscriptions were priced mid-project, I changed two methods in &lt;code&gt;SubscriptionService&lt;/code&gt; and one in &lt;code&gt;SubscriptionRepository&lt;/code&gt;. The routes, controllers, and every other service were completely untouched.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters to a Client &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;If you're a client reading this: layered architecture means your project doesn't become unmaintainable the moment the original developer moves on. Any competent backend developer can read this codebase and understand where to make a change within minutes, not hours.&lt;/p&gt;

&lt;p&gt;If you're an employer reading this: this is the pattern used in production systems at scale. The reason it's taught in enterprise codebases isn't bureaucracy — it's because the alternative is a system where nobody can safely change anything without breaking something else.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bug That Taught Me Something Real &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Midway through development, Prisma started throwing connection timeout errors. Not consistently — just intermittently, and always on the first query after a cold start.&lt;/p&gt;

&lt;p&gt;I spent an embarrassing amount of time checking my connection pool configuration, my &lt;code&gt;.env&lt;/code&gt; file, my database credentials. Everything looked correct.&lt;/p&gt;

&lt;p&gt;The actual problem: on Windows, &lt;code&gt;localhost&lt;/code&gt; resolves to the IPv6 address &lt;code&gt;::1&lt;/code&gt; before it tries IPv4 &lt;code&gt;127.0.0.1&lt;/code&gt;. PostgreSQL, by default, listens on IPv4. Prisma was trying to connect to the IPv6 address, the connection was being refused, and the error message gave me no indication that address resolution was even involved.&lt;/p&gt;

&lt;p&gt;The fix was a single character change in the database URL — replacing &lt;code&gt;localhost&lt;/code&gt; with &lt;code&gt;127.0.0.1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before&lt;br&gt;
DATABASE_URL="postgresql://user:pass@localhost:5432/flacron"&lt;br&gt;
After&lt;br&gt;
DATABASE_URL="postgresql://user:&lt;a href="mailto:pass@127.0.0.1"&gt;pass@127.0.0.1&lt;/a&gt;:5432/flacron"&lt;/p&gt;

&lt;p&gt;I'm documenting this here because I found almost nothing about it when I was searching. If you're on Windows and Prisma is timing out on cold start, try this before you spend three hours reading connection pool documentation.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Full Stack at a Glance &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; Next.js 15 with App Router, deployed on Vercel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Express.js with the four-layer architecture described above&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; PostgreSQL with Prisma ORM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache:&lt;/strong&gt; Redis for live match data (cache-aside pattern, short TTL)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payments:&lt;/strong&gt; Stripe subscriptions with webhook verification&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth:&lt;/strong&gt; JWT access/refresh token pattern with role-based guards&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each piece was chosen deliberately. Redis sits in front of the live match queries specifically because that data is read far more often than it's written, and hitting the database on every poll request doesn't scale. Stripe handles payments because rolling your own payment processing is never the right call. PostgreSQL over MongoDB because this data is relational — users, subscriptions, and matches have hard dependencies between them that a document database makes awkward to enforce.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd Do Differently &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;One thing I underestimated was how much time Stripe webhook handling would take to get right. Verifying the webhook signature, handling idempotency, making the handler resilient to duplicate events — each of those is its own small problem. I'd budget more time for that in future projects.&lt;/p&gt;

&lt;p&gt;I'd also set up structured logging with Pino earlier. Console logs are fine locally. The first time you're debugging a production issue and you have no searchable, structured log trail, you feel it immediately.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Live Project
&lt;/h2&gt;

&lt;p&gt;Flacron Gamezone is live at &lt;a href="https://flacrongamezone.com" rel="noopener noreferrer"&gt;flacrongamezone.com&lt;/a&gt;. The source code for the local development version is on &lt;a href="https://github.com/HafizSyedAhmedAli/Flacron-Gamezone-Local" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're building something and want a developer who thinks about architecture before writing the first line of code, reach me at &lt;a href="https://syedahmedali.com" rel="noopener noreferrer"&gt;syedahmedali.com&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Ahmed Ali is a Full-Stack Developer based in Pakistan, building production-ready web apps and AI-powered SaaS products. Next.js · Node.js · PostgreSQL · Redis · TypeScript.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
