Open source · Go module

One interface. Many databases. Zero rewrites.

DALgo is a Database Abstraction Layer for Go. Write your domain logic once against a single, well-tested interface — swap Postgres for Firestore, SQLite for Datastore, an in-memory store for a real one — without touching your business code.

Get started pkg.go.dev $ go get github.com/dal-go/dalgo
01 — Why a DAL

Storage is a detail, not a destiny.

Six reasons developers reach for DALgo on day one.

Storage-agnostic logic

Write business code against dal.Database. Decide later whether it lives in Postgres, Firestore, or a JSON file on disk.

Less code, more readable

One small, opinionated API replaces piles of driver-specific boilerplate. Records, keys, queries, transactions — uniform across every adapter.

Testable by construction

Swap a production driver for an in-memory one in tests. No mocks-of-mocks. mocks4dalgo ships generated mocks for the whole interface surface.

Hooks & logging, for free

Because every operation goes through one interface, observability and audit logging are a wrapper — not a refactor.

One end-to-end test suite

Every official adapter is validated against the same end-to-end test suite. Conformance is the contract.

Transactions, the Go way

Read-only and read-write transactions are first-class. Pass a closure, get atomicity — without leaking driver-specific transaction types into your domain.

02 — Adapters

Same interface. Different stores.

Ten official adapters spanning SQL, NoSQL, embedded key-value, file, and Git-versioned storage.

03 — Quick start

Install the core. Pick an adapter. Write a record.

A working example in fewer than 40 lines.

  1. 1

    Install the core module and one adapter — say, SQLite for local development.

  2. 2

    Open a database. The returned dal.Database is the only type your app needs to know about.

  3. 3

    Define a record, set its data, persist it inside a read-write transaction.

  4. 4

    Later, swap dalgo2sqlite for dalgo2firestore. Your handlers don't change.

main.go Go
// $ go get github.com/dal-go/dalgo
// $ go get github.com/dal-go/dalgo2sqlite

package main

import (
    "context"
    "log"

    "github.com/dal-go/dalgo/dal"
    "github.com/dal-go/dalgo2sqlite"
)

type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
}

func main() {
    ctx := context.Background()

    db, err := sqlite.Open(ctx, "app.db")
    if err != nil {
        log.Fatal(err)
    }

    user := &User{Name: "Ada", Email: "ada@example.com"}
    key  := dal.NewKeyWithID("users", "u_001")
    rec  := dal.NewRecordWithData(key, user)

    err = db.RunReadwriteTransaction(ctx,
        func(ctx context.Context, tx dal.ReadwriteTransaction) error {
            return tx.Insert(ctx, rec)
        },
    )
    if err != nil {
        log.Fatal(err)
    }
}
04 — The interface

Small enough to memorize.

Strict enough to keep every adapter honest.

Every DALgo adapter — SQL, NoSQL, embedded, filesystem — implements the same dal.Database contract. Read operations live on ReadonlySession; mutating operations live on ReadwriteSession. Transactions are closures: pass a function, the driver gives you a session, your code never sees a driver-specific handle.

Drivers that can't implement an operation return dalgo.ErrNotSupported — explicit failure beats silent nonsense.
dal/database.go Go
package dal

type Database interface {
    TransactionCoordinator
    ReadonlySession
}

type TransactionCoordinator interface {
    RunReadonlyTransaction(ctx context.Context,
        f ROTxWorker, opts ...TransactionOption) error

    RunReadwriteTransaction(ctx context.Context,
        f RWTxWorker, opts ...TransactionOption) error
}

type ReadonlySession interface {
    Get(ctx context.Context, record Record) error
    GetMulti(ctx context.Context, records []Record) error
    Select(ctx context.Context, query Select) (Reader, error)
}

type ReadwriteSession interface {
    ReadonlySession
    Insert(ctx context.Context, r Record, opts ...InsertOption) error
    Set(ctx context.Context, r Record) error
    Update(ctx context.Context, key *Key,
        updates []Update, pre ...Precondition) error
    Delete(ctx context.Context, key *Key) error
}

Storage is a detail. Treat it like one.

DALgo is MIT-licensed, production-tested, and waiting for you to break it. Contributions for new adapters and edge cases are welcome.