Communication Patterns
The lifeblood of any Happen system is communication between nodes. Happen distills communication down to fundamental patterns that can be composed to create any interaction model.
System-wide Broadcasting
System-wide broadcasting creates an environment where information flows freely throughout the entire ecosystem. Any node can transmit events that propagate across the system, reaching all nodes without targeting specific recipients.
// Weather monitoring service broadcasts a system-wide alert
weatherMonitor.broadcast({
type: "severe-weather",
payload: {
condition: "hurricane",
region: "Gulf Coast",
expectedImpact: "severe",
timeframe: "36 hours",
},
});
// Any node in the system can listen for these alerts
emergencyServices.on(type => type === "severe-weather", event => {
deployEmergencyTeams(event.payload.region);
});
Broadcasting is ideal for:
System-wide notifications
Important state changes that many nodes might care about
Environmental changes affecting the entire system
Crisis or exceptional condition alerts
Direct Communication
Direct communication establishes dedicated point-to-point channels between nodes, enabling private exchanges, guaranteed delivery, and response-oriented interactions.
Request-Response Pattern
// Order service directly communicates with payment service
async function processOrder(event, context) {
// Send to payment service and await response using .return()
const result = await orderNode.send(paymentNode, {
type: "process-payment",
payload: {
orderId: event.payload.orderId,
amount: calculateTotal(event.payload.items),
currency: "USD",
customerId: event.payload.customerId,
}
}).return();
// Handle the response
if (result.status === "approved") {
return createShipment;
} else {
return handlePaymentFailure;
}
}
// Payment service handles the request
paymentNode.on("process-payment", event => {
// Process the payment
const result = chargeCustomer(event.payload);
// Return result directly
return {
status: result.success ? "approved" : "declined",
transactionId: result.transactionId,
message: result.message
};
});
The .return()
method explicitly indicates that you're expecting a response, making the code more readable and intention-clear. The receiving node simply returns a value from its handler as usual.
Response Handling with Callbacks
For more sophisticated response handling, you can provide a callback to .return()
:
// Send request and handle response with a callback
orderNode.send(inventoryNode, {
type: "check-inventory",
payload: { itemId: "123", quantity: 5 }
}).return(response => {
if (response.available) {
processAvailableItem(response);
} else {
handleOutOfStock(response);
}
});
Fire-and-Forget Communication
When no response is needed, simply don't call .return()
:
// Notification without needing a response
function notifyCustomer(event, context) {
// Send notification without calling .return()
orderNode.send(emailNode, {
type: "send-email",
payload: {
to: event.payload.customerEmail,
subject: "Order Confirmation",
orderId: event.payload.orderId
}
});
// Continue processing immediately
return finalizeOrder;
}
// Email node doesn't need to return anything
emailNode.on("send-email", event => {
sendCustomerEmail(event.payload);
// No explicit return needed for fire-and-forget
});
This approach has several advantages:
The sending node clearly indicates its expectations
The API is more extensible for future enhancements
It provides a cleaner way to handle responses
It avoids relying on node context details
Direct communication is best for:
Request-response interactions
Private or sensitive information exchange
Operations requiring acknowledgment
Complex workflows requiring coordination
Implicit Contracts
In Happen, contracts between nodes emerge naturally from event patterns:
A node's interface is defined by the events it handles and emits
The causal relationships between events form a natural contract
New nodes can understand existing patterns through observation
Contracts can evolve naturally as systems change
This emergent approach eliminates the need for formal interface definitions or schema registries.
Choosing the Right Pattern
While Happen allows you to freely mix communication patterns, here are some guidelines:
Use broadcasting when information needs to reach multiple recipients
Use direct communication for targeted interactions
Use
.return()
when you need a responseSkip
.return()
for fire-and-forget operations
The right combination of patterns will depend on your specific domain and requirements. By leveraging these fundamental communication patterns and composing them in different ways, you can create a wide range of interaction models that fit your specific needs.
Last updated