Loading learning content...
When one object needs to communicate with another, a fundamental choice exists: wait for a response or continue immediately. This choice between synchronous and asynchronous communication profoundly impacts system behavior, performance, resilience, and complexity.
Modern systems increasingly blend both styles. A web request might synchronously validate input, asynchronously queue email notifications, and synchronously return a response—all within a single operation. Sequence diagrams capture this nuanced choreography with distinct notation for each communication style.
By the end of this page, you will understand the difference between synchronous and asynchronous messages, their UML notation, when to use each style, and how to model complex interactions that combine both patterns.
In synchronous communication, the sender blocks until the receiver completes processing and returns. This is the default method call semantics in most programming languages.
Characteristics of Synchronous Calls:
When Synchronous Is Appropriate:
Synchronous calls chain their latencies. If A calls B, which calls C synchronously, A's total wait time is: A-to-B latency + B's processing + B-to-C latency + C's processing + return path. In distributed systems, this compounds quickly.
In asynchronous communication, the sender does not wait for the receiver to complete. The sender dispatches a message and immediately continues with its own execution. The receiver processes the message independently, possibly at a later time.
Characteristics of Asynchronous Calls:
UML Notation for Asynchronous Messages:
Asynchronous messages use a solid line with an open (stick) arrowhead, distinguishing them from synchronous calls (filled arrowhead):
| Message Type | Arrow Style | Arrowhead | Example |
|---|---|---|---|
| Synchronous | Solid line | Filled (▶) | ──▶ |
| Asynchronous | Solid line | Open (>) | ──> |
| Return | Dashed line | Open (>) | - - -> |
Note: Many diagramming tools don't distinguish arrowheads perfectly. Some teams use labels like <<async>> to clarify.
Understanding when to use each communication style is a critical design decision. Let's compare them across key dimensions:
| Dimension | Synchronous | Asynchronous |
|---|---|---|
| Caller Behavior | Blocks until response | Continues immediately |
| Coupling | Tight (must know callee) | Loose (publishes message) |
| Error Handling | Immediate exception/return | Delayed, requires callbacks or polling |
| Result Availability | Immediate | Eventually (callback, promise, or polling) |
| Throughput | Limited by slowest call | Higher (parallel processing) |
| Complexity | Lower (linear flow) | Higher (concurrency, ordering) |
| Failure Resilience | Caller fails if callee fails | Caller unaffected by callee failure |
| Debugging | Easier (stack traces) | Harder (distributed traces) |
Asynchronous communication manifests in several patterns, each with its own modeling considerations:
1. Fire-and-Forget
The sender dispatches a message and never expects a response. Common for notifications, logging, and analytics events.
2. Callback Pattern
The sender provides a callback reference. When the receiver completes, it invokes the callback with results.
3. Request-Reply (Async)
The sender sends a request to one queue and listens on a separate reply queue. Common in message-driven architectures.
In async request-reply, correlation IDs link requests to their responses. Include these in your sequence diagrams when modeling async communication to show how responses are matched to their originating requests.
Asynchronous calls enable concurrent execution—multiple operations happening simultaneously. Sequence diagrams visualize this through parallel activation bars and the par fragment (covered in depth in a later page).
Visual Indicators of Concurrency:
Example: Concurrent Notifications
Reading the Diagram:
The OrderService fires off three asynchronous notifications. The par fragment (parallel) shows that all three services process concurrently. No service waits for another. The total time is the duration of the slowest service, not the sum of all three.
UML also defines a 'coregion' notation (brackets on a single lifeline) to show that events can occur in any order. However, the par fragment is more commonly used and widely supported.
Real systems rarely use pure synchronous or pure asynchronous communication. Most combine both strategically:
Common Hybrid Pattern: Sync for Core, Async for Side Effects
A request is processed synchronously for its primary purpose (validating, persisting, returning response), while secondary operations (notifications, logging, analytics) happen asynchronously.
Analysis:
The user receives their response as soon as the order is saved. The email is sent later, asynchronously. If email sending fails, the order still succeeds—the operations are decoupled.
Design Benefits:
Use notes or visual separation to distinguish synchronous from asynchronous regions in hybrid diagrams. This helps readers understand which operations the user waits for and which happen in the background.
Asynchronous communication introduces unique error handling challenges. Since the sender continues immediately, failures in the receiver can't be directly reported to the sender through normal return mechanisms.
Error Handling Strategies:
| Strategy | Description | Use When |
|---|---|---|
| Dead Letter Queue | Failed messages are routed to a separate queue for later inspection and retry | Transient failures; batch reprocessing |
| Callback Errors | Call an error callback or error channel on failure | Sender needs to know about failures |
| Compensation | Execute a compensating action to undo partial work | Saga pattern; distributed transactions |
| Retry with Backoff | Automatically retry failed operations with increasing delays | Transient network/service issues |
| Circuit Breaker | Stop sending after repeated failures; allow recovery | Protecting failing downstream services |
Modeling Async Errors:
Sequence diagrams can show error paths using alt (alternative) fragments or separate scenarios:
When designing async systems, always ask: 'What happens if this message fails?' Model failure scenarios explicitly in your diagrams. The main path is often the easy part—error handling reveals the real complexity.
Let's model a complete order processing flow that strategically combines synchronous and asynchronous communication.
The Scenario:
A customer places an order. The system must:
Design Analysis:
| Phase | Style | Rationale |
|---|---|---|
| Payment | Synchronous | Must know outcome — can't confirm order if payment fails |
| Inventory | Synchronous | Must confirm availability before accepting order |
| Database | Synchronous | Order must be persisted before confirmation |
| Asynchronous | Customer doesn't wait; failure doesn't affect order | |
| Warehouse | Asynchronous | Decoupled system; different availability requirements |
| Analytics | Asynchronous | Non-critical; shouldn't slow the happy path |
This design optimizes for user experience (fast confirmation), reliability (sync for critical ops), and scalability (async for side effects).
We've explored the two fundamental communication paradigms and how to model them in sequence diagrams:
What's Next:
We've covered how messages flow between objects. The next page explores return values and self-calls in greater depth—how to model what comes back from method invocations and how objects communicate with themselves.
You now understand the critical distinction between synchronous and asynchronous communication in sequence diagrams. You can choose the appropriate style for different scenarios and model complex hybrid flows that combine both paradigms.