Four decorators around an HTTP client, composed in an order that is obvious once and unrecoverable afterwards.
$client = new LoggingClient( // 4. logs what was sent
new RetryingClient( // 3. retries the signed request
new SigningClient( // 2. signs with a fresh timestamp
new RateLimitedClient( // 1. waits before anything
$psr18,
),
),
),
);
Signing inside retry is mandatory — a retried request needs a new timestamp or the signature is stale and the second attempt fails differently from the first. Logging outermost means the log records what was actually sent including retries, and rate limiting innermost means the wait happens once per attempt. Every one of those is a bug if inverted, and a comment per layer is the only documentation.