π Backend for Frontend (BFF)
Orchestration layer that aggregates and enriches data from multiple microservices for the frontend.
The Backend-for-Frontend (BFF) is the orchestration layer between the frontend (Vue.js SPA) and the downstream microservices. It serves three key purposes:
Data Aggregation β Merges data from Hotel, Room, and Booking services into composite responses (e.g., hotel details + room list, reservation + hotel + room info)
Service Bridging β Validates cross-service constraints before forwarding (e.g., verifies hotel exists before creating a room in it)
Business Enrichment β Calculates derived fields like total_price = room.price Γ nights and injects user_id from JWT claims
Layer
Technology
Language
Go 1.25
Router
go-chi/chi v5
Auth
JWT verification (RSA-256 public key)
HTTP Clients
Custom typed clients for each downstream service
Container
Docker (multi-stage Alpine build)
app/
βββ cmd/api/ # Application entrypoint
β βββ main.go
βββ internal/
β βββ client/ # HTTP clients for downstream services
β β βββ client.go # Base HTTP client
β β βββ errors.go # Client error types
β β βββ hotel_client.go # Hotel Service client
β β βββ room_client.go # Room Service client
β β βββ reservation_client.go # Legacy reservation client
β β βββ booking_client.go # Booking Service client
β β βββ payment_client.go # Payment Service client
β βββ config/ # YAML config loader with env var expansion
β βββ handler/ # HTTP handlers, routing, JWT middleware
β β βββ handlers.go # Base handler + health checks
β β βββ hotel_handlers.go # Hotel aggregation endpoints
β β βββ room_handlers.go # Room bridge endpoints
β β βββ reservation_handlers.go # Reservation orchestration
β β βββ middleware.go # JWT, CORS, security, rate limit
β β βββ routing.go # Route definitions
β βββ helper/ # Response helpers
β βββ logging/ # Structured slog logger
β βββ models/ # Aggregated domain models
β β βββ models.go # Hotel, Room, Booking, composite types
β βββ service/ # Business logic layer
β βββ service.go # Service interface + mappings
β βββ hotel_service.go # Hotel operations
β βββ room_service.go # Room operations
β βββ reservation_service.go # Reservation orchestration
βββ config.yaml
βββ Dockerfile
βββ go.mod
All endpoints require JWT authentication (except health checks).
Method
Path
Description
GET
/health
Liveness probe
GET
/ready
Readiness probe (checks downstream services)
Hotel Aggregation Endpoints
Method
Path
Type
Description
GET
/hotels
Passthrough
List hotels (forwarded to Hotel Service)
GET
/hotels/{hotelId}
Passthrough
Get hotel details
GET
/hotels/{hotelId}/details
Aggregation
Hotel + all its rooms (merged)
Method
Path
Type
Description
GET
/rooms/{roomId}
Passthrough
Get room details
POST
/hotels/{hotelId}/rooms
Bridge
Verify hotel exists β create room
Reservation Orchestration Endpoints
Method
Path
Type
Description
GET
/reservations
Passthrough
List user's reservations
GET
/reservations/{id}
Passthrough
Get reservation
GET
/reservations/{id}/details
Aggregation
Reservation + hotel + room (merged)
POST
/reservations
Orchestration
Full booking flow (validate β price β pay β book)
flowchart TD
A["Frontend Request"] --> B["JWT Middleware"]
B --> B1{"Token Valid?"}
B1 -->|No| B2["401 Unauthorized"]
B1 -->|Yes| C{"Endpoint Type?"}
C -->|GET /hotels/id/details| D["AGGREGATION"]
D --> D1["Fetch Hotel from Hotel Service"]
D1 --> D2["Fetch Rooms from Room Service"]
D2 --> D3["Map + Merge into HotelWithRooms"]
D3 --> D4["Return composite JSON"]
C -->|POST /hotels/id/rooms| E["BRIDGE"]
E --> E1["Fetch Hotel from Hotel Service"]
E1 --> E2{"Hotel Exists?"}
E2 -->|No| E3["404 Not Found"]
E2 -->|Yes| E4["Forward CreateRoom to Room Service"]
E4 --> E5["Return created Room"]
C -->|POST /reservations| F["ORCHESTRATION"]
F --> F1["Extract user_id from JWT"]
F1 --> F2["Fetch Room from Room Service"]
F2 --> F3{"Room Exists?"}
F3 -->|No| F4["404 Not Found"]
F3 -->|Yes| F5["Calculate: nights Γ room.price"]
F5 --> F6["Process Payment via Payment Service"]
F6 --> F7{"Payment Succeeded?"}
F7 -->|No| F8["Return payment error"]
F7 -->|Yes| F9["Create Booking via Booking Service"]
F9 --> F10["Confirm Booking"]
F10 --> F11["Return Booking JSON"]
C -->|GET /reservations/id/details| G["AGGREGATION"]
G --> G1["Fetch Booking"]
G1 --> G2["Fetch Hotel"]
G2 --> G3["Fetch Room"]
G3 --> G4["Merge into BookingDetails"]
G4 --> G5["Return composite JSON"]
C -->|Passthrough| H["Forward to downstream service"]
H --> H1["Return downstream response"]
Loading
graph LR
subgraph Actors
User["π€ Authenticated User"]
Admin["π Admin"]
Frontend["π₯οΈ Vue.js Frontend"]
end
subgraph "BFF Service"
UC1["View Hotel with Rooms"]
UC2["Create Room (bridged)"]
UC3["Create Reservation (orchestrated)"]
UC4["View Reservation Details"]
UC5["List User Reservations"]
UC6["Browse Hotels"]
end
subgraph "Downstream Services"
Hotel["π¨ Hotel Service"]
Room["ποΈ Room Service"]
Booking["π
Booking Service"]
Payment["π³ Payment Service"]
end
Frontend --> UC1
Frontend --> UC3
Frontend --> UC4
Frontend --> UC5
Frontend --> UC6
Admin --> UC2
UC1 --> Hotel
UC1 --> Room
UC2 --> Hotel
UC2 --> Room
UC3 --> Room
UC3 --> Payment
UC3 --> Booking
UC4 --> Booking
UC4 --> Hotel
UC4 --> Room
Loading
stateDiagram-v2
[*] --> Initializing
Initializing --> Ready : All clients created
Ready --> Processing : Request received
Processing --> Ready : Response sent
Ready --> Degraded : Downstream health check fails
Degraded --> Ready : Downstream recovers
Ready --> ShuttingDown : SIGTERM/SIGINT
Degraded --> ShuttingDown : SIGTERM/SIGINT
ShuttingDown --> [*] : Graceful shutdown (30s)
state Processing {
[*] --> Authenticating
Authenticating --> Routing
Routing --> Aggregating : Composite endpoint
Routing --> Bridging : Bridge endpoint
Routing --> Orchestrating : Orchestration endpoint
Routing --> Forwarding : Passthrough endpoint
Aggregating --> [*]
Bridging --> [*]
Orchestrating --> [*]
Forwarding --> [*]
}
Loading
graph TB
subgraph "cmd/api"
Main["main.go"]
end
subgraph "internal"
subgraph "handler"
BaseHandler["handlers.go"]
HotelH["hotel_handlers.go"]
RoomH["room_handlers.go"]
ResH["reservation_handlers.go"]
MW["middleware.go (JWT, CORS, etc.)"]
Routes["routing.go"]
end
subgraph "service"
SvcIF["service.go (interface + mappings)"]
HotelSvc["hotel_service.go"]
RoomSvc["room_service.go"]
ResSvc["reservation_service.go"]
end
subgraph "client"
BaseClient["client.go"]
HotelClient["hotel_client.go"]
RoomClient["room_client.go"]
BookingClient["booking_client.go"]
ResClient["reservation_client.go"]
PayClient["payment_client.go"]
Errors["errors.go"]
end
subgraph "models"
Models["models.go"]
end
subgraph "config"
Config["config.go"]
end
end
Main --> Config
Main --> Routes
Main --> SvcIF
Main --> HotelClient
Main --> RoomClient
Main --> BookingClient
Main --> PayClient
Routes --> BaseHandler
Routes --> HotelH
Routes --> RoomH
Routes --> ResH
Routes --> MW
HotelH --> SvcIF
RoomH --> SvcIF
ResH --> SvcIF
SvcIF --> HotelSvc
SvcIF --> RoomSvc
SvcIF --> ResSvc
HotelSvc --> HotelClient
HotelSvc --> RoomClient
RoomSvc --> HotelClient
RoomSvc --> RoomClient
ResSvc --> BookingClient
ResSvc --> HotelClient
ResSvc --> RoomClient
ResSvc --> PayClient
HotelClient --> BaseClient
RoomClient --> BaseClient
BookingClient --> BaseClient
PayClient --> BaseClient
Loading
Reservation Orchestration (Detailed)
The POST /reservations endpoint is the most complex flow:
1. Extract user_id from JWT claims
2. Validate CreateBookingRequest
3. Fetch Room from Room Service β get price_per_night
4. Parse start_date, end_date
5. Calculate: nights = (end_date - start_date).days
6. Calculate: total_price = nights Γ price_per_night
7. Process payment via Payment Service
- Send: booking_id (pre-generated), amount, payment_method_id
- On failure β return error (no booking created)
8. Create Booking via Booking Service
- Send: user_id, hotel_id, room_id, dates, total_price, guest info
9. Confirm Booking via Booking Service
- PATCH status β "confirmed"
10. Return confirmed Booking to frontend
server :
port : 8080
downstream_services :
hotel_service_url : " ${HOTEL_SERVICE_URL}"
room_service_url : " ${ROOM_SERVICE_URL}"
booking_service_url : " ${BOOKING_SERVICE_URL}"
reservation_service_url : " ${RESERVATION_SERVICE_URL}"
payment_service_url : " ${PAYMENT_SERVICE_URL}"
timeout : 30s
rate_limit :
enabled : true
requests_per_second : 100
burst : 200
Variable
Description
HOTEL_SERVICE_URL
Hotel Service URL (e.g., http://hotel-service:8080)
ROOM_SERVICE_URL
Room Service URL
BOOKING_SERVICE_URL
Booking Service URL
RESERVATION_SERVICE_URL
Reservation Service URL (same as booking)
PAYMENT_SERVICE_URL
Payment Service URL
Host Path
Container Path
Description
./keys/public.pem
/app/keys/public.pem
JWT verification key
Context
Port
Internal (container)
8080
External (host)
8087