A comprehensive microservices-based parking places booking platform built with Go, featuring distributed tracing, monitoring, event-driven notifications, and a modern React frontend.
- Overview
- Architecture
- Technology Stack
- Microservices
- Prerequisites
- Quick Start
- Configuration
- API Documentation
- Development
- Testing
- Monitoring and Observability
- HTTPS Setup
- Project Structure
- Troubleshooting
- Security Considerations
- Production Deployment
This project implements a parking places booking system using a microservices architecture. The system supports two user roles: drivers who book parking spaces and parking owners who manage their parking facilities. The platform provides REST APIs, gRPC services, a modern React frontend, and a Telegram bot interface.
- Clean layered architecture with Handler, Service, and Repository layers
- Domain-driven design with separate domain models
- Interface-based design for testability
- Standardized error handling across services
- Multi-tenant architecture with role-based access control
- Distributed tracing with Jaeger
- Metrics collection with Prometheus
- Event-driven notifications via Kafka
- OAuth2/OIDC authentication with Keycloak
- REST APIs for external communication and gRPC for inter-service calls
- Full Docker Compose orchestration
- Modern React frontend with multi-language support
- Telegram bot interface for user interaction
- Secure payment processing with promocode system
- Support for multiple parking types: outdoor, covered, underground, and multi-level
The system follows a microservices architecture with a clean layered design:
- Handler Layer: HTTP request/response handling and domain-to-API mapping
- Service Layer: Business logic, validation, and orchestration
- Repository Layer: Data access with interface-based design
- Domain Models: Core business entities separate from API models
Each service follows the same consistent structure:
Handler → Service → Repository → Database
- Language: Go 1.23.1
- API Framework:
- REST: go-swagger (OpenAPI 2.0)
- gRPC: Protocol Buffers v3
- Database: PostgreSQL 16.4
- Authentication: Keycloak (OAuth2/OIDC)
- Message Queue: Apache Kafka 7.3.0 with Zookeeper
- Containerization: Docker and Docker Compose
- Framework: React 18.3 with Vite 5
- Styling: Tailwind CSS 3
- HTTP Client: Axios
- Routing: React Router 6
- Internationalization: i18next
- Distributed Tracing: Jaeger
- Metrics: Prometheus with Grafana dashboards
- Logging: Structured logging across all services
- Database Driver: jackc/pgx/v5
- Keycloak Client: Nerzal/gocloak/v13
- Kafka Client: segmentio/kafka-go
- Telegram Bot: go-telegram-bot-api/v5
- OpenTelemetry: OTEL SDK for tracing
- Configuration: Centralized config management with validation
Responsibility: User authentication and authorization
Features:
- User registration with role assignment (driver/owner)
- Login with JWT token generation
- Password change functionality
- Keycloak integration for identity management
- Token validation for protected endpoints
- Admin user management
API Endpoints:
POST /login- User authenticationPOST /register- New user registrationPOST /change-password- Password modificationGET /user- Get user information (admin only)GET /google/login- Initiate Google OAuth login (redirects to Google)GET /google/callback- Handle Google OAuth callbackGET /metrics- Prometheus metrics
Database: Uses Keycloak's database for user management
Responsibility: Parking place management and information retrieval
Features:
- CRUD operations for parking places
- Search parking by city, name, or type
- Role-based access control (owners manage their parking places)
- Dual API exposure (REST and gRPC)
- gRPC service for internal service-to-service communication
- Hourly rate-based pricing model
- Domain models with validation
API Endpoints:
GET /parking- Search parking places with filtersPOST /parking- Create new parking place (owners only)GET /parking/{parking_id}- Get parking place detailsPUT /parking/{parking_id}- Update parking place (owner only)DELETE /parking/{parking_id}- Delete parking place (owner only)GET /metrics- Prometheus metrics
gRPC Service:
GetParkingPlace(ParkingPlaceRequest)- Retrieve parking place information
Database: parking_db
Schema:
parking_places (id, name, city, address, parking_type, hourly_rate, capacity, owner_id)Responsibility: Booking management and lifecycle
Features:
- Create bookings with date validation
- Automatic payment processing on booking creation
- Booking status management (Waiting, Confirmed, Canceled)
- Retrieve bookings by ID or parking place
- Calculate total cost based on hourly rate and duration
- gRPC client to fetch parking place information
- gRPC client for payment processing
- Role-based access (drivers book, owners manage)
- Automatic refunds on booking cancellation
API Endpoints:
POST /booking- Create new booking (drivers)GET /booking- Get bookings by parking place (owners)GET /booking/{booking_id}- Get booking detailsPUT /booking/{booking_id}- Update booking statusDELETE /booking/{booking_id}- Cancel booking with refundGET /metrics- Prometheus metrics
Database: booking_db
Schema:
bookings (id, date_from, date_to, parking_place_id, full_cost, status, user_id)Responsibility: Financial operations and billing
Features:
- User balance management
- Transaction processing (charge drivers, pay owners)
- Refund processing
- Promocode system:
- Activate promocodes to add balance
- Generate promocodes from user balance (withdrawal)
- Admin creation of custom promocodes
- Transaction history
- Atomic transactions with database locking
- Overflow protection for balance operations
- gRPC service for internal payment processing
API Endpoints:
GET /payment/balance- Get user balanceGET /payment/transactions- Get transaction historyPOST /payment/promocode/activate- Activate a promocodePOST /payment/promocode/generate- Generate promocode from balancePOST /payment/promocode- Create promocode (admin only)GET /payment/promocode/{code}- Get promocode informationGET /metrics- Prometheus metrics
gRPC Service:
ProcessTransaction(TransactionRequest)- Process payment transactionProcessRefund(RefundRequest)- Process refund transaction
Database: payment_db
Schema:
balances (user_id, balance, currency)
transactions (id, user_id, amount, type, status, booking_id, created_at)
promocodes (code, amount, usage_limit, used_count, expires_at, created_by)Responsibility: Asynchronous notification handling
Features:
- Kafka consumer for notification events
- Telegram notification integration
- Graceful error handling and service resilience
- Extensible handler architecture
- Continues processing messages even when individual notifications fail
Kafka Integration:
- Topic: Configurable via environment
- Group ID: notification-service
- Message handlers for different notification types
Responsibility: Conversational interface for the platform
Features:
- Simple command-based interface
- User authentication through the bot
- Balance viewing
- Booking management
- Parking place management
- Role-specific menu options
- Session management per user
Supported Commands:
/start- Initialize bot and display Telegram ID/help- Show available commands/login- Authenticate user (login and password)/balance- View account balance/bookings- View current bookings (drivers)/parkings- View owned parking places (owners)
Responsibility: Modern web interface for the platform
Features:
- React.js with Vite for fast development
- Tailwind CSS for beautiful, responsive UI
- Multi-language support (English/Russian)
- Role-based dashboards (Driver and Owner)
- Real-time data updates
- Mobile-responsive design
- JWT token authentication
- Fully decoupled from backend
- Admin panel with monitoring tools
Driver Features:
- Search parking places with filters
- Create and manage bookings
- View booking history and status
- Balance management
- Promocode activation and generation
Owner Features:
- Create and manage parking places
- View all bookings for owned parkings
- Access admin monitoring tools
Common Features:
- Admin panel with links to Jaeger, Prometheus, and Grafana
- Monitoring and service health dashboards
- Promocode management (admin)
Deployment:
- Standalone Docker container
- Communicates with backend via HTTPS (nginx reverse proxy on host)
- Can be deployed on separate server
- Environment variable:
VITE_API_BASE_URL
Before running the project, ensure you have the following installed:
- Docker: Version 20.10 or higher
- Docker Compose: Version 2.0 or higher
- Make (optional): For using Makefile commands
- Go 1.23.1+ (for local development)
- Protocol Buffers Compiler (for gRPC code generation)
- go-swagger (for API code generation)
- Python 3 (for running integration tests)
-
Clone the repository:
git clone <repository-url> cd parking_net
-
Configure environment variables:
cp .env-example .env
-
Edit
.envfile with your configuration. Important values to set:KEYCLOAK_CLIENT_SECRET: Set a secure random string (e.g., useopenssl rand -hex 32)KEYCLOAK_ADMIN_PASSWORD: Change from default if neededPOSTGRES_PASSWORD: Change from default if neededTELEGRAM_API_KEY: Set your Telegram bot token if you need Telegram notificationsINTERNAL_SERVICE_TOKEN: Set a secure token for inter-service gRPC communication
Security Note: The
.envfile is in.gitignoreand will not be committed to Git. Never commit sensitive credentials. -
Start all services:
docker-compose up -d
This will:
- Start all backend services
- Start the frontend on port 3000
- Automatically run setup service after database and Keycloak are ready
- Configure Keycloak client secret from your
.envfile - Create all required database tables
- Set up Kafka configuration
The setup runs automatically as part of docker-compose. No manual steps needed.
-
Verify setup:
docker-compose logs setup docker-compose ps
All services should be running. The setup service will show as "exited" after successful completion, which is normal.
-
Access the application:
- Frontend: http://localhost:3000
- Backend APIs: http://localhost:8800 (auth), http://localhost:8888 (parking), etc.
- Keycloak Admin: http://localhost:8080
- Jaeger UI: http://localhost:16686
- Prometheus: http://localhost:9090
- Grafana: http://localhost:3000 (if configured)
-
Run backend integration tests:
python3 tests/integration_test.py
Or using Make:
make test -
Run frontend tests:
cd frontend npm install npm test
The frontend can be deployed on a separate server from backend:
Example: Backend on 192.168.1.100, Frontend on 192.168.1.101
-
On backend server (192.168.1.100):
docker-compose up -d
Services are available on their direct ports (8800, 8888, 8880, 8890) For production, configure nginx reverse proxy on host for HTTPS (see HTTPS Setup)
-
On frontend server (192.168.1.101):
cd frontend docker build --build-arg VITE_API_BASE_URL=http://192.168.1.100 -t parking-frontend . docker run -d -p 3000:80 parking-frontend
Frontend will be available at http://192.168.1.101:3000
-
For development:
cd frontend echo "VITE_API_BASE_URL=http://192.168.1.100" > .env npm install npm run dev
The setup is fully automated through a setup service in docker-compose:
- Setup Service: Automatically runs after
dbandkeycloakservices are healthy - No Manual Steps: Everything happens automatically when you run
docker-compose up -d - Works Everywhere: No Makefile needed - works in any environment with Docker
To check setup progress:
docker-compose logs -f setupMake commands are optional convenience wrappers. Everything works with just docker-compose:
make help # Show all available commands
make setup # Initial setup (copy .env-example to .env)
make up # Start all services (setup runs automatically)
make down # Stop all services
make restart # Restart all services
make test # Run integration tests
make logs # Show logs from all services
make ps # Show status of all services
make clean # Stop services and remove volumesNote: Make is optional. You can use docker-compose commands directly - setup runs automatically.
Key environment variables (see .env-example for complete list):
PostgreSQL:
POSTGRES_USER: Database user (default: postgres)POSTGRES_PASSWORD: Database passwordPOSTGRES_PORT: Database port (default: 5432)PARKING_DB_NAME: Parking database name (default: parking_db)BOOKING_DB_NAME: Booking database name (default: booking_db)PAYMENT_DB_NAME: Payment database name (default: payment_db)AUTH_DB_NAME: Auth database name (default: auth_db)TELEGRAM_DB_NAME: Telegram database name (default: telegram_db)
Service Ports:
PARKING_REST_PORT: Parking REST API port (default: 8888)PARKING_GRPC_PORT: Parking gRPC port (default: 50051)BOOKING_REST_PORT: Booking API port (default: 8880)PAYMENT_REST_PORT: Payment API port (default: 8890)PAYMENT_GRPC_PORT: Payment gRPC port (default: 50052)AUTH_REST_PORT: Auth API port (default: 8800)
Keycloak:
KEYCLOAK_PORT: Keycloak port (default: 8080)KEYCLOAK_CLIENT: Client name (default: parking-auth)KEYCLOAK_REALM: Realm name (default: parking-users)KEYCLOAK_CLIENT_SECRET: Client secret (must be set)KEYCLOAK_ADMIN: Admin username (default: admin)KEYCLOAK_ADMIN_PASSWORD: Admin passwordKEYCLOAK_FRONTEND_URL: Keycloak frontend URL (for HTTPS)
Kafka:
KAFKA_BROKER: Kafka broker address (default: kafka:9092)KAFKA_TOPIC: Kafka topic nameKAFKA_GROUP_ID: Consumer group ID
Telegram:
TELEGRAM_API_KEY: Telegram bot token
Internal Service Authentication:
INTERNAL_SERVICE_TOKEN: Token for inter-service gRPC communication
Google OAuth Configuration (optional):
GOOGLE_OAUTH_CLIENT_ID: Google OAuth application client IDGOOGLE_OAUTH_CLIENT_SECRET: Google OAuth application client secretGOOGLE_OAUTH_REDIRECT_URI: OAuth callback URL (default: http://localhost:8800/auth/google/callback)FRONTEND_URL: Frontend URL for OAuth redirect (default: http://localhost:3000)AUTH_SERVICE_URL: Auth service URL (default: http://auth:8800)
Domain Configuration (for HTTPS setup):
FRONTEND_DOMAIN: Frontend domain (e.g., parking-net.space)BACKEND_DOMAIN: Backend domain (e.g., backend.parking-net.space)JAEGER_SUBDOMAIN: Jaeger subdomainGRAFANA_SUBDOMAIN: Grafana subdomainKEYCLOAK_SUBDOMAIN: Keycloak subdomainPROMETHEUS_SUBDOMAIN: Prometheus subdomainFRONTEND_IP: Frontend server IPBACKEND_IP: Backend server IP
The system automatically creates five separate databases on startup:
parking_db- Parking service databooking_db- Booking service datapayment_db- Payment service dataauth_db- Keycloak authentication datatelegram_db- Telegram bot user data
Database schemas are initialized via SQL scripts in scripts/init_sql/:
init_parking.sql- Parking places tableinit_booking.sql- Bookings tableinit_payment.sql- Balances, transactions, and promocodes tablesinit_telegram.sql- Telegram bot user data
Keycloak is pre-configured with a realm export (keycloak/config/realm-export.json). The configuration includes:
- Realm:
parking-users - Client:
parking-auth - Roles:
driver,owner,admin
The setup service automatically configures the client secret from your .env file.
The system supports Google OAuth as an alternative authentication method. To enable it:
-
Register a Google OAuth Application:
- Go to https://console.cloud.google.com/apis/credentials
- Create a new OAuth 2.0 Client ID
- Set the authorized redirect URI to:
https://keycloak.backend.parking-net.space/realms/parking-users/broker/google/endpoint(or your Keycloak broker endpoint) - Copy the Client ID and Client Secret
-
Configure Environment Variables: Add to your
.envfile:GOOGLE_OAUTH_CLIENT_ID=your-google-client-id GOOGLE_OAUTH_CLIENT_SECRET=your-google-client-secret GOOGLE_OAUTH_REDIRECT_URI=http://localhost:8800/auth/google/callback FRONTEND_URL=http://localhost:3000
-
Automatic Setup: The setup script automatically configures Google as an Identity Provider in Keycloak when the credentials are provided. If credentials are not set, Google OAuth is skipped.
-
Usage: Users can click "Login with Google" on the login page. The OAuth flow will:
- Redirect to Google for authentication
- Return to Keycloak with an authorization code
- Exchange the code for a token via Keycloak
- Redirect to the frontend with the token
Note: After adding Google OAuth endpoints to Swagger, you need to:
-
Generate API Code:
cd auth make swagger_generate -
Register Handlers: After code generation, add these lines in
auth/internal/restapi/configure_parkings_auth.goin theconfigureAPIfunction:api.GetAuthGoogleLoginHandler = operations.GetAuthGoogleLoginHandlerFunc(authHandler.GoogleLoginHandler) api.GetAuthGoogleCallbackHandler = operations.GetAuthGoogleCallbackHandlerFunc(authHandler.GoogleCallbackHandler)
-
Restart Services:
docker-compose restart auth-svc
- External → Services: REST APIs (Swagger/OpenAPI)
- Booking → Parking: gRPC (for parking place information retrieval)
- Booking → Payment: gRPC (for payment processing)
- Services → Keycloak: REST API for token validation
- Services → Jaeger: OTLP for trace export
- Services → Kafka: For event publishing/consumption
- Inter-service gRPC: Authenticated with
INTERNAL_SERVICE_TOKEN
API specifications are defined using OpenAPI 2.0 (Swagger):
- Auth API Specification:
auth/api/swagger/auth.yaml - Parking API Specification:
parking/api/swagger/parking.yaml - Booking API Specification:
booking/api/swagger/booking.yaml - Payment API Specification:
payment/api/swagger/payment.yaml
Most endpoints require authentication. Include the token in the api_key header:
curl -H "api_key: YOUR_JWT_TOKEN" http://localhost:8888/parkingcurl -X POST http://localhost:8800/register \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"login": "testuser",
"password": "Password123",
"role": "driver",
"telegram_id": 123456789
}'curl -X POST http://localhost:8800/login \
-H "Content-Type: application/json" \
-d '{
"login": "testuser",
"password": "Password123"
}'curl -X POST http://localhost:8888/parking \
-H "Content-Type: application/json" \
-H "api_key: YOUR_TOKEN" \
-d '{
"name": "Central Parking",
"city": "Moscow",
"address": "Red Square 1",
"parking_type": "underground",
"hourly_rate": 150,
"capacity": 200
}'curl "http://localhost:8888/parking?city=Moscow&parking_type=underground"curl -X POST http://localhost:8880/booking \
-H "Content-Type: application/json" \
-H "api_key: YOUR_TOKEN" \
-d '{
"parking_place_id": 1,
"date_from": "2024-12-01T10:00:00Z",
"date_to": "2024-12-05T18:00:00Z"
}'curl -X POST http://localhost:8890/payment/promocode/activate \
-H "Content-Type: application/json" \
-H "api_key: YOUR_TOKEN" \
-d '{
"code": "PROMO123"
}'curl -H "api_key: YOUR_TOKEN" http://localhost:8890/payment/balanceThe project uses code generation from OpenAPI specs and Protocol Buffers:
make codegenmake swagger_generate
# Or manually:
./scripts/generate_from_swagger.shThis generates:
- Server boilerplate
- Request/response models
- Validation logic
- API handlers structure
make grpc_generateThis generates Go code from .proto files for:
- Parking service gRPC server
- Payment service gRPC server
- Booking service gRPC clients
-
Install dependencies:
# For each service cd parking && go mod download cd ../booking && go mod download cd ../auth && go mod download cd ../payment && go mod download
-
Run services locally:
# Set environment variables source .env # Run a service cd parking && go run cmd/main.go
-
Run with hot reload (using air or similar):
air -c .air.toml
The project includes comprehensive integration tests that verify the complete system flow:
python3 tests/integration_test.pyOr using Make:
make testThe integration tests cover:
- User registration and authentication
- Parking CRUD operations
- Parking search and filtering
- Booking creation and management
- Payment processing and promocodes
- Authorization checks
- Error handling (404, 403, 401)
Run unit tests for a specific service:
# Test a specific service
cd parking && go test ./...
# Test with coverage
go test -cover ./...
# Test with race detection
go test -race ./...Run frontend unit tests:
cd frontend
npm install
npm testRun frontend E2E tests:
cd frontend
npm run test:e2eUse tools like hey, wrk, or k6:
# Example with hey
hey -n 10000 -c 100 http://localhost:8888/parkingAll services expose metrics at /metrics endpoint:
- Request count: HTTP request totals
- Request duration: Latency histograms
- Error count: Failed request totals
- Custom business metrics: Service-specific metrics
Access Prometheus: http://localhost:9090
Example Queries:
# Request rate for parking service
rate(http_requests_total{job="parking"}[5m])
# 95th percentile latency
histogram_quantile(0.95, http_request_duration_seconds_bucket)
# Error rate
rate(http_requests_errors_total[5m])
All services are instrumented with OpenTelemetry.
Access Jaeger UI: http://localhost:16686
Trace Features:
- Request flow across services
- Service dependencies
- Latency breakdown
- Error tracking
Pre-configured Grafana dashboards are available for:
- Service overview
- Individual service metrics (auth, parking, booking)
- System health monitoring
Access Grafana: http://localhost:3000 (default credentials: admin/admin)
Services use structured logging with contextual information:
- Request ID
- User ID
- Service name
- Trace ID (for correlation with Jaeger)
View logs:
docker compose logs -f <service-name>This guide covers setting up HTTPS for both frontend and backend services using Let's Encrypt SSL certificates.
- Domain names pointing to your server IPs
- Docker and Docker Compose installed
- Ports 80 and 443 open on your servers
- Root or sudo access to the servers
Before setup, configure domains in .env:
# Domain Configuration
FRONTEND_DOMAIN=parking-net.space
BACKEND_DOMAIN=backend.parking-net.space
JAEGER_SUBDOMAIN=jaeger.backend.parking-net.space
GRAFANA_SUBDOMAIN=grafana.backend.parking-net.space
KEYCLOAK_SUBDOMAIN=keycloak.backend.parking-net.space
PROMETHEUS_SUBDOMAIN=prometheus.backend.parking-net.space
KEYCLOAK_FRONTEND_URL=https://keycloak.backend.parking-net.space
# Server IPs (for DNS configuration)
FRONTEND_IP=158.160.159.53
BACKEND_IP=158.160.131.173Note: Replace these values with your actual domains and IPs.
-
Install Nginx and Certbot:
sudo apt update sudo apt install nginx certbot python3-certbot-nginx -y
-
Configure Nginx:
sudo cp frontend/nginx-host.conf.example /etc/nginx/sites-available/parking-frontend sudo ln -s /etc/nginx/sites-available/parking-frontend /etc/nginx/sites-enabled/ sudo mkdir -p /var/www/certbot
Update
server_namein config to match yourFRONTEND_DOMAIN. -
Test and Reload Nginx:
sudo nginx -t sudo systemctl reload nginx
-
Get SSL Certificate:
sudo certbot --nginx -d ${FRONTEND_DOMAIN} -d www.${FRONTEND_DOMAIN}
Follow prompts and choose to redirect HTTP to HTTPS.
-
Enable Auto-Renewal:
sudo systemctl enable certbot.timer sudo systemctl start certbot.timer
-
Set Up DNS:
Add A records:
${BACKEND_DOMAIN}→${BACKEND_IP}${JAEGER_SUBDOMAIN}→${BACKEND_IP}${GRAFANA_SUBDOMAIN}→${BACKEND_IP}${KEYCLOAK_SUBDOMAIN}→${BACKEND_IP}${PROMETHEUS_SUBDOMAIN}→${BACKEND_IP}
-
Configure Nginx for Backend:
sudo cp nginx/nginx-backend-https.conf.example /etc/nginx/sites-available/parking-backend sudo ln -s /etc/nginx/sites-available/parking-backend /etc/nginx/sites-enabled/
Update config file:
- Replace placeholders with actual values from
.env - Update proxy_pass ports to match your service ports
- Ensure the Keycloak subdomain server block is included (it should proxy to
localhost:${KEYCLOAK_PORT})
- Replace placeholders with actual values from
-
Test and Reload Nginx:
sudo nginx -t sudo systemctl reload nginx
-
Get SSL Certificates:
sudo certbot --nginx -d ${BACKEND_DOMAIN} -d ${JAEGER_SUBDOMAIN} -d ${GRAFANA_SUBDOMAIN} -d ${KEYCLOAK_SUBDOMAIN} -d ${PROMETHEUS_SUBDOMAIN}
-
Update Frontend Configuration:
Frontend automatically uses HTTPS URLs in production. Verify in
.env:VITE_API_BASE_URL=https://${BACKEND_DOMAIN} VITE_BASE_HOST=${BACKEND_DOMAIN}
-
Rebuild Frontend:
cd frontend docker-compose build docker-compose up -d -
Update Keycloak Configuration:
Set in
.env:KEYCLOAK_FRONTEND_URL=https://${KEYCLOAK_SUBDOMAIN}Then restart keycloak:
docker-compose restart keycloak
Frontend:
curl -I https://${FRONTEND_DOMAIN}Backend:
curl -I https://${BACKEND_DOMAIN}/auth/metrics
curl -I https://${JAEGER_SUBDOMAIN}
curl -I https://${GRAFANA_SUBDOMAIN}
curl -I https://${KEYCLOAK_SUBDOMAIN}
curl -I https://${PROMETHEUS_SUBDOMAIN}All should return 200 OK.
502 Bad Gateway:
- Check services are running:
docker-compose ps - Check ports match in nginx config
- Check nginx error log:
sudo tail -f /var/log/nginx/error.log
Certificate Errors:
- Wait 5 minutes after DNS changes
- Check DNS:
dig ${BACKEND_DOMAIN} - Verify certificates:
sudo certbot certificates
Mixed Content Errors:
- Clear browser cache
- Verify
API_BASE_URLuses HTTPS - Check CSP headers allow backend domain
Auto-Renewal: Certificates expire every 90 days. Auto-renewal is enabled by default:
sudo systemctl status certbot.timerTo manually renew:
sudo certbot renewparking_net/
├── api/proto/ # Protocol Buffer definitions
├── auth/ # Auth microservice
│ ├── cmd/ # Service entry point
│ ├── internal/
│ │ ├── impl/ # Business logic implementations
│ │ ├── models/ # API models (generated)
│ │ └── restapi/ # Generated API code
│ └── api/swagger/ # OpenAPI specification
├── frontend/ # React.js web interface
│ ├── src/
│ │ ├── components/ # Reusable React components
│ │ ├── context/ # React context providers
│ │ ├── pages/ # Page components
│ │ ├── services/ # API service layer
│ │ └── config/ # Configuration files
│ ├── public/ # Static assets
│ ├── Dockerfile # Frontend Docker config
│ └── package.json # NPM dependencies
├── booking/ # Booking microservice
│ ├── cmd/
│ ├── internal/
│ │ ├── repository/ # Data access layer
│ │ ├── database_service/ # Database operations
│ │ ├── grpc/ # gRPC client & generated code
│ │ ├── models/ # API models (generated)
│ │ └── restapi/ # Generated API code
│ └── api/swagger/
├── parking/ # Parking microservice
│ ├── cmd/
│ │ ├── grpc/ # gRPC server startup
│ │ └── rest/ # REST server startup
│ ├── internal/
│ │ ├── repository/ # Data access layer
│ │ ├── service/ # Business logic layer
│ │ ├── handlers/ # HTTP handlers
│ │ ├── di/ # Dependency injection container
│ │ ├── grpc/ # gRPC server & generated code
│ │ ├── models/ # API models (generated)
│ │ └── restapi/ # Generated API code
│ └── api/swagger/
├── payment/ # Payment microservice
│ ├── cmd/
│ │ ├── grpc/ # gRPC server startup
│ │ └── rest/ # REST server startup
│ ├── internal/
│ │ ├── database_service/ # Database operations
│ │ ├── grpc/ # gRPC server & generated code
│ │ ├── models/ # API models (generated)
│ │ └── restapi/ # Generated API code
│ └── api/swagger/
├── notification/ # Notification microservice
│ ├── cmd/
│ └── internal/
│ ├── handlers/ # Message handlers
│ ├── server/ # Kafka consumer
│ └── services/ # Notification logic
├── telegram_bot/ # Telegram bot interface
│ ├── cmd/
│ ├── api_service/ # API client for services
│ ├── database_service/ # Bot's database operations
│ └── data_representation/ # Message formatting
├── pkg/ # Shared packages
│ ├── domain/ # Domain models
│ ├── errors/ # Standardized error types
│ ├── config/ # Centralized configuration
│ ├── client/ # Keycloak client
│ ├── jaeger/ # Tracing setup
│ ├── middlewares/ # Prometheus metrics middleware
│ └── notification/ # Kafka notification client
├── keycloak/config/ # Keycloak realm configuration
├── scripts/
│ ├── init_sql/ # Database initialization scripts
│ └── generate_from_swagger.sh
├── tests/ # Integration tests
├── docker-compose.yaml # Service orchestration
├── Makefile # Build automation
└── prometheus.yml # Prometheus configuration
Each service follows a consistent layered architecture:
-
Handler Layer (
internal/handlers/orinternal/restapi/handlers/)- HTTP request/response handling
- Domain ↔ API model mapping
- Error conversion
- Tracing/logging
-
Service Layer (
internal/service/orinternal/database_service/)- Business logic
- Validation
- Authorization checks
- Orchestration
-
Repository Layer (
internal/repository/)- Interface-based data access
- PostgreSQL implementation
- Query building
- Error handling
-
Domain Models (
pkg/domain/)- Core business entities
- Validation logic
- Type-safe enums
- Separate from API models
If services fail to start:
-
Check logs:
docker-compose logs <service-name>
-
Verify
.envfile exists and has all required variables -
Check if ports are already in use:
lsof -i :8800 # Auth service lsof -i :8880 # Booking service lsof -i :8888 # Parking service lsof -i :8890 # Payment service
If setup fails:
-
Check setup logs:
docker-compose logs setup
-
Verify
KEYCLOAK_CLIENT_SECRETis set in.env(not the default value) -
Restart setup service:
docker-compose up -d setup
-
Check if Keycloak is ready:
docker-compose logs keycloak | tail -20
If database tables are missing:
-
Check database logs:
docker-compose logs db
-
Manually run setup again:
docker-compose up -d setup docker-compose logs -f setup
If Keycloak setup fails:
- Wait longer - Keycloak can take 30-60 seconds to fully start
- Check Keycloak logs:
docker-compose logs keycloak
- Verify
KEYCLOAK_CLIENT_SECRETis set in.env - Restart setup:
docker-compose restart setup docker-compose logs -f setup
If you see errors like "Invalid client or Invalid client credentials":
- Check Keycloak is running:
docker-compose ps keycloak - Verify environment variables in
.env:KEYCLOAK_CLIENTKEYCLOAK_CLIENT_SECRETKEYCLOAK_REALMKEYCLOAK_ADMINKEYCLOAK_ADMIN_PASSWORD
- Ensure Keycloak realm is imported and configured
If tests fail with connection errors:
- Check all services are up:
docker-compose ps - Check service logs:
docker-compose logs <service-name> - Verify ports are not blocked
If registration/login fails:
- Check database is healthy:
docker-compose ps db - Verify databases are initialized
- Check database logs:
docker-compose logs db
If payment processing fails:
- Check payment service logs:
docker-compose logs payment - Verify
INTERNAL_SERVICE_TOKENis set in.env - Check payment database:
docker-compose exec db psql -U postgres -d payment_db -c "SELECT * FROM balances LIMIT 5;" - Verify gRPC connection between booking and payment services
# Start all services (setup runs automatically)
docker-compose up -d
# Stop all services
docker-compose down
# Restart all services
docker-compose restart
# View logs
docker-compose logs -f
# or for specific service
docker-compose logs -f <service-name>
# Check service status
docker-compose ps
# Run tests
python3 tests/integration_test.py
# Clean everything (removes volumes)
docker-compose down -v- Authentication: OAuth2/OIDC via Keycloak
- Authorization: Role-based access control (RBAC)
- Token Validation: Every protected endpoint validates JWT tokens
- Database Security: Connection pooling with pgx driver, parameterized queries
- Secret Management: Environment variables (use secret managers in production)
- Network Isolation: Services communicate via Docker network
- Input Validation: All user inputs are validated
- SQL Injection Protection: Parameterized queries throughout
- Balance Overflow Protection: Safe arithmetic operations for financial calculations
- Inter-service Authentication: gRPC calls authenticated with
INTERNAL_SERVICE_TOKEN - Error Sanitization: Generic error messages to prevent information disclosure
Security Best Practices:
- Never commit
.envfile - It's already in.gitignore - Use strong secrets - Generate random strings for production
- Rotate secrets regularly in production
- Use different secrets for development and production environments
- Enable HTTPS in production (see HTTPS Setup)
- Regularly update dependencies
- Monitor security advisories
Before deploying to production:
- Configuration Management: Use proper secret managers (Vault, AWS Secrets Manager)
- Database:
- Set up replication and backups
- Use connection pooling
- Implement migration strategy
- Regular backup schedule
- Monitoring:
- Set up alerting rules in Prometheus
- Configure log aggregation (ELK, Loki)
- Set up uptime monitoring
- Configure Grafana dashboards
- Scaling:
- Configure horizontal pod autoscaling
- Use load balancers
- Implement rate limiting
- Consider service mesh (Istio, Linkerd)
- Security:
- Enable TLS/SSL for all services (see HTTPS Setup)
- Implement API gateway
- Regular security audits
- Use WAF (Web Application Firewall)
- Performance:
- Add caching layer (Redis)
- Optimize database queries
- Implement circuit breakers
- Use CDN for frontend assets
- High Availability:
- Deploy multiple instances of each service
- Use database replication
- Implement health checks
- Set up automatic failover
| Service | Container Name | Internal Port | External Port | Purpose |
|---|---|---|---|---|
| PostgreSQL | db | 5432 | 5432 | Multi-database persistence |
| Parking | parking-svc | 8888, 50051 | 8888, 50051 | Parking place management |
| Booking | booking-svc | 8880 | 8880 | Booking management |
| Payment | payment-svc | 8890, 50052 | 8890, 50052 | Payment processing |
| Auth | auth-svc | 8800 | 8800 | Authentication |
| Notification | notification-svc | - | - | Notification handling |
| Telegram Bot | telegram | - | - | Bot interface |
| Frontend | parking-frontend | 80 | 3000 | React web interface |
| Keycloak | keycloak | 8080 | 8080 | Identity management |
| Kafka | kafka | 9092 | 9092 | Message broker |
| Zookeeper | zookeeper | 2181 | 2181 | Kafka coordination |
| Jaeger | jaeger | 16686, 14268 | 16686, 14268 | Distributed tracing |
| Prometheus | prometheus | 9090 | 9090 | Metrics collection |
| Grafana | grafana | 3000 | 3000 | Metrics visualization |
- Services: Direct access on ports 8800 (auth), 8888 (parking), 8880 (booking), 8890 (payment)
- For HTTPS: Configure nginx reverse proxy on host (see HTTPS Setup)
/auth- Auth API/parking- Parking API/booking- Booking API/payment- Payment API
- Frontend (standalone): http://localhost:3000
- Keycloak Admin: http://localhost:8080
- Jaeger UI: http://localhost:16686
- Prometheus: http://localhost:9090
- Grafana: http://localhost:3000