Skip to content

Support “Interface-as-Contract, Service-Name-as-Target” Declarative Client (e.g., @ServiceClient) for Protocol-Agnostic Service Invocation #35929

@seal90

Description

@seal90

Type: Enhancement

With the increasing service-ification of infrastructure (e.g., SaaS, DBaaS, STaaS) and the widespread adoption of microservices exposing capabilities via gRPC/Protobuf or HTTP/REST, invoking services by logical name—whether inside or outside the cluster—has become a natural and widely accepted pattern.

The service provider inherently owns the most authoritative contract: a Java interface with annotations.
Ideally, consumers should be able to:

  • Reuse that exact interface directly,
  • Invoke methods using only a logical service name (e.g., "user-service"),
  • Enjoy local-method-like syntax without worrying about protocols, serialization, service discovery, or networking details.

This ideal centers on two core principles:
Interface as Contract
Service Name as Target

However, the current Spring Boot ecosystem lacks native support for this paradigm:

Protocol Current Solution Gap vs. Ideal Paradigm
HTTP RestTemplate / WebClient Manual URI/method/error handling; no interface reuse
HTTP @HttpExchange + WebClient Supports interfaces but requires explicit proxy creation
HTTP @ImportHttpServices Groups clients by service—closer to the ideal
HTTP OpenFeign Widely used, but forces consumers to duplicate interface definitions, violating "single source of truth"
gRPC @ImportGrpcClients Similar to @ImportHttpServices, but protocol-specific

Despite protocol differences, all scenarios point to the same need:

Let providers publish a Java interface; let consumers inject it via @ServiceClient("service-name")—no extra config, no duplication.

Ideal usage example:

// Interface published by user-service (e.g., via shared library)
public interface UserClient {
    @GetMapping("/users/{id}")
    User getUser(@PathVariable String id);
}

// Consumer simply injects by logical service name
@Service
public class OrderService {

    @ServiceClient("user-service") // ← Resolved to actual endpoint automatically
    private final UserClient userClient;

    public void processOrder() {
        User user = userClient.getUser("123"); // Transparent remote call
    }
}

This pattern has already proven valuable in several ecosystems:

  • gRPC: grpc-spring enables interface reuse and service-name binding via @GrpcClient("service-name").
  • OpenFeign: Though it requires interface duplication, it demonstrates strong demand for declarative service clients.

I've built a working prototype (see #https://github.com/seal90/service-client-spring/).

Proposal

Could Spring Boot provide an official, lightweight, declarative service client abstraction (e.g., @ServiceClient) that supports the “interface reuse + service-name-as-target” paradigm?

It should offer:

  1. Contract Reuse: Use the provider's published Java interface directly—no duplication.
  2. Service-Name Driven: Resolve endpoints by logical name (e.g., "payment-service") and integrate with Spring Cloud LoadBalancer.
  3. Protocol Agnostic: Start with HTTP support; extensible to gRPC, RSocket, etc.
  4. Zero-Config Experience: Seamlessly integrate with Spring Boot observability (Micrometer), health checks, config refresh, and auto-configuration.

Adopting a “single source of truth + declarative invocation” model would dramatically reduce boilerplate and cognitive load for service-to-service communication.

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)status: waiting-for-feedbackWe need additional information before we can continuestatus: waiting-for-triageAn issue we've not yet triaged or decided on

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions