- calling boilerplate (
emit+ await completion + unwrap result) for every request - eventual consistency anxiety (“will my response event arrive?”)
- duplicating signatures across schemas, handlers, and implementation functions
1) Pain: painful calling interface boilerplate
You usually end up writing verbose call sites repeatedly.events_suck.wrap(...) gives you a method-shaped client API (client.create(...)) while still routing through events.
- Python
- TypeScript
- Go
- Rust
Minimal end-to-end wrap(...) wiring
- Python
- TypeScript
- Go
- Rust
2) Pain: eventual consistency headaches
If your mental model is “I called something, I need a result now,” pure fire-and-forget event flows can feel stressful. Two patterns reduce that stress:- request/response on one bus with direct return values (
event_result()afternow({first_result: true})/wait({first_result: true})) - immediate execution for nested calls inside handlers (RPC-style queue-jump)
Nested request/response with immediate execution
- Python
- TypeScript
- Go
- Rust
3) Pain: defining signatures multiple times
You can keep one source of truth for payload shapes and reuse it in implementation code.One source of truth examples
- Python
- TypeScript
- Rust
Use implementation function signatures as the source of truth, then generate event classes from them.
Migration playbook
- Start with
wrap(...)to clean up call-site boilerplate first. - Use immediate execution patterns where you need function-call-like request/response behavior.
- Consolidate types with
@validate_call(Python) orz.infer(TypeScript) to avoid signature drift. - Add timeouts/retry policies where needed, instead of forcing eventual-consistency semantics everywhere.