tielur

tielur

Phoenix Contexts - is it common to preload all resources on the context methods with whatever everything needs access to?

Assumptions:

  • All Repo calls are isolated to the context’s within your application

I have lots of context methods that several parts of my phoenix applications use. Depending on what I’m doing with resources I may need different relationships preloaded on it. Is it common to just preload all resources on the context methods with whatever everything needs access to?

Or are people using Repo.preload outside of context’s to load up the additional data they may need when and where they need it?

What I have right now is lots of data that is being queried and loaded and sometimes that data isn’t even being used. A few alternatives I’ve thought about over the past few weeks:

  • Add additional public functions for each different way data is being used. I’m not a huge fan of this idea only because the surface area of the public API into your system becomes large. Though it’s tame-able with using ecto composable queries to reuse a lot of internal building of the queries.

    • Users.get_user(id)
    • Users.get_user_with_accounts(id)
    • Users.get_user_with_accounts_and_posts(id
  • Expose public composable functions that the “clients”(code needing the data) put together with whatever data they need. This is nice in that the clients are able to ask for only what data they need, what I don’t like is now you need to pass the query back to the context for it to execute ( assuming that all Repo calls are isolated to the context modules).

    • id |> Users.with_id() |> Users.get()
    • id |> Users.with_id() |> Users.with_accounts() |> Users.get()
    • id |> Users.with_id() |> Users.with_accounts() |> Users.with_posts() |> Users.get()

    Where with_id/1, with_accounts/0, and with_posts/0 are all composable queries that return the ecto query and Users.get/1 would take a query and basically call Repo.one

  • I haven’t looked that far into GraphQL but it would be interesting to see if that concept where the client could ask for what it needs and the server would just hand it back could be brought into this situation. Where my “client” another module could give a graphql query to my context and it would give back what it’s asking for. I’m not sure if this is possible now?

How are you handling this in your elixir applications?

Most Liked

josevalim

josevalim

Creator of Elixir

Yes. However, keep in mind you can always preload after the fact too, so you can do this:

User.get_user!(...) |> Blog.preload_posts_for_user(...) |> Other.preload_another_for_user(...)
tielur

tielur

Another idea would be to allow the passing in of the Keyword list of preloads into the public context methods. Something like:

def get_user(id, preloads \\[])
  Repo.get(User, id)) |> Repo.preload(preloads) 
end

So I could get the accounts with:
Users.get_user(id, [:accounts]

or the accounts and posts with:
Users.get_user(id, [:accounts, :posts])

LostKobrakai

LostKobrakai

I feel like managing preloads or more generally flexible querying is certainly one of the more complex parts related to contexts. I’d also say you’re likely to not get black or white answers to the options you gave, but it’s probably more of a “use a solution that suits the requirements”.

One idea I want to propose is of having an aggregate root. This would mean a context does only handle data from one or few root datatype(s), like e.g. an Order, while access to child data (like e.g. OrderAddress or OrderLine) is transparently handled by the context either by upfront loading everything or loading on demand. From the callers perspective there’s nothing extra to do. This is probably the most simple and sane approach.

Your options about having an API for the caller to select what’s needed to be preload doesn’t really seem desirable to me. E.g. why should Users know about posts (besides that I don’t really like Users as a context name). A more domain focused api would rather look like:

user = Accounts.get_user(id)
posts = Blog.get_posts(user)

Given that I’d refrain from preloading in ecto queries in favor of preloading via second queries as much as possible (preloads and filtering often don’t go well together) this will result in two queries to the db just like a single call to a domain api would as well.

The last part I want to cover is flexible querying of data in general. There you have basically two options, like you seem to have already discovered based on your proposed solutions:

  1. Find a way to have a custom data structure represent the querying options available:
    This can be a function name or e.g. a keyword list/map passed as additional parameter.
  2. Have a composable API using the builder pattern, which builds up a data structure (personally I’d directly build up an ecto query) to be supplied to various context functions for actually querying the database. Given that both MyApp.Repo as well as MyApp.Context kinda implement a repository pattern it’s no wonder that the APIs might be similar even though on vastly different abstraction levels.

Also I’d like to leave this here, which might clear up what I don’t like about a Users context and how to make potentially very big context files smaller: http://devonestes.herokuapp.com/a-proposal-for-context-rules

Where Next?

Popular in Questions Top

_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
Kurisu
For example for a current url like http://localhost:4000/cosmetic/products?_utf8=✓&query=perfume&page=2, I would like to get: ...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
JulienCorb
I am trying to implement my new.html.eex file to create new posts on my website. new.html.eex: <h1>Create Post</h1> <%= ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
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
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New

Other popular topics Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29603 241
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
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
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
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36352 110
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 48342 226
New
AstonJ
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement