Scalable Java Full Stack Architecture & Resilient API Design
1. Backend API: Intermittent Failures and Inconsistent JSON
Section titled “1. Backend API: Intermittent Failures and Inconsistent JSON”Question
Section titled “Question”A backend API starts returning intermittent failures and inconsistent JSON structures.
How would you design:
- Error handling
- Retries
- Fallbacks
- Circuit breakers
- User notifications
What information should be logged on both frontend and backend?
Answer
Section titled “Answer”For a Java Spring Boot + React full-stack application, handle intermittent API failures using a combination of:
- Standardized error responses
- Controlled retries
- Circuit breakers
- Business-appropriate fallbacks
- Centralized user notifications
- Structured logging
- Correlation/trace IDs
The most important principle is to make failures predictable and observable rather than allowing every service to return a different error format.
1.1 Error Handling
Section titled “1.1 Error Handling”The backend should expose a consistent error contract even when the underlying exception differs.
Example:
{ "timestamp": "2026-08-11T21:20:30Z", "status": 503, "code": "CUSTOMER_SERVICE_UNAVAILABLE", "message": "Customer service is temporarily unavailable", "traceId": "abc-123"}In Spring Boot, use:
@RestControllerAdvice@ExceptionHandler- Custom business exceptions
- Standard HTTP status codes
- Validation error handling
- A common
ErrorResponseDTO
Example:
@RestControllerAdvicepublic class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity<ErrorResponse> handleNotFound( ResourceNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND) .body(new ErrorResponse( "RESOURCE_NOT_FOUND", ex.getMessage())); }
@ExceptionHandler(Exception.class) public ResponseEntity<ErrorResponse> handleGeneric(Exception ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new ErrorResponse( "INTERNAL_ERROR", "Something went wrong")); }}Why this matters
Section titled “Why this matters”The React application should not have to understand dozens of backend exception formats.
Instead:
Backend exceptions | vGlobal Exception Handler | vStandard ErrorResponse | vReact1.2 Handling Inconsistent JSON
Section titled “1.2 Handling Inconsistent JSON”If a downstream API returns inconsistent JSON, don’t expose that structure directly to React.
Use:
- Response validation
- Mapping to stable DTOs
- Schema validation where appropriate
- Controlled handling of malformed responses
flowchart LR
A[React] --> B[API Gateway]
B --> C[Spring Boot Service]
C --> D[Downstream API]
D --> E{Valid Response?}
E -->|Yes| F[Map to Stable DTO]
E -->|No| G[Validation Error]
F --> H[Standard API Response]
G --> I[Retry / Fallback / Error]
I --> H
H --> A
This prevents a downstream API’s inconsistent contract from becoming a frontend problem.
1.3 Retries
Section titled “1.3 Retries”Do not retry every failure.
Good retry candidates
Section titled “Good retry candidates”Usually transient failures such as:
- Network timeout
- Connection reset
- HTTP
502 - HTTP
503 - HTTP
504
Usually don’t retry
Section titled “Usually don’t retry”400 Bad Request401 Unauthorized403 Forbidden404 Not Found- Business validation failures
Retry strategy
Section titled “Retry strategy”Use:
- Limited retry count, typically 2–3 attempts
- Exponential backoff
- Jitter
- Timeouts
- Retry only appropriate/idempotent operations
Example:
sequenceDiagram
participant C as Client Service
participant D as Downstream API
C->>D: Attempt 1
D-->>C: Timeout
Note over C: Wait 200 ms
C->>D: Attempt 2
D-->>C: 503
Note over C: Wait 500 ms
C->>D: Attempt 3
D-->>C: Failure
Note over C: Fallback / Error
Important interview point
Section titled “Important interview point”For non-idempotent operations such as payment or order creation, retries can create duplicate operations.
Use idempotency keys when retrying such operations.
1.4 Circuit Breaker
Section titled “1.4 Circuit Breaker”Retries alone are not enough.
If a downstream service is continuously failing, repeated retries can create a retry storm and make the outage worse.
Use a Circuit Breaker, for example with Resilience4j.
Circuit breaker states
Section titled “Circuit breaker states”stateDiagram-v2
[*] --> CLOSED
CLOSED --> OPEN: Failure threshold exceeded
OPEN --> HALF_OPEN: Wait duration elapsed
HALF_OPEN --> CLOSED: Test requests succeed
HALF_OPEN --> OPEN: Test request fails
Example
Section titled “Example”@CircuitBreaker( name = "customerService", fallbackMethod = "customerFallback")public Customer getCustomer(Long id) { return customerClient.getCustomer(id);}Important configuration
Section titled “Important configuration”Configure:
- Failure-rate threshold
- Sliding window
- Minimum number of calls
- Open-state wait duration
- Number of permitted half-open calls
1.5 Fallbacks
Section titled “1.5 Fallbacks”Fallback behavior should depend on the business capability.
Example: Recommendation Service
Section titled “Example: Recommendation Service”If recommendations fail:
flowchart LR
A[Recommendation Request] --> B[Recommendation Service]
B -->|Failure| C[Circuit Breaker]
C --> D[Fallback]
D --> E[Popular Products / Cached Data]
E --> F[User]
Possible fallback strategies:
- Cached response
- Default response
- Read-only mode
- Queue operation asynchronously
- Graceful degradation
- User-friendly error
Critical transactions
Section titled “Critical transactions”For payment or order processing, don’t silently return fake/default data.
Instead, clearly indicate that the operation could not be completed.
1.6 User Notifications
Section titled “1.6 User Notifications”The user should see a business-friendly message, not a Java exception.
503 Service Unavailable - SocketTimeoutExceptionPrefer
Section titled “Prefer”We're temporarily unable to load your orders.Please try again.React can centralize error handling using an Axios interceptor.
axios.interceptors.response.use( response => response, error => { if (error.response?.status === 503) { showNotification( "Service is temporarily unavailable. Please try again." ); }
return Promise.reject(error); });Error-to-user behavior
Section titled “Error-to-user behavior”| Error | User experience |
|---|---|
| Network failure | Check your connection |
| 401 | Redirect to login |
| 403 | You don’t have permission |
| 404 | Resource not found |
| 429 | Too many requests, try later |
| 500 | Something went wrong |
| 503 | Service temporarily unavailable |
2. Logging and Observability for API Failures
Section titled “2. Logging and Observability for API Failures”Use structured JSON logging and correlate frontend and backend logs using a trace/correlation ID.
2.1 Frontend logging
Section titled “2.1 Frontend logging”Useful information:
- API URL or logical API name
- HTTP method
- Status code
- Timestamp
- Request duration
- Trace/correlation ID
- Error type
- Component/page
- Retry attempt
- User action that triggered the request
- Browser/device information where appropriate
Example:
{ "level": "ERROR", "api": "/api/orders", "method": "GET", "status": 503, "durationMs": 3200, "traceId": "abc-123", "errorCode": "ORDER_SERVICE_UNAVAILABLE", "page": "OrderHistory"}Do not log:
- Passwords
- Access tokens
- Authorization headers
- Credit-card numbers
- Sensitive customer data
2.2 Backend logging
Section titled “2.2 Backend logging”Useful information:
- Trace/correlation ID
- Request ID
- Service name
- API endpoint
- HTTP method
- Status code
- Response time
- Exception type
- Stack trace
- Downstream service
- Retry count
- Circuit-breaker state
- Timeout information
- Database failures
- Kafka/message information where applicable
- Host/container/pod information
Example:
{ "level": "ERROR", "service": "order-service", "traceId": "abc-123", "endpoint": "/api/orders", "downstream": "customer-service", "status": 503, "retryCount": 2, "circuitState": "OPEN", "durationMs": 3100, "exception": "SocketTimeoutException"}3. Interview Summary: Resilient API Design
Section titled “3. Interview Summary: Resilient API Design”I would standardize the backend error contract using
@ControllerAdvice, validate and map inconsistent downstream responses into stable DTOs, and use Resilience4j for timeout, limited exponential-backoff retries, circuit breakers, and business-appropriate fallbacks. On the React side, I would centralize API error handling through Axios interceptors and show user-friendly notifications. I would use structured logging with a correlation/trace ID across frontend and backend, capturing status, latency, retry count, downstream service, circuit state, and exception details while ensuring that credentials and sensitive customer data are never logged.
4. Scalable Java Full Stack Architecture
Section titled “4. Scalable Java Full Stack Architecture”Question
Section titled “Question”Design a scalable Java Full Stack architecture where ReactJS consumes multiple backend APIs.
Discuss:
- API Gateway
- Authentication
- Caching
- Observability
- Containerization
- CI/CD
- Horizontal scaling
- Microservices vs Monolith
5. High-Level Architecture
Section titled “5. High-Level Architecture”A scalable architecture can be designed as:
flowchart TB
U[Users] --> R[ReactJS Application]
R -->|HTTPS / JWT| G[API Gateway]
G --> US[User Service]
G --> OS[Order Service]
G --> PS[Payment Service]
G --> NS[Notification Service]
US --> UDB[(User DB)]
OS --> ODB[(Order DB)]
PS --> PDB[(Payment DB)]
US --> C[(Redis Cache)]
OS --> C
OS --> K[Kafka / Event Streaming]
K --> NS
US --> O[Observability]
OS --> O
PS --> O
G --> O
subgraph Kubernetes
US
OS
PS
NS
end
6. API Gateway
Section titled “6. API Gateway”React should generally not communicate directly with every microservice.
Instead:
flowchart LR
R[React] --> G[API Gateway]
G --> U[User Service]
G --> O[Order Service]
G --> P[Payment Service]
G --> N[Notification Service]
The API Gateway provides a single entry point.
Responsibilities
Section titled “Responsibilities”- Routing
- Authentication/token validation
- Authorization checks where appropriate
- Rate limiting
- Request/response transformation
- CORS
- TLS termination
- Load balancing
- Correlation ID propagation
- API versioning
- Circuit breaking where appropriate
Example:
GET /api/orders | vAPI Gateway | vOrder ServiceReact does not need to know where the Order Service is physically deployed.
7. Authentication and Authorization
Section titled “7. Authentication and Authorization”Use an OAuth2/OIDC-based identity provider such as:
- Keycloak
- Okta
- Azure AD / Microsoft Entra ID
- Another enterprise identity provider
Typical flow:
sequenceDiagram
participant R as React
participant IDP as Identity Provider
participant G as API Gateway
participant S as Backend Service
R->>IDP: Login
IDP-->>R: Access Token
R->>G: API Request + Bearer Token
G->>G: Validate JWT
G->>S: Forward Request
S->>S: Authorize Resource
S-->>G: Response
G-->>R: Response
JWT claims can include:
subrolesscopesexpissuerAuthentication vs Authorization
Section titled “Authentication vs Authorization”Authentication:
Who are you?
Authorization:
What are you allowed to do?
For example:
ADMIN → Create/Delete usersUSER → View own ordersSpring Security can enforce authorization at the service level.
Important design decision
Section titled “Important design decision”Do not rely exclusively on the gateway for authorization.
Each microservice should protect its own resources because services may also be accessed through internal communication.
8. Caching
Section titled “8. Caching”Use Redis for frequently accessed and relatively stable data.
Architecture:
flowchart LR
R[React] --> G[Gateway]
G --> P[Product Service]
P --> C{Redis Cache}
C -->|Cache Hit| R
C -->|Cache Miss| DB[(Database)]
DB --> C
C --> P
P --> R
Good caching candidates
Section titled “Good caching candidates”- Product/catalog data
- Reference/master data
- User preferences
- Frequently accessed configuration
- Session-related data where appropriate
Define:
- TTL
- Cache eviction strategy
- Cache invalidation
- Maximum cache size
For rapidly changing data, avoid aggressive caching because stale data can become a business problem.
9. Observability
Section titled “9. Observability”For distributed systems, logs alone are not enough.
Use the three pillars:
- Logs
- Metrics
- Distributed tracing
9.1 Logs
Section titled “9.1 Logs”Use structured JSON logs and centralized collection.
flowchart LR
S[Spring Boot Services] --> F[Fluent Bit / Log Collector]
F --> E[Elasticsearch]
E --> K[Kibana]
Log:
- Trace ID
- Request ID
- Service
- Endpoint
- Status
- Latency
- Exception
- Business correlation information
Never log passwords, tokens, or sensitive customer information.
9.2 Metrics
Section titled “9.2 Metrics”Use:
flowchart LR
S[Spring Boot] --> A[Spring Boot Actuator]
A --> P[Prometheus]
P --> G[Grafana]
Monitor:
- CPU
- Memory
- Request rate
- Error rate
- Response time
- JVM memory
- GC
- Thread pools
- Database connection pools
- Kafka lag
- Circuit breaker state
9.3 Distributed Tracing
Section titled “9.3 Distributed Tracing”Use OpenTelemetry with a tracing backend such as Jaeger or Tempo.
A request might flow through:
React ↓Gateway ↓Order Service ↓Payment Service ↓DatabaseA shared trace ID allows the complete request path to be investigated.
This helps answer:
Why did this API take 5 seconds?
10. Containerization
Section titled “10. Containerization”Each Spring Boot service can be packaged as a Docker image.
flowchart LR
A[Spring Boot Application] --> B[Docker Image]
B --> C[Container]
C --> D[Kubernetes Pod]
Example Dockerfile:
FROM eclipse-temurin:21-jre
COPY target/order-service.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]Containers should be:
- Stateless
- Small
- Immutable
- Configured through environment/config management
- Free from persistent local storage
Persistent data belongs in external databases or storage systems.
11. Kubernetes and Horizontal Scaling
Section titled “11. Kubernetes and Horizontal Scaling”Kubernetes allows multiple instances of a service to run simultaneously.
flowchart TB
LB[Load Balancer]
LB --> P1[Order Pod 1]
LB --> P2[Order Pod 2]
LB --> P3[Order Pod 3]
P1 --> DB[(Order Database)]
P2 --> DB
P3 --> DB
If traffic increases:
flowchart LR
A[3 Pods] --> B[Traffic / CPU Increases]
B --> C[Horizontal Pod Autoscaler]
C --> D[6 Pods]
Use Kubernetes HPA based on:
- CPU
- Memory
- Request rate
- Other meaningful application metrics
Services should be stateless so any pod can process a request.
12. CI/CD
Section titled “12. CI/CD”A production CI/CD pipeline could be:
flowchart LR
A[Developer] --> B[Git]
B --> C[Pull Request]
C --> D[Code Review]
D --> E[Build]
E --> F[Unit Tests]
F --> G[Static Analysis]
G --> H[Security Scan]
H --> I[Docker Build]
I --> J[Container Registry]
J --> K[Deploy Dev]
K --> L[Integration Tests]
L --> M[Deploy QA]
M --> N[Approval]
N --> O[Production]
Possible tools:
- GitHub/GitLab
- Maven
- JUnit
- SonarQube
- Docker
- Jenkins/GitHub Actions/GitLab CI
- Kubernetes
- Helm
For critical systems, use:
- Blue-green deployments
- Canary releases
- Automated rollback
- Health checks
13. Microservices vs Monolith
Section titled “13. Microservices vs Monolith”Do not automatically choose microservices simply because scalability is required.
13.1 Monolith
Section titled “13.1 Monolith”flowchart LR
R[React] --> S[Spring Boot Application]
S --> DB[(Single Database)]
Advantages
Section titled “Advantages”- Simpler development
- Easier debugging
- Easier deployment
- Less infrastructure
- Easier transactions
- Lower operational complexity
For small or medium applications, a modular monolith can be an excellent starting point.
13.2 Microservices
Section titled “13.2 Microservices”flowchart LR
G[API Gateway] --> U[User Service]
G --> O[Order Service]
G --> P[Payment Service]
G --> N[Notification Service]
U --> UDB[(User DB)]
O --> ODB[(Order DB)]
P --> PDB[(Payment DB)]
Advantages
Section titled “Advantages”- Independent deployment
- Independent scaling
- Team ownership
- Technology isolation
- Fault isolation
- Business-domain separation
Disadvantages
Section titled “Disadvantages”- Network failures
- Distributed transactions
- More monitoring
- More deployment complexity
- Service discovery/configuration
- Data consistency challenges
- Higher infrastructure cost
14. Recommended Architecture Decision
Section titled “14. Recommended Architecture Decision”For a new system, start with a well-structured modular monolith unless there is a clear business or technical reason for microservices.
Extract services when there are:
- Independent scaling requirements
- Clear business boundaries
- Different release cycles
- Large development teams
- Fault-isolation requirements
- Strong domain boundaries
A good evolution path is:
flowchart LR
A[Modular Monolith] --> B[Identify Bounded Contexts]
B --> C[Extract High-Value Service]
C --> D[Independent Deployment]
D --> E[Independent Scaling]
E --> F[Full Microservices Where Justified]
This avoids introducing distributed-system complexity before it is necessary.
15. Complete Production Architecture
Section titled “15. Complete Production Architecture”flowchart TB
U[Users] --> R[ReactJS]
R -->|HTTPS + JWT| G[API Gateway]
G --> US[User Service]
G --> OS[Order Service]
G --> PS[Payment Service]
G --> NS[Notification Service]
US --> UDB[(User DB)]
OS --> ODB[(Order DB)]
PS --> PDB[(Payment DB)]
US --> REDIS[(Redis)]
OS --> REDIS
OS --> KAFKA[Kafka]
KAFKA --> NS
subgraph K8S[Kubernetes Cluster]
G
US
OS
PS
NS
end
K8S --> HPA[Horizontal Pod Autoscaler]
US --> OTEL[OpenTelemetry]
OS --> OTEL
PS --> OTEL
NS --> OTEL
G --> OTEL
OTEL --> TRACE[Tracing Backend]
US --> PROM[Prometheus]
OS --> PROM
PS --> PROM
NS --> PROM
PROM --> GRAF[Grafana]
US --> LOG[Centralized Logging]
OS --> LOG
PS --> LOG
NS --> LOG
G --> LOG
LOG --> KIBANA[Kibana]
16. Architecture Responsibilities
Section titled “16. Architecture Responsibilities”| Layer | Main Responsibility |
|---|---|
| ReactJS | UI, client-side state, routing, user experience |
| API Gateway | Routing, security boundary, rate limiting, API composition |
| Spring Boot Services | Business logic and domain capabilities |
| Spring Security | Authentication/authorization enforcement |
| Redis | Low-latency caching |
| Kafka | Asynchronous event-driven communication |
| Database | Persistent business data |
| Docker | Application packaging |
| Kubernetes | Container orchestration and scaling |
| HPA | Horizontal scaling |
| Prometheus | Metrics |
| Grafana | Metrics dashboards and alerts |
| OpenTelemetry | Distributed tracing/telemetry |
| Centralized logging | Searchable application logs |
| CI/CD | Automated build, test, security and deployment |
17. Interview-Ready Answer
Section titled “17. Interview-Ready Answer”I would design the application with React as the presentation layer and an API Gateway as the single entry point to Spring Boot backend services. Authentication would use OAuth2/OIDC with JWT, while authorization would be enforced at both the gateway and service level. Redis would be used for frequently accessed data, and Kafka could handle asynchronous communication between services.
For observability, I would implement centralized structured logging, Prometheus/Grafana metrics, and distributed tracing using OpenTelemetry. Each service would be containerized using Docker and deployed on Kubernetes, with HPA providing horizontal scaling.
CI/CD would automate build, testing, security scanning, container creation, and deployment. For critical services, I would use canary or blue-green deployments with automated rollback.
However, I would not choose microservices automatically. I would start with a modular monolith when the system and team are small or medium-sized, and extract services when independent scaling, deployment, fault isolation, or strong domain boundaries justify the additional distributed-system complexity.
18. Strong Lead-Level Points to Mention
Section titled “18. Strong Lead-Level Points to Mention”For a senior/lead interview, emphasize these decisions:
Scalability
Section titled “Scalability”“I design services to be stateless so they can scale horizontally.”
Resilience
Section titled “Resilience”“Retries are controlled and only used for transient failures. Circuit breakers prevent repeatedly calling unhealthy dependencies.”
Security
Section titled “Security”“Authentication and authorization are separate concerns, and services shouldn’t blindly trust the gateway.”
Performance
Section titled “Performance”“I use Redis selectively and define an explicit cache invalidation strategy.”
Observability
Section titled “Observability”“Every request should be traceable across React, gateway, microservices, databases, and asynchronous messaging.”
Deployment
Section titled “Deployment”“Containers should be immutable and deployments should support health checks and rollback.”
Architecture
Section titled “Architecture”“Microservices are a means to solve organizational, scalability, and domain problems—not a default architecture.”
“Each microservice should own its data where practical, and distributed transactions should be avoided in favor of patterns such as Saga and event-driven workflows when appropriate.”
19. Final Mental Model
Section titled “19. Final Mental Model”flowchart TB
A[React UI]
B[Secure API Gateway]
C[Authenticated Spring Boot Services]
D[Redis Cache]
E[Databases]
F[Kafka Events]
G[Observability]
H[Kubernetes]
I[CI/CD]
A --> B
B --> C
C --> D
C --> E
C --> F
C --> G
C --> H
I --> H
The overall design principle is:
Secure at the edge, validate contracts, keep services stateless, cache carefully, communicate asynchronously where appropriate, observe every request, scale horizontally, and introduce microservices only when their benefits outweigh their operational complexity.