noam87

noam87

Should we Separate Ecto From Phoenix in Umbrella App?

Hey, so we have an umbrella app, where the API is served by a Phoenix app, and then kick off a bunch of apps to perform application logic. The result from those calculations needs to be stored in the DB as well. So, for example:

  • User calls API and updates some information on his profile.
  • The data is stored in DB.
  • Another app in the umbrella is kicked off to perform some aync job (collect more data from external API, run some calculations, etc.)
  • Result from those calculations need to also be stored in the DB, but that module does not have the Repo as a dependency.

So I’m considering either:

  1. Separating Ecto from Phoenix, and have a “DB” app in the umbrella that handles all the db stuff, and then including that app in both the API and any other modules that need to talk to the DB as a dependency.

  2. Have the modules send a message back to Phoenix API that called it with its result, and then have the Phoenix app handle all db requests.

Most Liked

christhekeele

christhekeele

I’m experimenting with this now, and liking the following setup:

  • mix new project --umbrella
  • cd project/apps
  • mix new db --sup --module Project.DB
  • mix new app --sup --module Project.App or what have you for other otp apps
  • mix phoenix.new site --module Project.Site
  • Remove postgrex from site deps and applications, leave phoenix_ecto
  • Add {:db, in_umbrella: true} to site and other app deps, also add :db to the application :applications config in their mix.exss
  • Add ecto and postgrex to db deps and applications
  • mv site/lib/site/repo.ex db/lib/db/repo.ex
  • Rename all occurrences of Project.Site.Repo to Project.DB.Repo
  • mv site/priv/repo db/priv
  • mv site/web/models db/lib/db
  • Mirror phoenix’s config/<env>.exs files setup in db
  • Copy the /priv/static/ and /config/prod.secret.exs rules from site/.gitignore to db/.gitignore
  • Move ecto_repos configuration to db/config.exs
  • Move the repo adapter config in each site config/<env>.exs file to db
  • Make sure the "phoenix" and "phoenix_html" filepaths in your site/package.json’s "dependencies" reference "file:../../deps/<dep>"
  • Stop using phoenix model generators, use mix ecto.gen.migration -r Project.DB.Repo from the db app
  • Freely access models, queries, etc from your other apps as Project.DB.<Model>

I think that’s about everything necessary that I did.

Further optional conveniences:

  • Rename databases from "site_<env>" in these adapter configs to "project_<env>"
  • Delete def model... from site/web/web.ex
  • Make a db/lib/db/model.ex with a __using__ macro with the quote from your Project.Site.Web.model you just deleted
  • Replace all occurrences of use Project.Site.Web, :model to use Project.DB.Model in db/lib/db/models
  • Move site/mix.exs’s ecto aliases to db/mix.exs (optionally under a new db namespace instead of ecto)
  • Move site/mix.exs’s test alias to the umbrella app’s mix.exs
  • I removed all of the references to Project.DB.Repo in site/web/web.ex and other Ecto stuff and instead made it so that you can use Project.DB.<Model> to get aliases for both that model and the repo, then used that instead atop my controllers and channels. Ditto with my Project.DB.<Query>s.
  • I changed all my otp app names to be prefaced with “project”, which requires changes in mix.exss, config/ files, db/lib/db/repo.ex, site/lib/site/endpoint.ex, and site/web/gettext.ex. You also have to rename your folders in the apps folder to follow the otp name, prefix and all, to avoid weird umbrella app and phoenix hot code reloading issues.
  • I experimented with moving everything from site/web into site/lib/site. You have to remove "web" from the site’s "elixirc_paths" and rename references to the web dir with lib/site in brunch stuff, and change the root of your templates within your site/lib/web.ex directory. However, this destroys hot code reloading so I can’t say I recommend it.
michalmuskala

michalmuskala

That is my preferred way of structuring apps as well. At least two apps in an umbrella - a domain one and an api one. First handles all the persistence and knows nothing of https, the second one calls plain function from the first one and knows nothing of ecto or any other persistence mechanism.

10
Post #3
noam87

noam87

Update: so yeah we did end up separating Ecto from Phoenix, and it was easy to do and easy to work with. There’s only some mild annoyances when running generators and stuff like that.

Now building my second Elixir project the same way. This time it’s a GraphQL API (using Absinthe) so there’s not even an HTML layer or controllers, making Phoenix a very thin layer for organising plugs and GraphQL schemas in a standard way really.

Here’s my short cheat sheet for starting projects this way: Redirecting...

Where Next?

Popular in Questions Top

WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
Lily
In templates/appointment/index.html.eex: &lt;%= for appointment &lt;- @appointments do %&gt; &lt;tr&gt; &lt;td&gt;&lt;%= appoi...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: The documentation above suggests that while ...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
yawaramin
In the Dialyzer docs ( dialyzer — OTP 29.0.2 (dialyzer 6.0.1) ), there is a way to turn off a specific warning for a function: -dialyzer...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

Other popular topics Top

malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 41539 114
New
Nvim
Anybody knows a comprehensive comparison of Django and Phoenix, thanks for the help. Where are they similar? Where do they differ the m...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
AstonJ
Please see the new poll here: Which code editor or IDE do you use? (Poll) (2022 Edition) It’s been a while since we first asked this, I...
208 31142 143
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36128 110
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement