Event Pattern Matching
In Happen, we've taken a different approach to event pattern matching. Instead of providing complex pattern syntax with its own parsing rules and limitations, we leverage JavaScript's first-class functions to give you complete freedom in defining how events should be matched.
Function-Based Matchers
At its core, a pattern in Happen is simply a function that decides whether an event should be handled:
// A pattern is a function that returns true or false for an event type
node.on(eventType => eventType === 'order.submitted', (event, context) => {
// Process order submission
// ...
// Return next function or result
return processPayment;
});This function receives the event's type string and returns a boolean: true if the event should be handled, false if it should be ignored.
This simple approach provides extraordinary flexibility:
// Match exact event type
node.on(type => type === 'order.submitted', handleOrderSubmission);
// Match events by domain prefix
node.on(type => type.startsWith('order.'), (event, context) => {
// Log all order events
logOrderEvent(event);
// Continue to domain-specific handler
return getDomainSpecificHandler(event.type);
});
// Match multiple specific events
node.on(type => ['payment.succeeded', 'payment.failed'].includes(type),
function(event, context) {
// Process payment result
processPaymentResult(event);
// Branch based on success or failure
return event.type === 'payment.succeeded' ?
handlePaymentSuccess : handlePaymentFailure;
}
);
// Match with regular expressions
node.on(type => /^user\.(created|updated|deleted)$/.test(type),
function(event, context) {
// Handle user lifecycle event
updateUserCache(event);
// Determine next step based on event type
const actions = {
'user.created': notifyUserCreated,
'user.updated': notifyUserUpdated,
'user.deleted': notifyUserDeleted
};
// Return appropriate next function
return actions[event.type];
}
);
// Complex conditional matching
node.on(type => {
const [domain, action] = type.split('.');
return domain === 'inventory' && action.includes('level');
}, function(event, context) {
// Process inventory level event
processInventoryLevel(event);
// Check if restock needed
if (isRestockNeeded(event.payload)) {
return createRestockOrder;
}
// Otherwise complete
return { processed: true };
});Creating Your Own Pattern System
With function-based matchers, you can build any pattern matching system that suits your needs. Here's an example of how you might create your own pattern utilities:
Now you can use these utilities to create expressive, reusable matchers:
String Pattern Support
For convenience, Happen also supports string patterns which are converted to matcher functions internally:
String patterns support several features:
Exact matching: 'order.submitted'
Wildcards: 'order.*'
Alternative patterns: '{order,payment}.created'
Multiple segments: 'user.profile.*'
However, function matchers provide greater flexibility and expressiveness when you need more complex matching logic.
Building Domain-Specific Pattern Systems
For larger applications, you might want to build a more structured pattern system. Here's an example of a domain-oriented approach:
Benefits of Function-Based Pattern Matching
This approach to pattern matching offers several advantages:
Unlimited Flexibility: Any matching logic can be implemented
Zero Parse-Time Overhead: Patterns are just functions, no parsing needed
Type Safety: TypeScript can fully type your pattern functions
Testability: Pattern functions can be unit tested independently
Composition: Combine matchers to create complex patterns
Familiar JavaScript: No special syntax to learn, just standard JS
Best Practices
Keep matcher functions pure: They should depend only on the input event type
Create reusable pattern factories: Build a library of matcher creators for your application
Compose simple matchers: Build complex patterns by combining simple ones
Test your matchers: Unit test complex matching logic independently
Consider performance: For high-frequency events, optimize your matcher functions
Last updated