Skip to content

A Blackjack game built with TypeScript, Node.js, WebSockets, and React, featuring clean domain-driven architecture and real-time gameplay.

Notifications You must be signed in to change notification settings

adanSiqueira/blackjack-ts

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

47 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Blackjack-ts

A Blackjack game built entirely with TypeScript, featuring clean architecture, reusable domain logic, real-time gameplay, and a production deployment.

πŸ”— Live Demo: https://blackjack-ts.onrender.com


Project Overview

Blackjack-ts is a web implementation of the Blackjack card game, designed not just as a playable game, but as a software architecture showcase.

The application is split into three main layers:

  • Domain β†’ Pure game logic (cards, deck, players, rules)
  • Backend β†’ API + WebSocket server (Node.js)
  • Frontend β†’ Graphical user interface (React)

The project emphasizes:

  • Clean separation of concerns
  • Domain-driven design principles
  • Type safety across the entire stack
  • Real-time communication using WebSockets
  • A production-ready deployment

The same domain logic originally developed for a CLI version is reused in a web-based, real-time environment without modification.


Architecture Overview


root
β”œβ”€β”€ domain      β†’ Pure game rules and entities
β”œβ”€β”€ backend     β†’ HTTP API + WebSocket server
β”œβ”€β”€ frontend    β†’ React UI (Vite)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚        Frontend         β”‚
β”‚  React + TypeScript     β”‚
β”‚  (Vite)                 β”‚
β”‚                         β”‚
β”‚  - UI Components        β”‚
β”‚  - Custom Hooks         β”‚
β”‚  - WebSocket Client     β”‚
β”‚  - REST Client          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚
   HTTP API β”‚        WebSocket
            β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         Backend         β”‚
β”‚  Node.js + TypeScript   β”‚
β”‚  Express + WS           β”‚
β”‚                         β”‚
β”‚  - Controllers          β”‚
β”‚  - Routes               β”‚
β”‚  - WebSocket Server     β”‚
β”‚  - Game State Store     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚
            β”‚ Calls domain logic
            β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚          Domain         β”‚
β”‚   Pure TypeScript       β”‚
β”‚                         β”‚
β”‚  - Cards / Deck         β”‚
β”‚  - Players / Dealer     β”‚
β”‚  - Game Rules           β”‚
β”‚  - Hand Evaluation      β”‚
β”‚                         β”‚
β”‚  (Framework-agnostic)   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Why this structure?

  • Domain logic is isolated and reusable
  • Backend acts as an orchestrator, not a rule holder
  • Frontend focuses only on UI and interaction
  • Enables future scalability:
    • Multiplayer tables
    • Persistence
    • Authentication
    • Mobile or CLI clients

This mirrors real-world backend/frontend/domain separation used in production systems.


Domain Layer (/domain)

The domain layer contains all Blackjack rules and entities, fully independent from any framework or transport.

Includes:

  • Card and deck modeling
  • Hand evaluation logic
  • Player and dealer behavior
  • Game flow and rule enforcement

Key characteristics:

  • No HTTP
  • No WebSockets
  • No React
  • No infrastructure code

πŸ”§ Backend (/backend)

Built with Node.js + TypeScript.

Responsibilities:

  • Expose game actions via a REST API
  • Manage real-time gameplay via WebSockets
  • Maintain authoritative game state on the server
  • Bridge communication between frontend and domain

Key concepts implemented:

  • Express routing and controllers
  • Typed request/response contracts
  • Centralized in-memory game state
  • WebSocket event lifecycle management

The backend does not contain game rules β€” it delegates all logic to the domain layer.


Frontend (/frontend)

Built with React + TypeScript, bundled with Vite.

Responsibilities:

  • Render the Blackjack table, cards, and game state
  • Handle player interactions (hit, stand, start game, etc.)
  • Synchronize state with the backend via WebSockets
  • Trigger HTTP requests when needed (game initialization, resets)

Key concepts:

  • Functional components
  • Custom hooks (useGame, useWebSocket)
  • Typed state and props
  • Clear separation between UI and networking logic

