System Design

Last Updated: 1/21/2026

Service Discovery

The Problem: Dynamic Locations & Tight Coupling

  • Hardcoded IPs: Services need to talk to others (e.g., Order Service needs Product Service), but IP addresses change as instances scale up/down, causing failures.
  • Tight Coupling: Hardcoding causes direct dependencies, making systems brittle and difficult to update or test across environments (dev, QA, prod).
  • Single Point of Failure: If a single instance is hardcoded and fails, the whole communication breaks.
  • Scalability Issues: Difficult to distribute load or add new instances without manual config changes.

The Solution: Service Discovery

  • Service discovery is the automatic process where applications and devices find each other on a network, crucial for dynamic environments like microservices.
  • Using a central Service Registry to register and look up network locations (IPs/ports) of services, eliminating hardcoded addresses and enabling dynamic scaling and communication.
  • Services register themselves, and consumers query the registry to find healthy instances, often using logical names instead of physical ones
  • Facilitated by tools like Consul, Kubernetes, or Eureka.

Key Components

  • Service Registry: A central database (like Consul, Eureka) holding real-time information about available services and their locations.
  • Service Provider: The service offering functionality (e.g., a payment service).
  • Service Consumer: The service that needs to use another service's function (e.g., an order service needing the payment service).

How it works

  • Registration: When a service starts, it registers its network address (IP/port) and health status with the Service Registry. Lookup: A service needing to communicate with another queries the registry using a logical service name (e.g., "User-Service"). Resolution: The registry returns the current physical address(es) of healthy instances, allowing the requesting service to connect directly.

Benefits

  • Simplified Operations: Automates complex network management in distributed systems.
  • Decoupling: Services don't need to know each other's hardcoded locations.
  • Health Checks: The registry monitors services to ensure only healthy, available instances are returned.
  • Resilience: Routes traffic to healthy instances, improving reliability.
  • Dynamic Scalability: Easily add or remove service instances without reconfiguration.

Examples in Practice

  • Kubernetes: Uses built-in service discovery for pods.
  • HashiCorp Consul: A popular tool for service discovery and configuration.
  • Netflix Eureka: A client-side discovery tool for Java/Spring applications

Service Discovery in .NET

  • Create ASP.NET Core Project
  • Add nuget package Microsoft.Extensions.ServiceDiscovery
  • Add service dependencies in program.cs
builder.Services.AddConfigurationServiceEndpointProvider();
  • Configure HttpClient and add service discovery
builder.Services
.AddHttpClient("sampleapi", client =>
{
    client.BaseAddress = new("https://sampleapi");
})
.AddServiceDiscovery();
  • Add config in appsettings.json
{
  "Services": {
	  "sampleapi": {
	    "https": [
	      "jsonplaceholder.typicode.com"
	    ]
	  }
	}
}
  • In HomeController.cs, inject IHttpClientFactory to create httpclient object and call api
public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;
    private readonly IHttpClientFactory _httpClientFactory;

    public HomeController(ILogger<HomeController> logger, IHttpClientFactory httpClientFactory)
    {
        _logger = logger;
        _httpClientFactory = httpClientFactory;
    }

    public async Task<IActionResult> Index()
    {
        var httpClient = _httpClientFactory.CreateClient("sampleapi");
        var result = await httpClient.GetFromJsonAsync<List<Todo>>("todos");
        return View(result);
    }
}

References