View Source Database DateTime Column Types

We made some non-default choices regarding the database column types used for datetime values.

Status: Complete (March 7, 2023)

problem-context

Problem Context

While generally poor form, this decision documents two distinct problems and two independent code edits (happening in quick succession) that provided solutions.

First, during development, we were reminded that the main reason Ecto chooses NaiveDateTime for its default implementation of timestamps() is for compatibility reasons. For a modern Elixir project using Postgres, it is more expressive to use the :utc_datetime format for those timestamps to end up with DateTime values over NaiveDateTime values in the Elixir code, which better codifies the UTC nature of these values.

Second, as we continued to expand our event-sourced code and tests, we observed a common code pattern where we were truncating DateTime values to the second. This would usually pop up as we would use DateTime.utc_now/1 to generate a published_at value and then attempt to use it inside a CreateArticle command test case. As the command generated events, and then those events persisted, a projection of the DateTime value was being truncated in the database because we had initially created the published_at column using :utc_datetime, lacking the fidelity of microseconds. Later in the test, we would try to assert the value from the database projection vs. the value we generated in memory, but without the microseconds, it did not match.

decision-made

Decision Made

For the first problem, during pull request #171, we updated our database and Ecto schemas to use a :utc_datetime column type. This resulted in a more consistent value type of the DateTime now for inserted_at and updated_at fields in addition to the previous published_at field.

For the second problem, we again edited those migrations during pull request #172, now changing the column type from :utc_datetime to :utc_datetime_usec. While we do not have a business need for microsecond precision, the event tooling provided by the Commanded and related projection libraries prefers microsecond precision. Moving forward, it feels easier to use microsecond precision inside Franklin's domain contexts and be consistent across the board. Overall, this change resulted in more straightforward code.

consequences-tradeoffs

Consequences & Tradeoffs

  • We felt confident editing historical migration files because the app is not yet deployed.
  • Adding this extra data has a database space cost. For the constraints of this simple app, it will be negligible, but if this were a more significant application, we would have to consider the size increase for N number of projected rows,