axelson
How would you handle real-time channel messages initiated by a non-phoenix application
I’m using an umbrella application to give some structure to the code.
Imagine the following general structure
- apps/sales (domain specific application with ecto)
- apps/web_interface (Phoenix application)
- WebInterface.UserChannel (Phoenix Channel)
- WebInterface.SaleView (Phoenix View)
- has a dependency on the sales application
Now when an event such as a new sale occurs in the sales application, I’d like to trigger a message to a user on the WebInterface.UserChannel that is rendered as JSON via the WebInterface.SaleView. But I have to problems:
- The
web _interfaceapplication depends on thesalesapplication so I am unable to make a direct call (since I don’t want a circular dependency). I can create a generic callback method so that I can send any messages I want (e.g. with this approach Best practice for calling a function in another application without defining a dependency) but I don’t really want thesalesapplication to be able to send any message, just a limited subset - Since the Phoenix Views are contained in the
web_interfacethesalesapplication cannot render the sale to JSON by itself.
Since everything (or nearly) everything in these applications are JSON-based rather than HTML-based I’m also starting to question if Phoenix Views are even necessary. Maybe I should just define modules in the sales application that return a purely map/array based rendering of the data.
Does anyone have any thoughts or suggestions?
Most Liked
kokolegorille
I had the same concern, but it is difficult to avoid circular dependencies if You want to communicate between.
web → sales
sales → web
In order for sales to communicate with web with the least inference, You could configure a notifier in sales.
# game engine config
config :game_engine, notify: GameWeb.Notifier
# game_engine module
def notify(message) do
module = Application.get_env(:game_engine, :notify)
module.notify(message)
end
# game_web notifier
defmodule GameWeb.Notifier do
@moduledoc false
require Logger
# Notification Hub for application
# Used by GameEngine to notify worker's exit
def notify(%{payload: payload, type: :game_created}) do
GameWeb.Endpoint.broadcast!("lobby", "game_added", %{game: payload})
end
def notify(%{payload: payload, type: :game_stopped}) do
GameWeb.Endpoint.broadcast!("lobby", "game_removed", %{uuid: payload})
# Notify remaining guest the game has ended!
GameWeb.Endpoint.broadcast!("game:#{payload}", "game_force_quit", %{uuid: payload})
end
def notify(%{type: :request_created} = message) do
# Do nothing, notification is done in lobby channel
Logger.debug(fn -> "No op #{inspect(message)}" end)
end
def notify(%{payload: payload, type: :request_stopped}) do
GameWeb.Endpoint.broadcast!("lobby", "request_cancelled", %{uuid: payload})
end
def notify(message) do
Logger.debug(fn -> "Unknown notification #{inspect(message)}" end)
end
end
So I am just passing an atom in the engine, that points back to web.
Although ultra simplistic, it works well. But for more complex solution, I would have used GenEvent if it was not deprecated.
I have been thinking it would be possible to have a common dependency for all, eg LocalBusSystem, where engines would produces events, and web would consume those.
I am also curious of other people solution, as it seems to be a concern when using umbrellas.
Popular in Questions
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









