Just launched! Get 30% off Rails Revisited Have a Look

Working With Ecto and PostgreSQL

Buy or Subscribe

You can access this course in just a minute, and support my efforts to rid the world of crappy online courses!

Buy Standalone  Subscribe

An ORM? In a functional language? Wait a minute...

Yes, it's an argument you'll likely sit through a few times in the Elixir community. I certainly have. I have a strong opinion on this (which will become obvious in a second) but overall here's the thing:

  • you need to store data
  • we're using a relational database to do so, which Ecto (the data tool created by the Elixir team) supports
  • that data is structured in some way

The Rub

Here comes the opinion... ORMs in the object-oriented world are plagued by issues relating to object-oriented programming. All of that centers around the idea that a relational database, as a concept, does not share the same general fundamental principles and concepts that object-oriented programming does. This is the "impedance mismatch" you'll hear about if you dive into the subject long enough.

It comes down to this: _there are things you can do with for loops, conditional branching, polymorphism, inheritance and is-a / has-a that are simply not part of the database vernacular. The whole idea of "lazy vs. eager loading" is part of this (how deep do you go to load up a related record, for instance).

My opinion is that functional programming is no immune to this problem of impedance mismatch. You'll hear Elixirists proclaim loudly that "there are no objects in Elixir!" while happily pumping data into structs with defaults set and a structure implied. Immutability does not mean something is not an object. This leads me to the final point...

One of the most difficult problems with using a relational database as a data store is that you have to map the data from the store to the structure (let's not kid ourselves it's an object). You have to resolve the database types (text, text, bigint, date, date with a timezone) to whatever the corresponding type is in the language. This is the next problem to solve.

How do you map a PostgreSQL JSONB field to a field on a struct? That's right: you map it somehow.

So: that's the issue we have to deal with. Ecto is an ORM that doesn't think it's an ORM and the team steadfastly refuses to believe they are subject to the same issues as an ORM.

I could be wrong, they could be right. I've written 3 ORMs in my career and I'll never do it again. I have scars to prove it. I feel each of those scars flare up when I use Ecto...

OK - opinion aside... let's get to it...

Data Time!

You've got the basics down, I think. You might have to do some Googling but the time to make our move has come.

It turns out that the CTO, Sara, has tasked some developers to build a Physics library using .NET! We have a meeting later today to discuss the future of the Science Program with the board and they want to see our work!

I need this space station operational!

Let's get to work.

Understanding Persistence and OTP

Let's take a look at our SolarFlareRecorder using an Agent one more time:

defmodule SolarFlareRecorder do
  use GenServer

  def start_link do #example 1
    Agent.start_link fn -> [] end
  end

  #public API
  def record_flare(pid, {:flare, classification: c, scale: s} = flare) do
    Agent.get_and_update pid, fn ->
      new_state = List.insert_at flares, -1, flare
      {:ok, new_state}
    end
  end

  def get_flares(pid) do
    Agent.get pid, fn(flares) ->
      flares
    end
  end
end

In the last chapter I mentioned I would be coming back to the idea of persistence with our Agent. Now that you know a little bit about OTP and how Agents work - how would you handle data persistence with our SolarFlareRecorder? Let's think about this for a second before we dive in.