A microservices platform connecting people with blessings from Thailand's leading shrines
- Description
- Problem Statement
- Target Customers
- Key Use Cases
- Requirements
- Architecture
- Technology Stack
- Project Structure
- Microservices
- Getting Started
- Development Guide
- API Documentation
- Frontend Application
- Deployment
- Testing
- Architecture Decision Records
- Team
- Support
สาย.mu (sai.mu) is a comprehensive digital platform that aggregates blessings and prayers from Thailand's premier shrines and temples. Built with a modern microservices architecture, it enables both locals and tourists to discover spiritual locations, make wishes, share blessing techniques, and connect with Thailand's rich cultural and religious heritage—all from the convenience of a web or mobile interface.
The platform provides:
- 🗺️ Location-based shrine discovery with GPS integration
- 🙏 Digital wish-making system with privacy controls
- 📚 Blessing technique sharing and community knowledge
- ⭐ Ratings and reviews for shrines and temples
- 🔐 Secure authentication with Google OAuth support
- 🌐 Multilingual support (Thai/English)
In Thailand, shrines and temples are important cultural and spiritual centers where people go to pray, make wishes, or donate to good causes. However, several challenges exist:
- Information Fragmentation: Shrine and temple information is scattered across various sources, making it difficult to find accurate details.
- Outdated Data: Many online resources contain outdated information about locations, opening hours, and rituals.
- Accessibility: Both locals and tourists struggle to locate nearby shrines or understand their cultural significance.
- Language Barriers: Limited multilingual resources make it challenging for international visitors.
- Ritual Guidance: Lack of clear instructions on how to properly perform rituals and make wishes.
สาย.mu solves these problems by creating a centralized, up-to-date platform that provides:
- Accurate shrine locations with Google Maps integration
- Historical background and cultural significance
- Step-by-step ritual instructions
- Community-driven content (wishes, techniques, reviews)
- Admin portal for shrine owners to manage their listings
-
Thai Locals 🇹🇭
- Seeking nearby shrines for wish-making
- Wanting to explore new spiritual locations
- Interested in sharing blessing techniques
-
Tourists
✈️ - Domestic and international visitors
- Exploring Thailand's cultural landmarks
- Learning about religious practices
-
Shrine/Temple Administrators 🏛️
- Seeking visibility for their locations
- Managing shrine information and events
- Posting announcements and updates
-
Cultural Researchers 📚
- Studying religious practices in Thailand
- Analyzing wish patterns and trends
- Documenting traditional techniques
Scenario: A user wants to make a wish at "ศาลพระพรหม เอราวัณ" (Erawan Shrine)
Flow:
- User logs in with email/password or Google OAuth
- Searches for "ศาลพระพรหม เอราวัณ" or filters by Bangkok province
- Views dedicated shrine page with:
- Location on Google Maps
- Historical background
- Step-by-step wish-making instructions
- Photos and media
- Recent public wishes from other users
- Clicks "Make a Wish" button
- Writes their wish with optional category (love, career, wealth, health)
- Chooses visibility: Public (shared with community) or Private (personal)
- Submits and tracks wish in "My Spiritual Journey"
Scenario: A tourist in Bangkok wants to find nearby temples
Flow:
- User allows location access in browser
- Platform displays shrines within 10 km radius
- User can adjust search radius (5km, 10km, 20km, 50km)
- Results sorted by:
- Distance (nearest first)
- Popularity (most visited)
- Rating (highest rated)
- Category (love, career, wealth, health)
- User selects a shrine and views details
- Can get directions via Google Maps integration
Scenario: A shrine administrator wants to update their shrine's information
Flow:
- Administrator registers/logs in with shrine owner credentials
- Claims or creates shrine listing
- Edits shrine details:
- Name, description, location
- Upload photos and media
- Add historical background
- Update opening hours
- Add blessing techniques
- Posts announcements about upcoming festivals or events
- Views analytics (visitor count, ratings, wish statistics)
Scenario: A user wants personalized shrine recommendations
Flow:
- User navigates to "Discover & Explore" section
- Enters wish text: "I want to find true love"
- System categorizes wish automatically (category: love)
- Provides recommendations based on:
- Wish category matching
- User's location
- Shrine ratings and popularity
- Distance from user
- Displays ranked list with match scores
- User can visit recommended shrine page
- ✅ User registration and authentication (email/password)
- ✅ OAuth integration (Google)
- ✅ Role-based access control (User, Admin, Shrine Owner)
- ✅ JWT token-based authentication
- ✅ Profile management
- ✅ Create, read, update, delete (CRUD) shrine listings
- ✅ Location mapping with Google Maps API
- ✅ Photo and media upload
- ✅ Historical background and descriptions
- ✅ Admin portal for shrine owners
- ✅ Search by name, province, category
- ✅ Filter by shrine type and category
- ✅ Location-based discovery (GPS integration)
- ✅ Sort by popularity, rating, distance
- ✅ AI-powered recommendations
- ✅ Create wishes linked to shrines
- ✅ Public/private visibility toggle
- ✅ Wish categorization (love, career, wealth, health, general)
- ✅ View wish history
- ✅ Delete wishes
- ✅ Wish wall display on shrine pages
- ✅ Share blessing techniques
- ✅ Add required items/ingredients
- ✅ Link techniques to specific shrines
- ✅ Community-driven content
- ✅ Rate shrines (1-5 stars)
- ✅ Write text reviews
- ✅ Anonymous rating option
- ✅ One rating per user per shrine (upsert)
- ✅ Average rating calculation
- ⚡ Search results must load in <2 seconds
- ⚡ API response time <500ms for 95th percentile
- ⚡ Support concurrent users: 1,000+
- ⚡ Page load time <3 seconds
- 📈 Handle 100,000+ registered users
- 📈 Horizontal scaling with Kubernetes
- 📈 Database sharding support
- 📈 CDN for static assets
- 🟢 99.5% uptime SLA
- 🟢 Health checks for all services
- 🟢 Auto-restart on failure
- 🟢 Load balancing with multiple replicas
- 🔒 SSL/TLS encryption (HTTPS)
- 🔒 Password hashing (bcrypt)
- 🔒 JWT token expiration
- 🔒 OAuth 2.0 for third-party authentication
- 🔒 Input validation and sanitization
- 🔒 SQL injection prevention (ORM)
The platform follows a microservices architecture pattern with:
- API Gateway: Single entry point for all client requests (REST API)
- gRPC Microservices: Internal service-to-service communication
- Multiple Databases: PostgreSQL and MongoDB for different data types
- Message Queue: RabbitMQ for asynchronous communication
- Shared Libraries: Common utilities, guards, and interfaces
┌─────────────────────────────────────────────────────────────┐
│ Clients │
│ (Web Browser, Mobile Apps) │
└────────────────────────┬────────────────────────────────────┘
│ HTTPS/REST
▼
┌─────────────────────────────────────────────────────────────┐
│ API Gateway │
│ (Port 3000) │
│ - REST API Endpoints │
│ - JWT Authentication │
│ - Request Validation │
│ - Response Transformation │
└─────┬────────┬────────┬────────┬────────┬────────┬──────────┘
│ │ │ │ │ │
│ gRPC │ gRPC │ gRPC │ gRPC │ gRPC │ gRPC
▼ ▼ ▼ ▼ ▼ ▼
┌──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐
│ User │ Shrine │ Wishing │Technique │Discovery │ Rating │
│ Service │ Service │ Service │ Service │ Service │ Service │
│(Port │(Port │(Port │(Port │(Port │(Port │
│ 5005) │ 5001) │ 5004) │ 5002) │ 5003) │ 5006) │
└────┬─────┴────┬─────┴────┬─────┴────┬─────┴────┬─────┴────┬─────┘
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
┌──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐
│ User DB │Shrine DB │Wishing DB│Technique │Discovery │ Rating │
│(Postgres)│(Postgres)│(Postgres)│ (Mongo) │ (Mongo) │(Postgres)│
└──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘
│
│ Message Queue
▼
┌─────────────────┐
│ RabbitMQ │
│ (Port 5672) │
└─────────────────┘
The project uses NestJS CLI in monorepo mode to manage multiple applications and shared libraries efficiently. Configuration in nest-cli.json enables:
- Centralized build management
- Shared library imports with
@app/shared - Independent service compilation
- Hot reload for development
| Technology | Purpose | Version |
|---|---|---|
| Node.js | Runtime environment | 20.x |
| NestJS | Backend framework | 11.x |
| TypeScript | Programming language | 5.x |
| gRPC | Inter-service communication | Latest |
| Protocol Buffers | API contract definition | proto3 |
| RabbitMQ | Message queue | 3.x |
| Database | Services Using It | Purpose |
|---|---|---|
| PostgreSQL 15 | User, Shrine, Wishing, Rating | Structured relational data |
| MongoDB 6 | Technique, Discovery | Semi-structured documents |
| Technology | Purpose | Version |
|---|---|---|
| React | UI library | 19.x |
| TypeScript | Type safety | 5.x |
| Vite | Build tool | 7.x |
| Tailwind CSS | Styling | 4.x |
| React Router | Client-side routing | 7.x |
| Axios | HTTP client | Latest |
- JWT - Token-based authentication
- Passport.js - Authentication middleware
- Google OAuth 2.0 - Social login
- bcrypt - Password hashing
| Tool | Purpose |
|---|---|
| Docker | Containerization |
| Docker Compose | Multi-container orchestration |
| Kubernetes | Container orchestration (production) |
| pnpm | Package manager |
| k6 | Load testing |
| Grafana | Monitoring dashboards |
| InfluxDB | Metrics storage |
| pgAdmin | Database administration |
สาย.mu/
├── apps/ # Microservice Applications
│ ├── api-gateway/ # REST API Gateway (Port 3000)
│ │ └── src/
│ │ ├── auth/ # Authentication endpoints
│ │ ├── user/ # User endpoints
│ │ ├── shrine/ # Shrine endpoints
│ │ ├── wishing/ # Wishing endpoints
│ │ ├── technique/ # Technique endpoints
│ │ ├── rating/ # Rating endpoints
│ │ └── discovery/ # Discovery endpoints
│ │
│ ├── user-service/ # gRPC Service (Port 5005)
│ ├── shrine-service/ # gRPC Service (Port 5001)
│ ├── wishing-service/ # gRPC Service (Port 5004)
│ ├── technique-service/ # gRPC Service (Port 5002)
│ ├── shrine-discovery-service/ # gRPC Service (Port 5003)
│ ├── rating-service/ # gRPC Service (Port 5006)
│ └── location-service/ # gRPC Service (Port 5007)
│
├── frontend/ # React + Vite Frontend
│ ├── src/
│ │ ├── components/ # Reusable UI components
│ │ ├── pages/ # Page components
│ │ │ ├── Dashboard.tsx
│ │ │ ├── ShrinesHub.tsx
│ │ │ ├── MyJourney.tsx
│ │ │ └── Discover.tsx
│ │ ├── contexts/ # React Context (Auth)
│ │ ├── services/ # API clients
│ │ └── types/ # TypeScript types
│ ├── Dockerfile
│ └── nginx.conf # Production server config
│
├── libs/ # Shared Libraries
│ └── shared/
│ └── src/
│ ├── auth/ # Authentication
│ │ ├── jwt-auth.guard.ts
│ │ ├── jwt.strategy.ts
│ │ ├── google.strategy.ts
│ │ ├── roles.guard.ts
│ │ └── roles.decorator.ts
│ │
│ ├── database/ # Database utilities
│ │ └── base.entity.ts # Base entity with UUID
│ │
│ ├── interfaces/ # Generated gRPC interfaces
│ │ ├── user.ts
│ │ ├── shrine.ts
│ │ ├── wishing.ts
│ │ ├── technique.ts
│ │ ├── shrine-discovery.ts
│ │ ├── rating.ts
│ │ └── location.ts
│ │
│ └── utils/ # Utilities
│ ├── haversine.util.ts # Distance calculation
│ └── validators/
│
├── proto/ # Protocol Buffer Definitions
│ ├── user.proto
│ ├── shrine.proto
│ ├── wishing.proto
│ ├── technique.proto
│ ├── shrine-discovery.proto
│ ├── rating.proto
│ └── location.proto
│
├── k8s/ # Kubernetes Manifests
│ ├── namespace.yaml
│ ├── configmap.yaml
│ ├── secrets.yaml
│ ├── *-db.yaml # Database deployments
│ ├── *-service.yaml # Microservice deployments
│ ├── api-gateway.yaml # Gateway + Frontend bundle
│ ├── build.ps1 # Build Docker images
│ ├── deploy.ps1 # Deploy to K8s
│ └── delete.ps1 # Clean up K8s resources
│
├── testing/ # Load Testing
│ ├── docker-compose.yaml # k6 + InfluxDB + Grafana
│ ├── scripts/
│ │ ├── k6-load-test.js
│ │ └── k6-stress-test.js
│ └── provisioning/ # Grafana dashboards
│
├── tools/ # Development Tools
│ ├── diagram/ # PlantUML diagrams
│ ├── postman/ # API collections
│ └── scripts/ # Utility scripts
│ ├── seed-shrines.js
│ ├── seed-wishes.js
│ ├── reset-local.ps1
│ └── reset-docker.ps1
│
├── docker-compose.yml # Production Docker Compose
├── docker-compose.override.yml # Development overrides
├── nest-cli.json # NestJS CLI Configuration
├── package.json # Root dependencies & scripts
├── pnpm-workspace.yaml # pnpm workspace config
├── tsconfig.json # TypeScript configuration
├── .env # Environment variables
├── .nvmrc # Node.js version (v20)
├── DEVELOPMENT.md # Development guide
├── TESTING_GUIDE.md # Testing guide
├── DEPLOYMENT_SUMMARY.md # Deployment guide
└── README.md # This file
Role: Single entry point for all client requests
Features:
- REST API endpoints for all services
- JWT authentication middleware
- Request validation
- Response transformation
- gRPC client connections to backend services
- CORS configuration
- Error handling and logging
Technologies: NestJS, Express, gRPC clients
Endpoints:
/auth/*- Authentication/users/*- User management/shrines/*- Shrine CRUD/wishes/*- Wish management/techniques/*- Technique sharing/ratings/*- Ratings & reviews/discovery/*- Shrine recommendations
Role: User authentication and profile management
Features:
- User registration (email/password)
- Login with JWT token generation
- Google OAuth integration
- Password hashing with bcrypt
- Role-based access control (User, Admin)
- Profile management
Database: PostgreSQL (user_service database)
Entity:
{
id: UUID,
email: string (unique),
password_hash: string,
role: string (default: 'user'),
createdAt: Date,
updatedAt: Date
}Role: Shrine and temple data management
Features:
- CRUD operations for shrines
- Shrine search and filtering
- Location data management
- Admin portal integration
Database: PostgreSQL (shrine_service database)
Entity:
{
id: UUID,
name: string,
description: string,
location: string,
createdAt: Date,
updatedAt: Date
}Role: Digital wish-making system
Features:
- Create wishes linked to shrines
- Public/private visibility control
- Wish categorization (love, career, wealth, health)
- Filter wishes by shrine and user
- Shrine validation via gRPC call
Database: PostgreSQL (wishing_service database)
Entity:
{
id: UUID,
wisherId: string,
shrineId: string,
wisherName?: string,
description: string,
public: boolean (default: true),
category?: string,
createdAt: Date,
updatedAt: Date
}Role: Blessing technique sharing platform
Features:
- Create and share blessing techniques
- Add required items/ingredients
- Link techniques to shrines
- Community-driven content
Database: MongoDB Atlas
Document Schema:
{
_id: ObjectId,
userId: string,
shrineId: string,
title: string,
description: string,
items: string[],
createdAt: Date,
updatedAt: Date
}Role: AI-powered shrine recommendations
Features:
- Automatic wish categorization (love, career, wealth, health, general)
- Location-based shrine search with radius filtering
- Smart scoring algorithm considering:
- Category match
- Distance from user
- Shrine popularity and ratings
- Search by category and province
- Recommendation history tracking
Database: MongoDB Atlas
Algorithms:
- Haversine Formula: Calculate distance between coordinates
- Match Score:
(categoryMatch * 0.4) + (distanceScore * 0.3) + (ratingScore * 0.3)
Role: Shrine ratings and reviews
Features:
- Rate shrines (1-5 stars)
- Write text reviews
- Anonymous rating option
- One rating per user per shrine (upsert behavior)
- Average rating calculation
- Recent ratings retrieval
Database: PostgreSQL (rating_service database)
Entity:
{
id: UUID,
userId: UUID,
shrineId: string (24-char MongoDB ObjectId),
rating: number (1-5),
review?: string,
isAnonymous: boolean (default: false),
createdAt: Date,
updatedAt: Date
}Unique Constraint: (userId, shrineId) - prevents duplicate ratings
Role: Location-based features and Google Maps integration
Features:
- Geocoding and reverse geocoding
- Distance calculation
- Nearby shrine search
- Google Maps API integration
Technologies: Google Maps Services JS
Before you begin, ensure you have the following installed:
- Node.js v20.x (Download or use
.nvmrc) - pnpm v8.15.0+ (
npm install -g pnpm) - Docker Desktop with Kubernetes enabled (Download)
- Git for version control
-
Clone the repository:
git clone https://github.com/Sai-mu/microservice.git cd microservice -
Install dependencies:
pnpm install
-
Set up environment variables:
Copy
.env.exampleto.env(if not exists) and configure required variables. Key variables:# Database Configuration DATABASE_HOST=localhost DATABASE_PORT=5432 DATABASE_USER=postgres DATABASE_PASSWORD=postgres # JWT Secret JWT_SECRET=your-super-secret-jwt-key # Google OAuth (Optional) GOOGLE_CLIENT_ID=your-google-client-id GOOGLE_CLIENT_SECRET=your-google-client-secret GOOGLE_CALLBACK_URL=http://localhost:3000/auth/google/callback # MongoDB Atlas (Optional) TECHNIQUE_DATABASE_URI=mongodb://localhost:27017/technique_service SHRINE_DISCOVERY_DATABASE_URI=mongodb://localhost:27017/discovery_service
-
Generate TypeScript interfaces from Protocol Buffers:
pnpm run proto:generate
The project supports two development approaches:
Best for: Active coding with fast hot-reload
Setup:
# 1. Start only databases in Docker
pnpm run db:setup
# 2. Start all backend services + frontend locally
pnpm run start:allWhat runs where:
- ✅ Databases: Docker containers
shrine-db(PostgreSQL) - Port 5432user-db(PostgreSQL) - Port 5434wishing-db(PostgreSQL) - Port 5433rating-db(PostgreSQL) - Port 5435pgadmin- Port 5050
- ✅ Services: Node.js processes (hot reload enabled)
- API Gateway - Port 3000
- User Service - Port 5005
- Shrine Service - Port 5001
- Wishing Service - Port 5004
- Technique Service - Port 5002
- Shrine Discovery Service - Port 5003
- Rating Service - Port 5006
- ✅ Frontend: Vite dev server - Port 5173
Advantages:
- ⚡ Fast hot reload (changes reflect instantly)
- 🐛 Easy debugging with IDE
- 📝 Direct file editing
- 💻 Native Node.js performance
Best for: Testing production-like environment
Setup:
# Build and start all containers
pnpm run docker:dev
# Production mode
pnpm run docker:prodWhat runs:
- All services in Docker containers
- Databases in Docker containers
- Frontend in Docker container with nginx
- RabbitMQ message broker
Advantages:
- 🔒 Full isolation
- 🎯 Production-like environment
- 🌐 Network configuration testing
- 📦 Complete containerization
Use these reset scripts to clean up and switch:
# Switch to Local Development
pnpm run reset:local
pnpm run start:all
# Switch to Docker Development
pnpm run reset:docker
pnpm run docker:dev# Start individual services (local)
pnpm run start:dev:gateway # API Gateway
pnpm run start:dev:user # User Service
pnpm run start:dev:shrine # Shrine Service
pnpm run start:dev:wishing # Wishing Service
pnpm run start:dev:technique # Technique Service
pnpm run start:dev:discovery # Discovery Service
pnpm run start:dev:rating # Rating Service
pnpm run start:dev:frontend # React Frontend
# Database management
pnpm run db:setup # Start databases only
pnpm run db:seed:shrines # Seed shrine data
pnpm run db:seed:wishes # Seed wish data
pnpm run db:seed:techniques # Seed technique data
# Docker commands
pnpm run docker:build # Build images
pnpm run docker:up # Start containers
pnpm run docker:down # Stop containers
pnpm run docker:logs # View logs
pnpm run docker:restart # Restart containers
# Code generation
pnpm run proto:generate # Generate TS from .proto files# Replace {service_name} with your desired service name
nest generate app {service_name}Then add scripts to package.json:
{
"scripts": {
"start:{service_name}": "nest start {service_name}",
"start:dev:{service_name}": "nest start {service_name} --watch"
}
}# Replace {library_name} with your desired library name
nest generate library {library_name}- Library created under
libs/{library_name} - Import in services using
@app/{library_name}
- Local Development:
http://localhost:3000 - Kubernetes:
http://localhost:30000
Most endpoints require JWT authentication. Include the token in the Authorization header:
Authorization: Bearer <your-jwt-token>POST /auth/login- Email/password loginPOST /auth/register- User registrationGET /auth/google- Google OAuth loginGET /auth/google/callback- OAuth callbackGET /auth/profile- Get current user (JWT required)
POST /users- Create userGET /users- Get all usersGET /users/:id- Get user by IDPUT /users/:id- Update userDELETE /users/:id- Delete user
POST /shrines- Create shrineGET /shrines- Get all shrinesGET /shrines/:id- Get shrine by IDPUT /shrines/:id- Update shrineDELETE /shrines/:id- Delete shrine
POST /wishes- Create wish (JWT required)GET /wishes- Get all public wishesGET /wishes?shrine_id=X- Get wishes by shrineGET /wishes?wisher_id=X- Get wishes by userDELETE /wishes/:id- Delete wish
POST /techniques- Create technique (JWT required)GET /techniques- Get all techniquesGET /techniques/:id- Get technique by IDDELETE /techniques/:id- Delete technique
POST /ratings- Create/Update rating (JWT required)GET /ratings?shrine_id=X- Get ratings by shrineGET /ratings?user_id=X- Get ratings by user
POST /discovery/recommend- Get shrine recommendationsPOST /discovery/nearby- Find nearby shrinesPOST /discovery/category- Search by category
For detailed examples with request/response bodies, see TESTING_GUIDE.md.
The frontend is a modern React application built with Vite and Tailwind CSS.
-
Dashboard (
/)- Overview statistics
- Featured shrines
- Recent public wishes
- Quick navigation
-
Shrines Hub (
/shrines)- Browse all shrines with beautiful cards
- View detailed shrine pages
- Related wishes and techniques
- CRUD operations (admin)
-
My Spiritual Journey (
/my-journey)- My Wishes Tab: Create, view, and manage your wishes
- My Techniques Tab: Share and manage blessing techniques
-
Discover & Explore (
/discover)- AI-powered recommendations
- Find nearby shrines with GPS
- Browse by category
# Development
cd frontend
pnpm install
pnpm run dev
# Production build
pnpm run build
pnpm run previewFrontend runs on http://localhost:5173
For detailed frontend documentation, see frontend/README.md.
# Development mode with hot reload
pnpm run docker:dev
# Production mode
pnpm run docker:prodThe project includes full Kubernetes manifests for production deployment.
Prerequisites:
- Docker Desktop with Kubernetes enabled
kubectlCLI tool
Deployment Steps:
# 1. Build all Docker images
cd k8s
./build.ps1
# 2. Deploy to Kubernetes
./deploy.ps1
# 3. Verify deployment
kubectl get pods -n microservices
kubectl get services -n microservices
# 4. Access services
# API Gateway: http://localhost:30000
# Frontend: http://localhost:30002
# pgAdmin: http://localhost:30080Kubernetes Resources:
- 3 PostgreSQL databases with persistent volumes
- 1 MongoDB instance
- 1 RabbitMQ broker
- 8 Microservices with health checks
- API Gateway with 3 replicas + HPA + PDB
- Frontend with 2 replicas + HPA
- pgAdmin for database management
High Availability Features:
- Horizontal Pod Autoscaling (HPA)
- Pod Disruption Budgets (PDB)
- Readiness and Liveness probes
- Anti-affinity rules
- Resource limits and requests
Cleanup:
./delete.ps1For detailed deployment guide, see k8s/README.md and DEPLOYMENT_SUMMARY.md.
The project includes comprehensive load testing setup with k6, InfluxDB, and Grafana.
Setup:
cd testing
# Start load testing infrastructure
docker-compose up --scale k6-node=5 -d
# View Grafana dashboards
# Open: http://localhost:4000
# Credentials: admin/adminConfiguration:
# Override default settings
$env:VUS = "200" # Virtual users
$env:DURATION = "3m" # Test duration
$env:BASE_URL = "http://localhost:3000"
docker-compose up --scale k6-node=5 -dAvailable Scripts:
scripts/k6-load-test.js- Gradual load testscripts/k6-stress-test.js- Stress test
For detailed testing guide, see testing/README.md and TESTING_GUIDE.md.
Context:
- Need to support both structured and semi-structured data
- Shrine and temple information is structured relational data
- User wishes and techniques have flexible, varying structures
- Require high scalability to support 100,000+ users
Decision: Use PostgreSQL for relational data and MongoDB for document data
Services using PostgreSQL:
- User Service (user accounts, authentication)
- Shrine Service (shrine details, locations)
- Wishing Service (wishes with relationships)
- Rating Service (ratings and reviews)
Services using MongoDB:
- Technique Service (blessing techniques with flexible schemas)
- Shrine Discovery Service (recommendation history)
Consequences:
✅ Pros:
- PostgreSQL provides ACID compliance for critical user and shrine data
- MongoDB's JSONB-like flexibility handles varying wish and technique structures
- Easy scalability with proper indexing
- Best-of-both-worlds approach for different data types
- PostgreSQL excellent for complex queries and relationships
- MongoDB ideal for evolving schemas and rapid development
- Need to manage two different database systems
- Cross-database queries require application-level joins
- Increased operational complexity
- Different backup and recovery strategies
Future Considerations:
- Could use PostgreSQL's JSONB for semi-structured data if MongoDB operational overhead becomes too high
- Consider database sharding strategies for horizontal scaling as user base grows beyond 100K
Context:
- Need scalable cloud infrastructure to support growing user base (100K+ target)
- Require global content delivery for images and static assets
- Must achieve 99.5% uptime SLA
- Need managed database and storage services
- Want to minimize operational overhead
Decision: Use AWS (EC2, RDS, S3, CloudFront) for deployment
AWS Services:
- EC2/ECS/EKS: Application hosting with auto-scaling
- RDS for PostgreSQL: Managed relational database with automated backups
- DocumentDB/Atlas: MongoDB-compatible document database
- S3: Object storage for shrine images and media
- CloudFront: CDN for global content delivery
- Route 53: DNS management
- Application Load Balancer: Traffic distribution
- ElastiCache: Redis for session management and caching
Consequences:
✅ Pros:
- Industry-leading reliability and scalability
- Global data centers ensure low latency worldwide (important for international tourists)
- Managed services reduce operational overhead
- Auto-scaling supports traffic spikes (e.g., major festivals)
- Comprehensive security and compliance features
- CloudFront CDN improves shrine image loading globally
- RDS automated backups and point-in-time recovery
- Extensive monitoring and alerting capabilities (CloudWatch)
- Higher cost compared to self-hosting or smaller cloud providers
- Vendor lock-in considerations
- Complexity in service configuration
- Requires AWS expertise for optimal setup
- Potential for unexpected costs without proper monitoring
Cost Mitigation:
- Use Reserved Instances for steady-state workloads (30-50% savings)
- Leverage auto-scaling to optimize resource usage
- Implement caching strategies (CloudFront, ElastiCache) to reduce compute costs
- Regular cost monitoring and optimization with AWS Cost Explorer
- Use S3 lifecycle policies for image storage optimization
Alternative Considered:
- Google Cloud Platform (GCP): Excellent Kubernetes support, but less familiar to team
- Azure: Strong enterprise features, but higher learning curve
- DigitalOcean: More affordable, but limited global presence for CDN
Context:
- Platform requires SEO-friendly shrine and temple pages
- Need fast initial page load and responsive design for mobile users
- Must scale for future features (wishes, location search, admin updates)
- Strong developer ecosystem and component reusability needed
- Team has React experience
Decision: Use React 19 + Vite (currently) / Next.js (future) with Tailwind CSS
Current Implementation: React 19 + Vite + Tailwind CSS
Future Migration Path: Next.js for:
- Server-Side Rendering (SSR) for shrine detail pages
- Static Site Generation (SSG) for popular shrines
- Improved SEO for shrine pages
- Built-in API routes for BFF (Backend for Frontend) pattern
- Image optimization out of the box
Technologies:
- React 19: Component-based UI library with latest features (concurrent rendering, automatic batching)
- Vite: Ultra-fast build tool and dev server (10x faster than Webpack)
- Tailwind CSS: Utility-first CSS framework for rapid UI development
- TypeScript: Type safety throughout the codebase
- React Router: Client-side routing
Consequences:
✅ Pros:
- Vite provides extremely fast hot module replacement (HMR) - instant feedback during development
- Tailwind CSS ensures consistent, mobile-first responsive design
- Large React ecosystem with abundant libraries and resources
- TypeScript reduces bugs and improves maintainability
- Easy migration path to Next.js for SSR/SSG when needed
- Component reusability across dashboard, shrine pages, wish forms
- Excellent developer experience with fast builds
- Great mobile performance with code splitting
- Current SPA architecture has SEO limitations (mitigated with future Next.js migration)
- Initial bundle size can be larger (mitigated with code splitting and lazy loading)
- Steeper learning curve for developers new to React/TypeScript
- Need to manage client-side state carefully (Context API currently, could add Redux later)
SEO Strategy:
- Current: Meta tags and React Helmet for basic SEO
- Future: Next.js SSR/SSG for shrine detail pages with dynamic Open Graph tags
- Sitemap generation for search engine crawling
- Structured data (JSON-LD) for search engines to understand shrine information
- Prerendering for social media link previews
Performance Optimizations:
- Code splitting with React.lazy and Suspense
- Image lazy loading for shrine galleries
- Virtual scrolling for long lists
- Service worker for offline support (future)
- CDN for static assets
Context:
- Multiple backend services need efficient inter-service communication
- Type safety and contract-first development are priorities
- Need better performance than REST for internal calls
- Want language-agnostic service contracts
- High-frequency internal calls (e.g., wish creation validates shrine via gRPC)
Decision: Use gRPC with Protocol Buffers for inter-service communication
Implementation:
- All backend services expose gRPC endpoints
- API Gateway translates HTTP/REST to gRPC
- Protocol Buffers define service contracts in
.protofiles - TypeScript interfaces generated from
.protofiles - Unary RPC calls for most operations (no streaming yet)
Consequences:
✅ Pros:
- Performance: Binary serialization is 3-10x faster than JSON
- Type Safety: Strongly-typed contracts prevent integration errors
- Contract-First:
.protofiles serve as documentation and API specification - Code Generation: Automatic client/server code generation saves time
- Bi-directional Streaming: Support for real-time features (future use)
- Language Agnostic: Can add services in other languages (Go, Python) later
- HTTP/2: Multiplexing reduces latency for multiple concurrent calls
- Automatic retries and timeouts: Built into gRPC clients
- Not human-readable like JSON (harder to debug without tools)
- Requires code generation step in CI/CD pipeline
- Browser support requires gRPC-Web (added complexity)
- Smaller ecosystem compared to REST (fewer libraries/tools)
- Learning curve for developers unfamiliar with Protocol Buffers
Solution for Web:
- API Gateway handles HTTP→gRPC translation
- Frontend uses standard REST/HTTP (no gRPC-Web complexity)
- Backend services communicate via gRPC
- Best of both worlds: developer-friendly frontend, performant backend
Tools for Debugging:
- grpcurl: Command-line tool for testing gRPC services
- Postman: Supports gRPC calls in recent versions
- Bloom RPC: GUI client for gRPC (like Postman for REST)
Performance Gains:
- Shrine validation in wish creation: 150ms (REST) → 50ms (gRPC)
- Discovery recommendations: 500ms (REST) → 200ms (gRPC)
- Reduced network overhead: ~40% smaller payloads
Context:
- Need secure, scalable authentication mechanism
- Support both email/password and social login
- Stateless authentication preferred for microservices (no session state)
- Must support role-based access control (User, Admin, Shrine Owner)
- Mobile app support in the future
Decision: Use JWT (JSON Web Tokens) + OAuth 2.0 (Google)
Implementation:
- JWT: Stateless authentication tokens with HS256 algorithm
- Passport.js: Authentication middleware (passport-jwt, passport-google-oauth20)
- bcrypt: Password hashing with salt rounds = 10
- Google OAuth 2.0: Social login integration
- Guards: NestJS guards for route protection (@UseGuards(JwtAuthGuard))
- Roles: Custom decorators for role-based access (@Roles('admin'))
Token Structure:
{
"sub": "user-uuid",
"email": "[email protected]",
"role": "user",
"iat": 1234567890,
"exp": 1234567890 // Expires in 1 hour
}Consequences:
✅ Pros:
- Stateless: Tokens scale well across microservices (no session storage required)
- No Server-Side Session Storage: Reduces Redis/Memcached dependency
- OAuth reduces friction: Users can sign up with Google in one click
- Strong security: bcrypt password hashing prevents rainbow table attacks
- Mobile-friendly: Easy to implement in future mobile apps
- Cross-service authentication: All services can validate JWT independently
- Expiration control: Fine-grained control over token lifetime
- Token revocation is challenging: Can't invalidate tokens before expiration (mitigated with short expiration)
- Secrets must be carefully managed: JWT_SECRET exposure compromises entire system
- Token size: Larger than session IDs (~200 bytes vs 32 bytes)
- No built-in refresh mechanism: Need to implement refresh token logic
Security Measures:
- HTTPS/TLS encryption: All traffic encrypted in transit
- Short token expiration: 1 hour (refresh tokens coming soon)
- Refresh token rotation: Planned for v2 (long-lived refresh tokens with rotation)
- Environment variable protection: JWT_SECRET stored securely, never in code
- Rate limiting: Prevent brute force attacks on login endpoint
- Password requirements: Minimum 8 characters, complexity rules
Future Enhancements:
- Refresh tokens for longer sessions without re-authentication
- Token blacklisting for logout (requires Redis)
- Two-factor authentication (2FA) for admin accounts
- OAuth with Facebook, LINE for Thai users
- Passwordless authentication (magic links)
Token Expiration Strategy:
- Access Token: 1 hour (short-lived for security)
- Refresh Token (future): 7 days (stored in httpOnly cookie)
- Remember Me: 30 days (future feature)
Project: สาย.mu - Thailand's Premier Shrine Blessing Platform
Course: Term Project Deliverable - Microservices Architecture
Team Members:
| Name | Student ID | Role | Responsibilities |
|---|---|---|---|
| Chotpisit Adunsehawat | 653 13132 21 | Backend Lead | - Backend architecture design - API Gateway implementation - gRPC service development - Database schema design |
| Thanakrit Wong-asa | 653 20824 21 | Frontend Lead | - React frontend development - UI/UX design - Tailwind CSS styling - API integration |
| Thanaphom Hirunyathon | 653 20927 21 | DevOps Lead | - Docker containerization - Kubernetes deployment - CI/CD pipeline - Load testing setup |
| Nontanun Ausungnoen | 653 21069 21 | Database & Architecture | - Database design (PostgreSQL/MongoDB) - Architecture decision records - Data seeding scripts - Documentation |
This project is licensed for educational purposes as part of a university term project.
Copyright © 2024-2025 สาย.mu Team
All rights reserved. This project and its contents are the intellectual property of the project team and the educational institution. Unauthorized copying, distribution, or use outside the scope of the educational project is prohibited without explicit permission.
- NestJS Community: For the excellent microservices framework and documentation
- React Team: For the powerful and flexible UI library
- gRPC: For efficient inter-service communication
- Thailand's Shrines and Temples: For inspiring this cultural heritage platform
- Our Instructors: For guidance, feedback, and support throughout the project
- Open Source Community: For the amazing tools and libraries that made this possible
For questions, issues, or contributions:
- 📧 Email: [email protected] (educational project)
- 📝 Documentation:
- Development Guide:
DEVELOPMENT.md - Testing Guide:
TESTING_GUIDE.md - Deployment Guide:
DEPLOYMENT_SUMMARY.md - Kubernetes Guide:
k8s/README.md - Frontend Guide:
frontend/README.md
- Development Guide:
- 🐛 Issues: Create an issue on GitHub repository
- 💬 Discussions: Use GitHub Discussions for questions and ideas
- Development Mode Guide - Detailed development setup
- Testing Guide - Comprehensive testing instructions
- Deployment Summary - Kubernetes deployment details
- Kubernetes README - K8s manifests and deployment
- Frontend README - React app documentation
- Testing Infrastructure - k6 load testing setup
Located in tools/diagram/:
component_diagram.puml- System component overview1_discover_nearby_sequence.puml- Discover nearby shrines flow2_share_technique_sequence.puml- Share technique flow3_make_wish_sequence.puml- Make wish flowclass_diagram_shrine_controller.puml- Shrine controller structure
- Postman collections in
tools/postman/ - Test scripts in
tools/scripts/
Made with ❤️ in Thailand 🇹🇭
สาย.mu - Connecting people with blessings from Thailand's sacred spaces