Latency
Remote collaboration always costs time and can vary under load or network pressure.
Once a workflow crosses service boundaries, the system gains new failure modes: the remote service may be slow, unavailable, overloaded, partially complete, or logically duplicated by retries. The design challenge is no longer just implementing the happy path. It is making the unhappy paths predictable and safe.
An order placement flow may authorize payment, reserve inventory, and emit notifications. If payment succeeds but the network times out before the response reaches the caller, did the operation succeed or fail? That ambiguity is normal in distributed systems and must be designed for explicitly.
Distributed failure is fundamentally about uncertainty. Local code usually knows whether a method finished or threw an exception. Remote code often leaves you with incomplete knowledge: maybe the call never arrived, maybe it arrived twice, maybe it succeeded and the acknowledgment was lost. This is why timeouts, idempotency keys, retries, circuit breakers, and asynchronous recovery matter.
Good distributed design assumes that networks are slow, clocks differ, downstreams overload, and remote state changes independently of the caller's assumptions. The job of the architect is not to eliminate uncertainty; it is to contain its blast radius and make business outcomes recoverable.
Remote collaboration always costs time and can vary under load or network pressure.
One service can fail while the overall user request is still alive and waiting.
A timeout rarely means 'nothing happened'; it often means 'we do not know what happened yet'.
Protect callers with timeouts, circuit breakers, fallbacks, and queues when synchronous dependency is not essential.
Use idempotency keys and deduplication so retries do not charge twice or reserve stock twice.
Bulkheads and concurrency limits stop one slow downstream from exhausting all request threads.
Tracing, structured logs, and correlation IDs help the team understand what happened across multiple services.
package org.javaomnibus.payments.client;
public final class PaymentAuthorizationClient {
private final PaymentGateway gateway;
public PaymentAuthorizationClient(PaymentGateway gateway) {
this.gateway = gateway;
}
public AuthorizationResult authorize(OrderId orderId, Money total, String idempotencyKey) {
try {
return gateway.authorize(orderId, total, idempotencyKey, Duration.ofSeconds(2));
} catch (TimeoutException ex) {
return AuthorizationResult.unknown("Timed out; outcome must be reconciled asynchronously");
}
}
}