The UI is intentionally simple for now, with plans for further polish and animations.


Technical Decisions

1️⃣ Full TypeScript Across the Stack

Decision: Use TypeScript in domain, backend, and frontend.

Why:

  • End-to-end type safety
  • Shared mental model across layers
  • Fewer runtime errors
  • Better refactoring and scalability

Trade-off: Slightly higher upfront complexity, but worth it for maintainability.


2️⃣ Domain-First Architecture (Framework-Agnostic Core)

Decision: Extract all game rules into a standalone /domain package.

Why:

  • Business rules are the most valuable part of the system

  • Domain logic should not depend on frameworks

  • Enables reuse across:

    • CLI
    • Backend
    • Tests
    • Future services

Result: The backend becomes an orchestrator, not a rule holder.


3️⃣ Backend as the Authoritative Game State

Decision: Store and manage game state exclusively on the server.

Why:

  • Prevents client-side cheating
  • Enables real multiplayer in the future
  • Simplifies synchronization logic
  • Matches real-world multiplayer game architecture

Alternative considered: Client-side state with validation β€” rejected due to consistency and security concerns.


4️⃣ WebSockets for Real-Time Gameplay

Decision: Use WebSockets instead of polling or HTTP-only updates.

Why:

  • Blackjack is event-driven
  • Immediate feedback improves UX
  • Scales naturally to multiplayer tables
  • Matches real-time system design patterns

Usage:

  • Player actions β†’ server
  • State updates β†’ broadcast to client(s)

5️⃣ REST + WebSockets (Hybrid Communication)

Decision: Use both REST and WebSockets instead of only one.

Why:

  • REST is ideal for:

    • Health checks
    • Game initialization
    • Stateless operations
  • WebSockets are ideal for:

    • Continuous game updates
    • Real-time interaction

This avoids forcing one protocol to do everything.


6️⃣ Explicit HTTP Server Creation

Decision: Create the HTTP server manually instead of relying on app.listen().

const server = http.createServer(app);
initGameSocket(server);

Why:

  • Required for clean WebSocket integration
  • Enables future protocol-level configuration
  • Aligns with production Node.js patterns

7️⃣ Environment-Based Configuration (Vite + Render)

Decision: Use environment variables for API and WebSocket URLs.

Why:

  • Clean separation between local and production environments
  • No hardcoded URLs
  • Avoids common deployment pitfalls

This decision directly solved the production bug encountered during deployment.


8️⃣ Single Deployment, Multiple Concerns

Decision: Deploy frontend and backend under the same public domain on Render.

Why:

  • Simplifies CORS
  • Simplifies WebSocket configuration
  • Reduces operational overhead
  • Ideal for small-to-medium applications

Scales later to: Separate services, Docker, or microservices if needed.


Environment Variables & Configuration

The project uses environment variables to correctly separate local development and production environments.

Frontend (Vite)

Local development (.env, not committed):

VITE_API_URL=http://localhost:3001/api
VITE_WS_URL=ws://localhost:3001

Production (configured on Render):

VITE_API_URL=https://blackjack-ts.onrender.com/api
VITE_WS_URL=https://blackjack-ts.onrender.com

Deployment

The application is deployed on Render as a full-stack service.

  • Single public URL serving both frontend and backend
  • Backend listens on the port provided by Render
  • Frontend build served as static assets
  • WebSockets enabled and working in production
  • Environment variables configured via Render dashboard

πŸ”— Live URL: https://blackjack-ts.onrender.com


β–Ά Running Locally

Prerequisites

  • Node.js (v18+ recommended)
  • npm or yarn

Install dependencies

npm install

Start backend

cd backend
npm run dev

Start frontend

cd frontend
npm run dev

Make sure the frontend .env file is configured correctly for local development.


Technologies Used

Core

  • TypeScript
  • Node.js

Backend

  • Express.js
  • WebSockets
  • REST APIs

Frontend

  • React
  • Vite
  • WebSocket client

About

A Blackjack game built with TypeScript, Node.js, WebSockets, and React, featuring clean domain-driven architecture and real-time gameplay.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published