apog
I’ve been making a basic todo-list application to learn phoenix and i’ve been looking through the forums for answers on how to properly design a phoenix application. The consensus seems to be to only make calls to the database through Repo in either the controller or some separate service object dedicated to that task. But there doesn’t seem to be a consensus on a design pattern for getting such data to other modules that need it.
In my case I have a genserver (called Todo.Server) that has a specific name and keeps its own list of todo-items in it’s state. It’s started from a dynamic supervisor. My issue comes comes in here: when Todo.Server is started I check the database for it’s initial state. When an entry is added I have it persist to the database as well as update it’s local state.
From what I’ve been seeing this is a bad practice for a phoenix app, and it sounds like the convention is to have that initial state passed in from a higher level. But this makes things more complex. I would then have to first find out if a server with the given name is already running (since it only reads from the database on startup, otherwise it uses it’s local state), and if not then make a call to Repo to get the data, and then pass it all the way down to the server. And where would that logic live? That logic seems very out of place for a controller. Does this mean I should make a separate service object responsible for getting the data needed for the dynamic supervisor and the Todo servers and pass it to them through the controller?
I’ve been having a very difficult time figuring out how to properly handle such a situation in phoenix, any help would be greatly appreciated!
Trending in Questions
Other Trending 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
- #blog-post
- #phoenix_html
- #iex
- #graphql
- #ai
- #genstage
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex











Showing Posts 1 to 10- Show Best Posts
- Show All Posts (oldest first)
- Show All Posts (newest first)
peerreynders
If I understand you correctly it sounds like Ecto was installed as part of the Phoenix project setup.
The “pattern” that you seem to be looking for would make Ecto part of the Todo application - not Phoenix.
The “pattern” is demonstrated in
hangmanis the equivalent to your Todo application.gallowsis the Phoenix based web interface forhangman. Nowhangmandoesn’t use a database - but if it did Ecto would be part ofhangman- notgallows.Therefore your Todo application should be designed to use Ecto (or whatever other persistent storage you use) even before Phoenix get involved.
You list Elixir in Action 2e as one of your books. If you look at the Chapter 11 example you have:
Todo.Serverwhich directly depends on the “Database”.Todo.Server(andTodo.Cache).Now the big difference here is that it’s organized as one single
Mixproject. Thegallows/hangmanapproach would organizeTodo.ServerandTodo.Databasein a separate OTP application (i.e. separateMixproject) that can then be used as a dependency forTodo.Web(in a differentMixproject).Similarly a Phoenix application could simply use a “Todo OTP Application” (that uses
Ectointernally) as a dependency without directly getting Ecto involved. Meanwhile the Phoenix project acts as “the application” that starts everything up but the “Todo Application” is responsible for managing its persistent storage (e.g. throughEctoor possibly yet another OTP application).However that is probably the most complicated way of using Phoenix.
You can build “Phoenix is your application” style applications where simply each request to the web server initiates some interaction with the database that results in a response.
The next level of refinement is to use “Phoenix contexts” - i.e. organizing code into domain/business (i.e. context) specific modules rather than simply leaving all the code in the various controllers.
For even better separation there are umbrella projects which allows multiple OTP applications to run under the same configuration.
Finally the
gallows/hangmanapproach which relies on bare path dependencies (which maximizes decoupling but makes many things less convenient (tradeoffs …)).kokolegorille
There is nothing wrong doing a todo list in phoenix without using gen_server, for example using data from db.
IIRC there is no ecto involved in the todo list of Elixir in Action.
In case You want to do both… for example having a gen_server loading state from db, there is a recommandation, try to have the quickest init possible. You can achieve this like that.
What would be a service object in FP?
david_ex
Note that if you’re using OTP >= 21, you can use
handle_continueto avoid race conditions when deferring your initialization:The advantage of this is that when using named processes, it prevents a message from being processed after
initfinished, but before the:set_statemessage is handled. Usingcontinuewill ensure the code to finish initialization is run before accepting a new message from the mailbox.More info here.
Note that if you want to be able to
@impl ...thehandle_continue/2function, you need to have Elixir >= 1.7apog
Thank you for all of the feedback! Yeah my todo application is based off of the one from Elixir in Action 2e and I was trying to modify it to make use the phoenix framework. But it looks like I am currently building phoenix as my application and I need to simply view it as a web interface (this is very new to me coming from a rails background) and keep the Todo app as it’s own separate thing. If I am understanding what you are saying, there is nothing wrong with me making a call to Repo from directly within my Todo.server genserver rather than having that be passed in?
apog
The gen_server was for efficiency since after it’s started I can get data from its state rather than hitting the db every time. And yeah Elixir in Action just uses file IO as the database, but I modified the project to see how adding a relation database would work. And I should have said ‘service module’ instead of ‘object’. What I meant was a module dedicated to making calls to Repo for data. (i.e. if I had some complicated query for getting a combination of lists, it could live in the service module rather than the query happening directly in the controller)
kokolegorille
Which is what contexts are made for
apog
ah, i’m still getting the terminology down. I think contexts are what I mean. So I guess my question boiled down to whether I should get the data from within a context and pass it to the genserver through the supervisor, or if it’s okay to just get the data from directly within a genserver. It’s also just confusing that the built in generators for phoenix go against the recommended design of an application. For example, based on what peerreynders said above, I wouldn’t want any of this to live in the phoenix app and so the phx.gen.context command would actually be guiding me in the wrong direction.
kokolegorille
Not really, it creates the context in the module connected to ecto.
If You create an app, You will have app, and app_web. And contexts are generated app side.
Phoenix still is an interface for your application, separated from your business logic.
peerreynders
It depends a bit on the architectural style that you are using.
“Phoenix is your Application” (kinda “Rails-style”) wouldn’t bother with caching the todo list in a process and would interact straight with the database. In memory caching isn’t always a total win (unless the data is entirely ephemeral).
The Elixir In Action 2e “Database” uses the file system - but for all
Todo.Serverknows it could be usingEcto/PostgreSQL. With that in mind there is some value in hiding the details fromTodo.Serverbehind aTodo.Databasemodule which is the only one who knows aboutEcto, the Repo and the queries. Most people are not willing to go to that extreme as it cuts them off from the functionality inEcto.Changesetfor data validation.apog
Well part of my question is trying to figure out what architectural style to use. I was hoping there was some sort of convention in the phoenix community around where to access the database and was the general structure should be.
For your second bullet, why wouldn’t you still be able to use
Ecto.Changesetfor data validation? The database module would still check the validity of the data before calling repo to persist.