katafrakt

katafrakt

What goes into your Ecto schema modules?

By default Phoenix generators, when creating an Ecto schema, puts a schema definition there (obviously) and a changeset function. I wonder what else do you put in your schema modules and what unwritten rules do you have for that.

I sometimes add functions that somewhat hide the details of underlying data storage, especially in regards to optional associations or weird choices (I have a DB where some boolean fields are represented C-style as 0 or 1 integers, for example).

def premium_package_name(user) do
  case user.premium_package do
    nil -> nil
    package -> package.name
  end
end

# or

def finished?(session) do
  case session.is_finished do
     0 -> false
     1 -> true
   end
end

I try not to put much more in schemas and keep them relatively minimal.

However, I know some people put functions returning Ecto queries there. I’m not a fan of that. It feel weird that a schema needs to know how it will be queried. But on the other hand I cannot totally reject this approach. If we treat schema module as the place that hides database complexity form the outside world, putting a query there (for which you need to know the database intricacies) makes some sense.

def latest_by_user(user_id, limit \\ 10) do
   from(o in Order, where: o.user_id == ^user_id, order_by: [desc: o.created_at], limit: limit)
end

Maybe someone thinks changeset does not belong in schema? That would be an interesting discussion… So what would I find in your schema modules and why? :wink:

Most Liked

katafrakt

katafrakt

I’m usually finding the opposite. The amount of queries grows almost infinitely in time, so I prefer to keep them in a separate module, where they don’t obscure other purposes of the unit of code. Even worse, the queries are usually more or less directly coming from UI requirements and to me it feels extremely weird to have UI dictate how the module closes to persistence looks like.

This can be partially addressed by keeping the query parts in schemas very generic and atomic. After all, composability is one of the main strengths of Ecto. But in that case you need some place when you stitch together the atomic query functions into something larger. I guess that would be a context. But having too many UI-driven query functions in context is a whole another discussion I’d like to have one day.

But yeah, I get your point.

eahanson

eahanson

I put my schema modules in a top-level namespace called Schema (for example, Schema.Profile). They contain just the schema definition and a @type.

And then, I have a core module (for example, Core.People.Profile*) that contains changeset functions as well as a Query submodule that has composable query functions, which are assembled in context functions.

I use Boundary to separate my Core modules from my Web modules, but because both of them need to access schemas, I let both of them access the Schema modules. I’m okay with that because the Schema modules don’t contain any code.

It might sound like a pain to have the schema and changeset functions in different files, but I wrote an editor plugin to jump between related files easily so it hasn’t been an issue for me.

*The Phoenix generators create top-level modules called MyApp and MyAppWeb; I rename them to Core and Web which has the advantages of being shorter and being easier to copy between projects.

krasenyp

krasenyp

I have a separate top-level module MyAppStorage. It exposes functions for querying and manipulating data which don’t return Ecto schemas. The schemas are only used internally. This way I keep full flexibility in the way I persist data. I can optimise as much as I want. At the same time my business entities don’t have to mirror my database tables’ structure.

Where Next?

Popular in Discussions Top

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 31265 143
New
mbenatti
Following https://github.com/tbrand/which_is_the_fastest |> https://raw.githubusercontent.com/tbrand/which_is_the_fastest/master/imgs...
New
AstonJ
I’ve just started the Phoenix part of the utterly brilliant online course by @pragdave. On generating the Phoenix app he uses the --no-ec...
New
New
WildYorkies
It seems that the more I read, the more I find Elixir users speaking about all the ways that Elixir is not good for x, y, and z use cases...
New
tmbb
This is a post to discuss the new Phoenix LiveView functionality. From Chris’s talk, it appears that they generate all HTML on the serve...
342 18243 126
New
opsb
We’re considering our architecture from a viewpoint of scaling our traffic heavily over the next 6 months. Our current deployment is runn...
New
pillaiindu
I want to convert a Phoenix LiveView CRUD website to a CRUD mobile app. What do you think is the easiest way to do so?
New
arpan
Hello everyone :wave: Today I am very excited to announce a project that I have been working on for almost 3 months now. The project is...
New
shishini
I think this twitter post and youtube video didn’t get as much attention as I hoped I am still new to Elixir, so can’t really judge ...
New

Other popular topics Top

rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
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
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
jaysoifer
Is there a way to rollback a specific migration and only that one (“skipping” all the other ones)? Would mix ecto.rollback -v 200809061...
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
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
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
Lily
In templates/appointment/index.html.eex: <%= for appointment <- @appointments do %> <tr> <td><%= appoi...
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement