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? ![]()
Most Liked
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
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
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.
Popular in Discussions
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
- #code-sync
- #javascript
- #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









