PrepZone Logo
PrepZone

Discovery, Config, Gateway

Service discovery, centralised config and API gateway with Spring Cloud.

Why this matters

  • Hardcoding http://inventory-service:8081 in every client breaks when services move, scale, or get replaced during a deploy.
  • A central config server lets you change payment gateway URLs or feature flags across all services without rebuilding images.
  • An API gateway gives BookStore customers one entry point while routing to catalog, order, and payment services behind the scenes.

StreamHub production architecture (AWS)

HTTPSstaticmissAPICLIENT
Mobile / WebStreamHub cli…
NETWORK
Route 53GeoDNS routing
NETWORK
CloudFrontCDN + WAF edge
NETWORK
AWS ALBTLS terminati…
NETWORK
API GatewayJWT · rate li…
STORAGE
Amazon S3media origin
COMPUTE
Amazon EKSAPI · auth · …
DATABASE
ElastiCachesessions · ho…
DATABASE
RDS Postgresprimary + rep…
INTEGRATION
Amazon MSKdomain events
ANALYTICS
OpenSearchstream discov…
OPS
CloudWatchmetrics · X-R…
End-to-end path from user to data — reference this when placing any new service.

Spring Cloud components for BookStore

  • Spring Cloud Gateway — reactive edge proxy; routes, rate-limits, and authenticates incoming traffic.
  • Eureka / Consul — service registry; instances register on startup and deregister on shutdown.
  • Spring Cloud Config — centralised application.yml served per service and profile.
  • @LoadBalanced RestClient — resolves service names through the registry instead of hardcoded hosts.
  • Spring Cloud Kubernetes — alternative to Eureka when you deploy on K8s and use native service discovery.

Service registry with Eureka

Java
<!-- Eureka server (separate deployable) -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
Java
@SpringBootApplication
@EnableEurekaServer
public class DiscoveryServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(DiscoveryServerApplication.class, args);
    }
}
Java
# bookstore-catalog-service application.yml
eureka:
  client:
    service-url:
      defaultZone: http://discovery-server:8761/eureka/
  instance:
    prefer-ip-address: true

spring:
  application:
    name: bookstore-catalog

API Gateway routing

Java
@Configuration
public class GatewayRoutesConfig {

    @Bean
    public RouteLocator bookstoreRoutes(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("catalog", r -> r
                        .path("/api/books/**")
                        .filters(f -> f.stripPrefix(0)
                                .addRequestHeader("X-Gateway", "bookstore"))
                        .uri("lb://bookstore-catalog"))
                .route("orders", r -> r
                        .path("/api/orders/**")
                        .uri("lb://bookstore-orders"))
                .route("payments", r -> r
                        .path("/api/payments/**")
                        .filters(f -> f.circuitBreaker(config -> config
                                .setName("paymentCircuitBreaker")
                                .setFallbackUri("forward:/fallback/payments")))
                        .uri("lb://bookstore-payments"))
                .build();
    }
}

Centralised configuration

Java
# config-server application.yml
spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/bookstore/platform-config
          search-paths: "{application}"
Java
# bookstore-orders bootstrap.yml
spring:
  application:
    name: bookstore-orders
  config:
    import: "configserver:http://config-server:8888"
  profiles:
    active: ${DEPLOY_ENV:local}
Java
@Configuration
@EnableConfigurationProperties(OrderServiceProperties.class)
public class OrderConfig {}

@ConfigurationProperties(prefix = "bookstore.orders")
public record OrderServiceProperties(
        Duration reservationTimeout,
        int maxItemsPerOrder,
        String paymentServiceUrl
) {}

Load-balanced client calls

Java
@Configuration
public class ServiceClientConfig {

    @Bean
    @LoadBalanced
    public RestClient.Builder loadBalancedRestClientBuilder() {
        return RestClient.builder();
    }
}

@Service
public class InventoryClient {

    private final RestClient restClient;

    public InventoryClient(@LoadBalanced RestClient.Builder builder) {
        this.restClient = builder.build();
    }

    public StockLevel checkStock(String isbn) {
        return restClient.get()
                .uri("http://bookstore-inventory/api/stock/{isbn}", isbn)
                .retrieve()
                .body(StockLevel.class);
    }
}

Quick recall

Everything you need if you only revisit this box.

  • Spring Cloud Gateway is the single entry point; route by path to backend services via lb://service-name.
  • Eureka (or K8s DNS) lets clients resolve service names without hardcoded URLs.
  • Spring Cloud Config serves environment-specific properties from a Git-backed config server.
  • @LoadBalanced RestClient.Builder picks a healthy instance from the registry on each call.
  • Do not adopt microservices until independent deployment and scaling justify the operational complexity.

Test yourself

Answer these before moving on — recall is what makes it stick.