A scalable, PubSub event-driven ticketing platform built with microservices architecture, leveraging Node.js, TypeScript, and React, orchestrated with Kubernetes, and featuring distributed data consistency through NATS Streaming for real-time event processing across services.
- Project Overview
- Technical Stack
- Architecture
- Core Functionalities
- Microservices Ecosystem
- Development Process
- Kubernetes Deployment
- Challenges Overcome
- Local Development Setup
This project is a robust, scalable ticketing platform built on a microservices-based architecture.
It's a showcase of an advanced implementation of distributed systems concepts, along with modern web development, DevOps practices, and cloud-native technologies.
Key highlights:
- Fully distributed microservices architecture with 5 independent services
- Event-driven design using NATS Streaming for reliable, asynchronous inter-service communication
- Implemented with TypeScript for type-safety and improved developer experience
- Leverages Docker and Kubernetes for containerization and orchestration
- Utilizes Next.js for server-side rendering, improving performance and SEO
- Implements JWT-based authentication with custom middleware for secure, stateless authentication across services
- Integrated Stripe for payment processing
- Backend: Node.js, Express.js, TypeScript
- Frontend: React, Next.js
- Database: MongoDB with Mongoose ODM
- Message Broker: NATS Streaming Server
- Containerization: Docker
- Orchestration: Kubernetes
- Testing: Jest, Supertest
- API Client: Axios
- State Management: Redux with Redux Thunk middleware
- Styling: Bootstrap
- Payment Processing: Stripe
The application follows a microservices architecture, with each service responsible for a specific business domain. This design allows for:
- Independent development and deployment of services
- Improved fault isolation and resilience
- Easier scaling of individual components
Each microservice:
- Is built with Node.js and Express
- Uses its own MongoDB database
- Publishes and subscribes to events via NATS Streaming
- Is containerized using Docker
- Is deployed to a Kubernetes cluster
Inter-service communication is primarily asynchronous, using an event-driven architecture to maintain data consistency across services.
-
User Authentication:
- Sign up, sign in, and sign out functionality
- JWT-based authentication
- Current user middleware for client-side rendering
-
Ticket Management:
- Create, update, and view tickets
- Ticket locking mechanism during purchase process
-
Order Processing:
- Create and cancel orders
- Implement 15-minute reservation period for tickets
-
Expiration Service:
- Manage time-sensitive operations
- Automate order expiration after 15 minutes
-
Payments:
- Process payments using Stripe
- Mark orders as complete upon successful payment
-
Event-Driven Updates:
- Real-time synchronization across services using NATS Streaming
-
Auth Service:
- Handles user registration, login, and logout
- Issues and validates JWT tokens
- Exposes currentUser, signup, signin, and signout routes
-
Tickets Service:
- Manages ticket creation, updating, and retrieval
- Publishes ticket:created and ticket:updated events
- Implements optimistic concurrency control
-
Orders Service:
- Handles order creation and cancellation
- Maintains order status (created, cancelled, awaiting:payment, complete)
- Publishes order:created, order:cancelled events
-
Expiration Service:
- Listens for order:created events
- Schedules and publishes expiration:complete events
- Uses Bull.js for job queueing and scheduling
-
Payments Service:
- Processes payments using Stripe API
- Updates order status upon successful payment
- Publishes payment:created events
-
Client Application:
- Next.js based frontend for user interactions
- Implements server-side rendering for improved SEO and performance
- Uses Redux for state management
Each service is independently deployable, with its own database and API. Inter-service communication is achieved through event publishing and subscription via NATS Streaming.
There are 4 custom data types we use in this project.
-
Test-Driven Development (TDD):
- Comprehensive unit tests for all services
- Integration tests for critical workflows
- Custom test setup for auth and database in each service
-
Code Organization:
- Consistent project structure across all services
- Shared library for common code and events
-
Error Handling:
- Custom error classes (RequestValidationError, DatabaseConnectionError, NotFoundError, BadRequestError)
- Centralized error handling middleware
-
Typescript Configuration:
- Strict type checking enabled
- Consistent tsconfig across services
-
NATS Streaming:
- Custom abstract class for event publishing
- Robust error handling and event acknowledgement
-
Mongoose:
- Custom plugins for optimistic concurrency control
- Type definitions for all models
-
Kubernetes Resources:
- Deployments for each microservice
- ClusterIP services for inter-service communication
- Ingress-NGINX for routing external traffic
-
Config and Secrets Management:
- Kubernetes secrets for sensitive data (JWT_KEY, STRIPE_KEY)
- Environment variables injected into pods
-
Local Development:
- Skaffold for automating Kubernetes deployments
- Hot-reloading configured for all services
-
Digital Ocean Deployment:
- Walkthrough of production deployment on Digital Ocean Kubernetes
-
Data Consistency:
- Implemented event-driven architecture
- Idempotent event handlers to handle duplicate events
- Optimistic concurrency control using version numbers
-
Distributed Transactions:
- Saga pattern for multi-service operations
- Compensating transactions for rollback scenarios
-
Authentication in Microservices:
- Stateless JWT-based auth
- Shared auth middleware via npm package
-
Testing Microservices:
- In-memory MongoDB for unit tests
- Custom test setup for auth and database mocking
-
TypeScript Integration:
- Consistent TypeScript configuration across services
- Type definitions for events and shared libraries
-
Prerequisites:
- Node.js v16.x
- Docker v20.x
- Kubernetes (enabled in Docker Desktop or Minikube)
- Skaffold v1.35.x
-
Environment Setup:
# Clone the repository git clone https://github.com/your-username/ticketing.git cd ticketing # Install dependencies npm install # Create Kubernetes secrets kubectl create secret generic jwt-secret --from-literal=JWT_KEY=your_jwt_key kubectl create secret generic stripe-secret --from-literal=STRIPE_KEY=your_stripe_key # Start the application skaffold dev
-
Accessing the application:
- The application will be available at http://ticketing.dev
- Add
127.0.0.1 ticketing.dev
to your/etc/hosts
file