easco
Domain events as GenServer messages
In my work with OTP, I’ve usually sent messages to a process when I want to invoke behavior - an imperative action, or command.
I’ve been learning about Event Storming and Event Sourcing and it occurred to me that using messaging to send Domain Events might be a more effective tool in cases.
I’m curious to hear the reports of the clever folks who came to this realization before me. If you’ve created such a thing, where the messages between processes are Domain Events, what was your context and how did the technique work for you?
Most Liked
karmajunkie
In both cases, what you’re sending is a message, plain and simple. Recognition of the message as a “command” or “event” is a layer of semantics you put on top of the mechanics that’s useful primarily for humans to understand what’s going on. To paraphrase the Honeybadger: “BEAM don’t give a f*ck…”
As you mention further down, whether you store these messages to be able to recreate a data model is another couple of steps beyond this that’s useful to do sometimes.
In terms of design and architecture, the use of “domain events” implies a few things you have to (and you seem to) be aware of—they indicate things that happened in the past, so losing them is a corruption of your data. Its also easy to get the boundaries wrong or model the messages naively, especially if you’re unfamiliar with the design technique (and even if you’ve used it before!) If you’re not doing eventsourcing that’s less of a problem, you can just update your code. But if you’re storing those, that’s a design mistake you have to live with for awhile. So the analysis phase takes on great significance.
As @blatyo mentioned, you may be thinking more about the differences between pubsub and request/response messaging. Both are useful in the right context, and event messages are a better fit for pubsub, where you’re advertising to the rest of your system things that have happened, while command messages you don’t typically want all handlers to fire. One consideration you’ll need to think about here is how your application is distributed—do you need multi-node pubsub? Do you need durability of messages? Acknowledgment of handling? What are the delivery semantics, e.g. at-least-once or at-most-once? All of these will affect the choice of message transport. For command messages, you usually want request/response or at least a point-to-point message (where you don’t care or don’t want a response.) Being able to look up the relevant node in a registry to send the message to is probably enough to deal with.
There’s a whole rabbit-hole of stuff to think about when you start moving in this direction for architecture. If you’re going to bite off ES architecture, I’d recommend using the commanded project in Elixir and promise yourself not to let yourself get bogged down in the mechanics until you’ve been doing it for at least a year. Every time I see someone get into this the first time the inclination seems to be to try to write your own CQRS/ES framework, and frankly, the world doesn’t need another one from someone learning about it as they go. (I speak from experience here, though at least I didn’t do it in Elixir…
)
blatyo
Yep, those could work fine. I think the problem with using GenStage or Flow specifically, is that it’s a mechanism for you to provide back pressure. Basically a way to say, don’t generate an event. If the event is generated from a user action for example, then they don’t make sense by themselves, because the event is going to happen independent of whether there is demand for it. You could keep those events in memory, but if you application can’t process them fast enough at peak load, it’ll crash. For GenStage and Flow to work, you need some queue to pull work from that won’t be subject to the same constraints. That could be DETS like you mention, or a database, or something like RabbitMQ.
Similarly, Phoenix PubSub has no mechanism for back pressure. So you need to process events faster than they come in or you application could be overwhelmed. So, for that reason, you usually want to make sure you always do a fast action. It follows that doing the least amount of work possible will be the fastest thing you can do. What I see people do most often in this situation is rely on some persistence mechanism to queue them and then process them at their leisure.
axelson
With genstage (depending on your use case) you can use load shedding to prevent the system from toppling over. Discord has a great blog post about that: Discord Blog
Popular in Discussions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









