Fact is an Elixir library that provides a file system based event store database.
- Traditional event sourcing capabilities:
- Append events to event streams
- Read events from specific event streams or all events in the event store
- Subscribe to specific event streams or all events in the event store
- Optimistic concurrency control
- Compliant with the Dynamic Consistency Boundary specification
- Read events from built-in indexes and custom queries.
- Subscribe to built-in indexes and custom queries.
- Just-in-time indexing for event data queries.
- "Pseudo-WORM" storage1
- Supports multiple instances for siloed isolation in multi-tenancy setups
- Configurable Content-Addressable Storage (CAS)
- Configurable event schemas
- Supported on Elixir 1.13+ and OTP 25+
- User guides
- Backup task
- Restore task
- Data tampering verification task (for CAS)
- Custom Indexers
- Telemetry
- Merkle Mountain Range
- inclusion proofs, this event exists in the ledger at position N.
- doesn't prevent tampering, but proves it did or didn't happen
- Needs one of these 🤔:
- signed checkpoints
- cross-system anchoring
- client-held receipts
- Proof of scale
- Target: 1M events per day, <= 86.4 ms per write, 11.5 events per second
- Honestly not trying to build for global scale, if that's what you need use Axon, Kurrent, or UmaDB
- Full stack example application
- A network protocol to enable non-BEAM based languages to interop.
- A gossip protocol to coordinate multiple BEAM nodes
- Graphical user interface to manage and operate the database (like a pgAdmin or Sql Server Management Studio)
The package can be installed by adding fact to your list of dependencies in mix.exs:
def deps do
[
{:fact, "~> 0.2.0"}
]
endThen create a database instance.
$ mix fact.create -p data/turtles# Start a database instance
iex> {:ok, db} = Fact.open("data/turtles")
# Create an event
iex> event = %{
...> type: "egg_hatched",
...> data: %{
...> name: "Turts"
...> }
...> }
# Append the event to a stream
iex> {:ok, pos} = Fact.append_stream(db, event, "turtle-1")
# Read the event stream
iex> Fact.read(db, {:stream, "turtle-1"}) |> Enum.to_list()
[
%{
"event_data" => %{"name" => "Turts"},
"event_id" => "3bb4808303c847fd9ceb0a1251ef95da",
"event_tags" => []
"event_type" => "egg_hatched",
"event_metadata" => %{},
"store_position" => 1,
"store_timestamp" => 1765039106962264,
"stream_id" => "turtle-1",
"stream_position" => 1
}
]1 - Its "pseudo-WORM" because immutability is enforced at the filesystem level by marking events as read-only. This prevents modification during normal operation, but does not provide hardware-level or regulatory WORM enforcement.
