ivawzh

ivawzh

Contextual schema VS out-of-context schema (in Phoenix 1.3)

These two days I have been reading Phoenix 1.3. Learnt that the new generator by default puts schema inside contexts. After reading the post from @michalmuskala Putting Contexts in Context, I agree having context as module is a good idea. But is it worthy to have contextual schema?

I guess the benefit of putting schema inside context is similar to rom-rb which will have fewer couplings between database table and model schema. E.g. model schema can be just a partial of database table schema. And the disadvantage will be harder to define cross-context associations.

The best practice to define cross-context associations either is still in a grey area or will never exist (because YMMV). According to Putting Contexts in Context, the approaches to define cross-context associations will be:

  1. Don’t use join at all. “So, for example, instead of having an association, you’d only store the id. You can still access the data using the public interface of the other context.”
    Cons: N+1 queries and worse performance.

  2. “Having schema in each context reading from the same table (each having access to mostly different fields)”
    Cons: fragmented schema and no single source of truth (sounds like how spaghetti begins).

  3. “Having multiple tables that use the same primary key value (so you don’t have to keep a separate foreign key around).”
    Cons: dirty hack around.

Since performance is one of the major reasons that makes me choose Elixir & Phoenix, I will opt out option 1. And I personally hate option 3. So last standing option 2.

Therefore to me, contextual schema VS non-contextual schema is a tradeoff of more couplings between database table and model schema VS fragmented schema and no single source of truth. I personally hate fragmented schema and no single source of truth more. Hence, an out-of-context schema structure (or database-coupled schema structure) might be more handy for my general projects.

For example, if I am making a car dealer app, Phoenix 1.3 will give me contextual schema structure like this:

Contextual schema example

CarDealerApp
└── contexts
    ├── admin-edit-product
    │   ├── interfaces
    │   │   └── update-product
    │   └── schema
    │       └── product
    ├── explore-product-plans
    │   ├── interfaces
    │   │   └── list-plans
    │   └── schema
    │       ├── plan
    │       ├── product
    │       └── user
    ├── explore-products
    │   ├── interfaces
    │   │   ├── list-products
    │   │   └── show-product
    │   └── schema
    │       ├── product
    │       └── user
    ├── get-product-plan-quote
    │   ├── interfaces
    │   │   └── create-quote
    │   └── schema
    │       ├── plan
    │       ├── product
    │       ├── quote
    │       └── user
    └── registration
        ├── interfaces
        │   └── create-user
        └── schema
            └── user

In contrast, the out-of-context schema I propose will look like this:

Out-of-context schema example

CarDealerApp
├── contexts
│   ├── admin-edit-product
│   │   ├── changesets
│   │   │   └── to-update-product
│   │   └── interfaces
│   │       └── update-product
│   ├── explore-product-plans
│   │   └── interfaces
│   │       └── list-plans
│   ├── explore-products
│   │   └── interfaces
│   │       ├── list-products
│   │       └── show-product
│   ├── get-product-plan-quote
│   │   ├── changesets
│   │   │   └── to-create-quote
│   │   └── interfaces
│   │       └── create-quote
│   └── registration
│       ├── changesets
│       │   └── to-create-user
│       └── interfaces
│           └── create-user
├── schema
│   ├── plan
│   ├── product
│   ├── quote
│   └── user
└── validations
    ├── plan_must_be_avaliable_in_region
    ├── product_must_be_active
    ├── quote_expiry_date_must_be_before_plan_expiry_date
    └── user_must_be_vip

Basically, I would still keep changesets contextual because it’s very common that contexts require different fields, etc. On the other hand, I would abstract the specific validations out of context since often they will be shared across contexts. validations folder structure can be either flat or grouped by schema name, it can make sense in both ways and does not really matter.

What do you think?

Most Liked

ivawzh

ivawzh

I can see your point. I agree generator is less important to experienced users who know stuff inside out. However, if Elixir on Phoenix really wants to be popular/mainstream like Rails, I believe convention over configuration is the bottom line that should be aimed for. Some benefits:

  1. make junior developers productive,
  2. to convince boss Phoenix and Elixir are good tools. Because new employees can pick up existing codes real quick since codes all follow common patterns like in Rails.

I thought about putting basic type validation changesets in schema as well. I can reason with this approach because schema is the guy who defines data structure, in that way, it makes sense to let schema handle type validations. I did not do that because it might open a door to make developers think it’s okay to use schema like OOP model and put more lifecycles in schema changeset. However, if you trust in developers’ discipline, this won’t be a problem. Another minor downside, now you will have separate changesets in schema and context, a bit more indirections.

cevado

cevado

I dont understand why everything needs a generator. Why don’t let let people handle contexts as it fits better their applications. I think Phoenix doesnt need to handle database at all.

I would add to the schema a basic changeset(to ease cast_assoc) with the validations that are made in the database, not null fields, string sizes, things like that.

cevado

cevado

The problem for me is living in the shadows of Rails, one of the things that I loved on Phoenix 1.3 is leaving the model pattern. Making the contexts the new model wont change anything.

CoC wasn’t a thing when Rails appeared, I remember people saying that it wouldn’t work because they need to be in control of their application and things like that(I know it’s anecdotal evidence and it doesn’t mean anything) but that make sense to people which Rails was aimed for. The point is to define the kind of public Phoenix is aimed for. I think Phoenix shouldn’t be aiming to the same public Rails is aimed, people that uses Phoenix today are the ones that Rails doesn’t fit. The same way Elixir isn’t Ruby, Phoenix shouldn’t aim to be Rails and that means that it doesn’t need to aim for the same public. The same way that are a lot of gems that have their generators and do things that isn’t covered by Rails generators, maybe Phoenix should leave contexts loose and see where the community goes and that doesn’t mean that Phoenix isn’t doing CoC just means that it isn’t omakase… :wink:

Where Next?

Popular in Questions Top

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
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
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
mgjohns61585
Could someone help me? I’m making my first elixir program, number guessing game. I can’t figure out how to convert the user’s guess from ...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
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
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
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
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
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
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
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
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
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement