Strong answer
Microservices trade in-process simplicity for independent deployment and team autonomy, so boundaries should follow business capabilities and data ownership rather than technical layers. Each service owns its data; other services interact through APIs or events, never through its tables. Choose synchronous calls (REST or gRPC) when the caller needs an immediate answer, and asynchronous messaging when work can complete later or many consumers react to the same fact. Gateways, discovery, and timeouts become first-class concerns.
Java example
1@HttpExchange("/api/inventory")
2interface InventoryClient {
3 @GetExchange("/{sku}")
4 StockLevel stock(@PathVariable String sku);
5}
6
7@Configuration
8class ClientConfig {
9 @Bean
10 InventoryClient inventoryClient(RestClient.Builder builder) {
11 var factory = new SimpleClientHttpRequestFactory();
12 factory.setConnectTimeout(Duration.ofMillis(500));
13 factory.setReadTimeout(Duration.ofSeconds(2));
14 RestClient client = builder
15 .baseUrl("http://inventory-service")
16 .requestFactory(factory)
17 .build();
18 return HttpServiceProxyFactory
19 .builderFor(RestClientAdapter.create(client))
20 .build()
21 .createClient(InventoryClient.class);
22 }
23}