flight_booking_bff
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| flight_booking_bff [2025/12/31 09:11] – [OpenTelemetry Tracing] pradnya | flight_booking_bff [2026/01/24 05:44] (current) – [Error Handling Strategy] madan | ||
|---|---|---|---|
| Line 15: | Line 15: | ||
| ==== Key Services / Core Components ==== | ==== Key Services / Core Components ==== | ||
| - | ^Service^Responsibility| | + | ^Service^Responsibility^Local Port| |
| - | |**API Gateway / BFF (Node.js)** |Single entry point for UI| | + | |**API Gateway / BFF (Node.js)** |Single entry point for UI|4000| |
| - | |**Auth Service** |Issues & validates tokens| | + | |**Auth Service** |Issues & validates tokens|4001| |
| - | |**Flight Service** |Calls supplier API + Redis cache| | + | |**Flight Service** |Calls supplier API + Redis cache|4002| |
| - | |**Booking Service (MySQL)** |Stores booking data| | + | |**Booking Service (MySQL)** |Stores booking data|4003| |
| - | |**Payment Service** |Payment initiation + webhooks| | + | |**Payment Service** |Payment initiation + webhooks|4004| |
| ==== Observability Stack ==== | ==== Observability Stack ==== | ||
| - | * | + | * **Pino** |
| - | + | * **Pino-Http** | |
| - | **Pino** | + | * **OpenTelemetry** |
| - | * | + | * **Cloud Logging (ELK / CloudWatch / Loki)** |
| - | + | ||
| - | **Pino-Http** | + | |
| - | * | + | |
| - | + | ||
| - | **OpenTelemetry** | + | |
| - | * | + | |
| - | + | ||
| - | **Cloud Logging (ELK / CloudWatch / Loki)** | + | |
| === Token Flow Diagram === | === Token Flow Diagram === | ||
| < | < | ||
| Line 71: | Line 64: | ||
| < | < | ||
| Search Flights → Cache Results | Search Flights → Cache Results | ||
| - | Select Flight → Create | + | Select Flight → Create |
| Initiate Payment → Payment Gateway | Initiate Payment → Payment Gateway | ||
| - | Webhook Received → Confirm | + | Webhook Received → Confirm |
| Persist in MySQL → Send Confirmation | Persist in MySQL → Send Confirmation | ||
| Line 80: | Line 73: | ||
| ===== Node.js Folder Structure (Clean Architecture) ===== | ===== Node.js Folder Structure (Clean Architecture) ===== | ||
| + | ^Layer^Purpose^Examples^Should define errors?| | ||
| + | |**Domain** |Business rules|Entities, | ||
| + | |**Domain/ | ||
| + | |**Infrastructure** |Technical adapters|DB, | ||
| + | |**Interface/ | ||
| < | < | ||
| + | |||
| src/ | src/ | ||
| ├ api/ | ├ api/ | ||
| | | ||
| | | ||
| - | | + | |
| + | | ||
| ├ domain/ | ├ domain/ | ||
| | | ||
| | | ||
| - | | + | |
| + | | ||
| ├ infrastructure/ | ├ infrastructure/ | ||
| | | ||
| | | ||
| | | ||
| - | | + | |
| + | | ||
| └ tests/ | └ tests/ | ||
| </ | </ | ||
| + | |||
| + | |||
| + | |||
| ===== MySQL — Connection Pool ===== | ===== MySQL — Connection Pool ===== | ||
| Line 334: | Line 339: | ||
| * trace id | * trace id | ||
| * Support dashboards & SLIs | * Support dashboards & SLIs | ||
| - | |||
| ===== Correlation ID Strategy ===== | ===== Correlation ID Strategy ===== | ||
| Line 374: | Line 378: | ||
| ==== Required JSON Fields ==== | ==== Required JSON Fields ==== | ||
| < | < | ||
| - | |||
| < | < | ||
| + | |||
| timestamp | timestamp | ||
| service | service | ||
| Line 390: | Line 394: | ||
| </ | </ | ||
| - | |||
| ===== Pino Implementation ===== | ===== Pino Implementation ===== | ||
| Line 397: | Line 400: | ||
| < | < | ||
| - | < | + | < |
| + | npm i pino pino-http pino-pretty | ||
| </ | </ | ||
| Line 416: | Line 421: | ||
| </ | </ | ||
| - | |||
| ==== HTTP Logger ( / | ==== HTTP Logger ( / | ||
| < | < | ||
| - | |||
| < | < | ||
| Line 437: | Line 440: | ||
| </ | </ | ||
| - | |||
| ==== Express Usage ==== | ==== Express Usage ==== | ||
| < | < | ||
| - | |||
| < | < | ||
| Line 452: | Line 453: | ||
| </ | </ | ||
| - | |||
| ==== Business Logging Example ==== | ==== Business Logging Example ==== | ||
| Line 459: | Line 459: | ||
| < | < | ||
| req.log.info({ | req.log.info({ | ||
| - | | + | |
| redisKey, | redisKey, | ||
| - | step: 'order-confirmation' | + | step: 'booking-confirmation' |
| - | }, 'Order confirmed' | + | }, 'Booking |
| </ | </ | ||
| - | |||
| ==== OpenTelemetry Tracing ==== | ==== OpenTelemetry Tracing ==== | ||
| Line 473: | Line 472: | ||
| * traceId | * traceId | ||
| * spanId | * spanId | ||
| - | |||
| ==== Benefits ==== | ==== Benefits ==== | ||
| Line 481: | Line 479: | ||
| * error attribution | * error attribution | ||
| - | < | + | < |
| + | Code to link trace → logs → DB query | ||
| </ | </ | ||
| Line 495: | Line 495: | ||
| < | < | ||
| - | < | + | < |
| - | it(' | + | describe(' |
| - | const log = logger.info({ test: true }, ' | + | it(' |
| - | expect(log).toBeDefined(); | + | const log = logger.info({ test: true }, ' |
| - | }); \\ | + | expect(log).toBeDefined(); |
| - | }); \\ | + | }); |
| + | }); | ||
| </ | </ | ||
| + | ====== Error Handling Strategy ====== | ||
| + | |||
| + | **1. Client Errors (4xx)** | ||
| + | |||
| + | ^Code^Meaning| | ||
| + | |400|Bad Request/ | ||
| + | |401|unauthorized| | ||
| + | |404|cache expired| | ||
| + | |409|idempotency conflict| | ||
| + | |500|internal error| | ||
| + | |||
| + | **2. Server Errors (5xx)** | ||
| + | |||
| + | ^Type^Source^Action| | ||
| + | |Dependency Failure|Redis/ | ||
| + | |Timeout|3rd-party API|Fail fast| | ||
| + | |Internal Crash|unexpected bug|Log & alert| | ||
| + | |||
| + | **3. Business Errors** | ||
| + | |||
| + | ^Example^Handling| | ||
| + | |Flight unavailable|409| | ||
| + | |Payment declined|422| | ||
| + | |Invalid state transition|409| | ||
| + | |||
| + | ===== Recommended Placement ===== | ||
| + | |||
| + | **1. ****Business / Domain Errors** | ||
| + | |||
| + | When business rule is violeted - | ||
| + | < | ||
| + | < | ||
| + | |||
| + | < | ||
| + | export class FlightUnavailableError extends Error { | ||
| + | code = ' | ||
| + | } | ||
| + | |||
| + | </ | ||
| + | |||
| + | **2. ****Application Errors** | ||
| + | |||
| + | Use-case level\\ | ||
| + | Validation / workflow / orchestration | ||
| + | |||
| + | / | ||
| + | < | ||
| + | |||
| + | < | ||
| + | export class ValidationError extends Error { | ||
| + | status = 400; | ||
| + | code = ' | ||
| + | } | ||
| + | |||
| + | </ | ||
| + | |||
| + | **3. ****Infrastructure Errors** | ||
| + | |||
| + | ONLY for wrapping technical failures | ||
| + | |||
| + | / | ||
| + | < | ||
| + | |||
| + | < | ||
| + | export class RedisConnectionError extends Error { | ||
| + | code = ' | ||
| + | } | ||
| + | |||
| + | </ | ||
| + | |||
| + | BUT — these should usually be **mapped upward** into application-level errors. | ||
| + | |||
| + | Example mapping: | ||
| + | < | ||
| + | |||
| + | < | ||
| + | try { | ||
| + | await redis.get(key); | ||
| + | } catch(e) { | ||
| + | throw new CacheUnavailableError(); | ||
| + | } | ||
| + | |||
| + | </ | ||
| + | |||
| + | So the **API layer never leaks Redis internals.** | ||
| + | |||
| + | |||
| + | ===== Error Flow (Best Practice) ===== | ||
| + | |||
| + | < | ||
| + | Infrastructure error | ||
| + | ⬇ wrap / map | ||
| + | Application error | ||
| + | ⬇ serialized to client | ||
| + | HTTP response | ||
| + | |||
| + | </ | ||
| + | |||
| + | final structure should look like | ||
| + | |||
| + | < | ||
| + | /src | ||
| + | ┣ /domain | ||
| + | | ||
| + | ┣ / | ||
| + | | ||
| + | ┣ / | ||
| + | | ||
| + | ┗ /api (interfaces) | ||
| + | ┗ error-middleware.ts | ||
| + | |||
| + | </ | ||
| + | |||
| + | **What to AVOID** | ||
| + | |||
| + | Defining all errors in '' | ||
| + | → couples business logic to technology | ||
| + | |||
| + | Throwing raw DB/Redis errors upward\\ | ||
| + | → leaks internal details | ||
| + | Mixed styles (sometimes returning strings, sometimes errors)\\ | ||
| + | → debugging nightmare | ||
flight_booking_bff.1767172292.txt.gz · Last modified: by pradnya
