skuld_repo
Ecto integration for Skuld — an effectful dispatch facade that makes
database calls feel like any other effect, with swappable backends for
testing. Write once against the effectful interface; swap InMemory in
tests, Ecto in production.
What's included
Skuld.Repo— effectful dispatch facade generated byEffectfulFacade. Typed callers for all standard Ecto operations (insert,get,get_by,update,delete,one,all, etc.) with bang variants that auto-Throwon error.Skuld.Repo.Ecto— production handler wrapping a real Ecto Repo. Dispatches operations through the Port effect exactly as your app would, with full Ecto query pipeline.Skuld.Repo.InMemory— closed-world in-memory store. All data must be inserted before queries that depend on it. Fast, deterministic, no database process — ideal for integration tests.Skuld.Repo.OpenInMemory— open-world in-memory store. Similar toInMemorybut supports dynamic seeding via callbacks. Useful when test data comes from factories or fixtures.Skuld.Repo.Stub— stateless stub returning canned responses. Use for unit tests where you want exact control over every query result without building up state.Skuld.Effects.Transaction.Ecto— database transaction handler. Wraps computations in real Ecto transactions with env state rollback semantics. Composes withEffectLoggerfor durable workflows.
Installation
def deps do
[
{:skuld_repo, "~> 0.32"}
]
end
Example: test-first with InMemory
# Production
comp do
user <- Repo.insert!(%User{name: "Alice"})
found <- Repo.get(User, user.id)
found
end
|> Port.with_handler(%{Repo.Effectful => MyApp.Repo.Ecto})
|> Throw.with_handler()
|> Comp.run!()
# Test — same code, zero setup beyond the handler
comp
|> Port.with_handler(%{Repo.Effectful => Repo.InMemory.new()})
|> Throw.with_handler()
|> Comp.run!()
Further reading
- Repo — full operation reference
- Hexagonal Architecture — Port/Adapter pattern in depth
See the architecture guide for how this fits into the Skuld ecosystem.