tomekowal

tomekowal

Modelling application core with relational entities

I’ve recently finished Designing Elixir Systems with OTP. It is an excellent book, and I highly recommend reading it!

Modelling the logic with pure data structures without using the DB is central to the architecture. In many cases, the core/entities have a relational structure. There is an excellent talk about it by Richard Feldman (the example is in Elm, but the same rules apply).

The problem is many to many relationships. An example from the above talk a student can attend many courses and courses have many students. In such situations, Richard recommended using dictionary (map in Elixir) id => %Student{}

That seems like a pretty clean idea, but I am not sure what to do with new records. Let’s say I want to add a new student to the course. I should generate a new id for that record. However, I should decouple the model from the DB, so I shouldn’t use DB id. But what should I use then? Other ids might be integers from the DB, and I don’t want to clash.

My end goal is a function like save(old_model, new_model) that checks what changes need saving and performs the writes.

I have a couple of ideas, but neither is very compelling.

Idea 1:
Using something like {:db, id} for stuff read from the DB and {:new, id} for new stuff. That makes the save function trivial. The downside is that I am leaking persistence details to the model/core.

Idea 2:
Introduce ids that are unrelated to DB. E.g. a student id might be a string "student-1". That would be unrelated to the DB id. When reading a particular set of courses, I would need to translate student.id: 237 to "student-1", student.id: 250 to `“student-2” and so on. That should work, but I need to keep the translations dictionary somewhere in case I need to update the student.

Do you have any other ideas for persistence? Or maybe an idea of using a map is not a good one, and I should the relationships differently?

Marked As Solved

JEG2

JEG2

Author of Designing Elixir Systems with OTP

My instinct is to favor ID’s unrelated to the database. It might be that you could get by with something as simple as :erlang.unique_integer/0, but my first choice would probably be to use UUID’s.

PostgreSQL supports using UUID’s as the primary key for a record. You can generate a UUID in your application and save it with the other fields. Put another way, you don’t have to have the database auto-assign the primary key (though it certainly will do that when desirable). So I don’t think any additional mapping would be required, if I’ve understood the constraints correctly.

Last Post!

tomekowal

tomekowal

Thank you! Yes, it looks like a helpful solution. This way core model does not need the DB for generating IDs and doesn’t require mapping.
After watching Richard’s talk, I thought integer keys are the only option. It never occurred to me that pairs of UUIDs can encode many-to-many relationships as good as integers.

Where Next?

Popular in Questions Top

hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
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
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
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
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
marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New

Other popular topics Top

jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
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
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 40165 209
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
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

We're in Beta

About us Mission Statement