I’d like to announce our second open source Elixir library—Mutiny. It helps ensure that records you consider immutable from a business perspective behave as such in your database.
To start using Mutiny, simply add it to your mix.exs
dependencies:
def deps do
[
{:mutiny, "~> 0.1.0"}
]
end
Let’s have a look at how Mutiny works in practice. Consider the following example.
You’re building an event sourcing feature into your Postgres-backed Elixir app, so you create a table using an Ecto migration that looks something like:
defmodule MyApp.Repo.Migrations.CreateEvents do
use Ecto.Migration
def change do
create table("events") do
add :type, :string
add :data, :map
add :last_viewed, :utc_datetime
end
end
end
You go on to begin capturing events, comfortable in the knowledge that you can use them in the future to reconstruct arbitrary application states. You are delighted with your work and move on to the next thing.
But then doubt begins to cloud your mind… If any one of the records in the events
table are modified, the entire system you’ve built could be compromised. Sure, you’ve written application-level checks to ensure that event data isn’t changed once recorded, but your gut tells you need deeper assurance. Listen to your gut.
It’s common practice that crucial data be validated at the application level and at the database level. Mutiny is here to assist. It allows you to protect certain database tables or columns from updates by calling simple functions in your Ecto migrations. Let’s revisit our original migration, this time incorporating Mutiny:
defmodule MyApp.Repo.Migrations.CreateEvents do
use Ecto.Migration
use Mutiny, adapter: Mutiny.Adapters.Postgres
def change do
create table("events") do
add :type, :string
add :data, :map
add :last_viewed, :utc_datetime
end
end
table("events")
|> protect([:type, :data])
|> execute()
end
Now, the type
and data
columns are immutable—that is to say, unable to be UPDATE
d once INSERT
ed. Any update commands that would modify those columns will now result in a database-level error. Thanks, gut!
Currently, PostgreSQL is the only supported database, but the library is designed in such a way that adding support for other databases, e.g. MySQL, SQL Server.
Check the docs for even more examples and, of course, feel free to open bug reports or enhancement PRs on GitHub.