Skip to content

Latest commit

 

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

apolon

A type-safe ORM for Go inspired by Entity Framework

Change tracking, fluent queries, and auto-migration with compile-time safety.

Go PostgreSQL

About

Note

Still working on this, requires approximtlly additional 3 years of free time to finish

Apolon is trying to bring Entity Framework-style patterns to Go: change tracking, unit of work, and type-safe queries.

Example

type Patient struct {
    ID   int    `apolon:"id,pk"`
    Name string `apolon:"name,notnull"`
    Age  int    `apolon:"age"`
}

func main() {
    db, _ := apolon.Open("postgres://...")
    defer db.Close()

    // Auto-create tables
    db.AutoMigrate(&Patient{})

    // Type-safe queries with generated fields
    patients, _ := apolon.Set[Patient](db).
        Where(PatientFields.Age.Gt(18)).
        Where(PatientFields.Name.Contains("Smith")).
        OrderBy(PatientFields.Name.Asc()).
        ToSlice()

    // Change tracking
    patients[0].Name = "Updated"
    db.SaveChanges() // Only updates changed fields
}

Design Decisions && comparison to other ORMs

GORM is cool ... but has few problems

GORM is the most popular Go ORM (cool), but it relies heavily on strings and reflection at runtime:
// GORM - strings everywhere, no compile-time safety - very uncool
db.Where("age > ?", 18).Where("name LIKE ?", "%Smith%").Find(&patients)

// Typo in column name? Runtime error.
db.Where("agee > ?", 18).Find(&patients) // Silent failure or runtime party
// Apolon - compile-time type safety
apolon.Set[Patient](db).
    Where(PatientFields.Age.Gt(18)).
    Where(PatientFields.Name.Contains("Smith")).
    ToSlice()

// Typo? Compiler catches it immediately.
PatientFields.Agee.Gt(18) // Compile error: PatientFields has no field Agee
Generated field accessors catch errors at compile time, not in production.

Ent is cool ... but is a different beast

Ent is a powerful entity framework backed by Meta. It takes a schema-first approach — you define fields in a DSL, and Ent generates everything (structs, builders, predicates) from that:
// Ent - schema defines the model, structs are generated
func (Patient) Fields() []ent.Field {
    return []ent.Field{
        field.String("name").NotEmpty().MaxLen(100),
        field.Int("age").Positive(),
    }
}

client.Patient.Create().SetName("John").SetAge(30).Save(ctx)

// Want to update two fields? Two separate calls.
client.Patient.UpdateOneID(id).SetName("Jane").Save(ctx)
client.Patient.UpdateOneID(id).SetAge(31).Save(ctx)
// Apolon - your struct is the schema, changes are tracked
p, _ := apolon.Set[Patient](db).Find(id)

p.Name = "Robert MeDiro"
p.Age = 31
db.SaveChanges() // Both changes flushed in one transaction
Ent is stateless and operation-oriented — each write is an explicit builder call with no memory of what came before. Apolon tracks your entities in memory and flushes all changes at once via `SaveChanges()`, so you work with plain Go structs instead of builder chains.
Ent also owns your types — it generates the structs, the predicates, and the mutation API from its schema DSL. Apolon flips this: you own your structs, and the ORM reads them via tags.

SQLBoiler is cool ... but starts from the other end

SQLBoiler is a database-first ORM — it introspects your existing database schema and generates fully type-safe models and query helpers from it:
// SQLBoiler - database-first: schema lives in PostgreSQL, code is generated from it
// 1. You create the table in SQL (or a migration tool)
// 2. Run `sqlboiler psql` to generate Go code
// 3. Use the generated models and query mods

patients, _ := models.Patients(
    qm.Where("age > ?", 18),
    qm.Where("name LIKE ?", "%Smith%"),
    qm.OrderBy("name ASC"),
).All(ctx, db)

// Type-safe models, but queries still use raw strings for conditions.
// Typo in "agee > ?" — no compile error, runtime surprise.
// Apolon - code-first(at least for now): your struct IS the schema
type Patient struct {
    ID   int    `apolon:"id,pk"`
    Name string `apolon:"name"`
    Age  int    `apolon:"age"`
}

// Table auto-created from struct tags, conditions are typed
apolon.Set[Patient](db).
    Where(PatientFields.Age.Gt(18)).
    Where(PatientFields.Name.Contains("Smith")).
    OrderBy(PatientFields.Name.Asc()).
    ToSlice()
SQLBoiler generates the models for you from the database — you don't write structs, it does. Apolon goes the other way: you write plain Go structs with tags, and the ORM derives the schema from them.
// SQLBoiler - explicit updates, you specify every column
patient, _ := models.FindPatient(ctx, db, id)
patient.Name = "Robert MeDiro"
patient.Age = 81
patient.Update(ctx, db, boil.Whitelist("name", "age")) // Must list changed columns

// Apolon - change tracking figures it out
p, _ := apolon.Set[Patient](db).Find(id)
p.Name = "Robert MeDiro"
p.Age = 81
db.SaveChanges() // Detects and flushes only changed fields
If you're familiar with Entity Framework, Apolon should feel natural. The main difference is using generated field types instead of lambda expressions (Go doesn't have those - so we do a bit of magic).

Getting Started

Prerequisites

Important

Apolon currently only supports PostgreSQL.

Installation

go get github.com/jkeresman01/apolon

Generating Field Accessors

Apolon requires generating typed field accessors for your models. Add the `go:generate` directive to your model file:
//go:generate go run github.com/jkeresman01/apolon/apolon-cli generate -i . -o .

type Patient struct {
    ID   int    `apolon:"id,pk"`
    Name string `apolon:"name"`
    Age  int    `apolon:"age"`
}

Then run:

go generate ./...
This creates a `model_fields.go` file with typed accessors like `PatientFields.Age`, `PatientFields.Name`, etc. that you can use in queries.
You can also run the generator manually:
go run github.com/jkeresman01/apolon/apolon-cli generate -i ./models -o ./models

Resources

https://entgo.io/docs/schema-fields

https://gorm.io/docs/

About

Simple entity framework like ORM implemenation in go [WIP]

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages