How do you model different ‘types’ with totally different fields in Ecto?

Thanks everyone for the responses!


Context / why I asked

While learning Ecto / Phoenix / LiveView, I ran into this modeling dilemma and was trying to understand the different layers at the same time.

Before the full mental model was really in place, I was jumping between different layers due to the learning process.


Why I didn’t immediately propose my own solution

I also explored the direction linked by LostKobrakai earlier (separate types, separate structures), but what held me back was:

  • at the database level, I modeled this using composite-key / multi-field identity approaches
  • in Ecto, this is not really supported in a native or idiomatic way
  • so it wasn’t clear to me whether this approach is actually considered idiomatic in an Ecto context

Ecto vs data modeling

What I found interesting is that Ecto + LiveView changesets work really well together, especially for dynamic forms.

If I completely drop Ecto, I don’t just lose an ORM, but also a very useful validation + UI integration layer.

At the same time, it became increasingly clear to me that the database model and the Ecto schema are not the same thing.

At the database level, you can have a perfectly valid relational model (e.g. composite-key / subtype-style structure), which Ecto doesn’t necessarily map in a natural way.


Concrete example (simplified)

schema "items" do
  field :type, Ecto.Enum, values: [:item_one, :item_two]

 has_one :item_one
 has_one :item_two

end

And the more specific schemas:

@primary_key {:id, :id, autogenerate: false}
schema "item_one" do
 field :type, Ecto.Enum, values: [:item_one], default: :item_one

 belongs_to :item, Item
end
@primary_key {:id, :id, autogenerate: false}
schema "item_two" do
 field :type, Ecto.Enum, values: [:item_two], default: :item_two

 belongs_to :item, Item
end

@sodapopcan

This is a really interesting approach — I find the idea of abstracting the association into a single “slot” quite compelling, especially as the number of possible underlying tables grows.

In my case the domain already tends toward multiple item types, so avoiding a growing number of empty or optional has_one associations is exactly the kind of problem I was running into.