habutre
Application communication best practices
Hi all!
This is my very first question here!
I was playing a bit with Phoenix and Elixir in a solution where I would like to use Phoenix as a client application which will provide solely a web interface for backend services allowing in future using other interfaces such as CLI or integration with other technologies through messaging or http.
Between Phoenix and Backend Elixir communication I would like ideally to take advantage of Erlang/Elixir ecosystem and make that communication through processes and I decide to use Genserver for that.
What I did was define the callback handle_call in the backend service and on client I wrap the Genserver calls in a MyApp.BackedEndApp.Client module which uses purely Genserver.call to invoke the backend functions.
I had received some critics from my team members about that approach. What could be a best practice in that case when I want to keep client and server decoupled in the same time I want to take the ecosystem advantages? I mean without HTTP/Rest or Messaging communication
Marked As Solved
joaothallis
Is there any reason why the web code has to stay in another node (separete machine)?
If the codes can stay on the same machine why not call the function directly? For me this is the best practice and the simplest.
One option to decouple and use the same machine is to use RabbitMQ or something, but for a simple API I understand it as a bad practice.
If you would you like build microservices was some approaches: http://www.ubazu.com/microservice-approaches-in-elixir
It’s worth seeing: http://www.ubazu.com/decouple-your-elixir-application-from-phoenix-and-ecto
Also Liked
al2o3cr
Have you considered using… nothing? Let’s walk through a typical Phoenix + Ecto application to explain what I mean.
(h/t to @jola, this is a long version of that earlier comment)
There are two big clumps of processes running: database connections and acceptors.
The application’s Repo is a single process, started at boot, and manages a pool of DBConnection processes, keeping track of which connections are checked out and handling failures.
A Phoenix endpoint uses Cowboy (and ultimately Ranch) to start a bunch of “acceptor” processes which (as the name might suggest) accept incoming TCP connections and start a new “handler” process that does the actual work (here’s a diagram and additional discussion).
That process handles running the per-request Phoenix code as well as user-defined plugs and eventually calls the specified function in the controller. When that code calls functions like Ecto.Repo.insert, Ecto checks out a connection - borrowing it from the DBConnection process that holds it when idle - and interacts with the database.
Notably absent here is a process boundary: some of the code that gets executed is defined in modules within the Phoenix “app” (routing, plugs, controllers), and some comes from the Ecto “app” (schemas, changesets) but all of it executes within that single “handler” process created for the specific HTTP request.
This is what the GenServer docs mean by “use processes only to model runtime properties, not code organization”. The runtime property desired here is per-HTTP-request concurrency.
On the other hand, having an EctoApp GenServer that every HTTP request interacts with would result in a bottleneck at that GenServer’s message queue.
So “nothing” at the beginning of this post is a little hyperbolic, but “a function call” is pretty close. The BEAM has a lot of powerful architectural features, but it’s vitally important to understand the philosophy underpinning them to use them correctly. GenServers != microservices
habutre
You got my point, since I came mostly from Java world where the lately there is a big movement towards microservices and event-driven architectures I tend to use more or less the same approach with Elixir.
Since I didn’t get constructive driven-by-example feedback from my aforementioned colleagues I end up here trying to find out the Elixir-way to write code that is decoupled, distributed and/or isolated among contained services holding mainly business logic.
I really appreciate all points that people brought me here and help me a lot. Thanks @kokolegorille @jola @peerreynders @axelson @al2o3cr and @joaothallis for spend your time to help me on my journey to become a good Elixir Dev.
jola
Based on your description I would recommend against GenServer. The thing is that the way Phoenix works is that it spawns a process for each request that comes in. This allows it to scale nicely over multiple cores. Assuming you start a single process to handle all the work you are effectively turning your app single-threaded. You’d get around that if you create a pool of processes etc, but that’s a lot of effort for what gain?
I would not use GenServer for this. Just create a client module and call those functions. This means your business code runs in every process spawned for requests, the way it is intended, and you still get the separation of concern and decoupling that you’re interested in. I don’t understand why you’d have two apps speaking over “HTTP/Rest” if all you’re concerned with is decoupling.
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
- #javascript
- #code-sync
- #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








