Quick Start¶
This guide walks you through creating your first entity and repository with Dynorm.
Step 1: Define Your Entity¶
Entities embed dynorm.Entity to get automatic ID generation, timestamps, and lifecycle management:
package main
import "github.com/go-gamma/dynorm"
// User entity - focus only on business fields
type User struct {
dynorm.Entity // Provides: ID, CreatedAt, UpdatedAt, DeletedAt
Email string `dynorm:"gsi:ByEmail"`
Username string
FirstName string
LastName string
Status string
}
What dynorm.Entity provides
- ID: Auto-generated ULID (26 characters, sortable, unique)
- CreatedAt: Derived from ULID timestamp on first save
- UpdatedAt: Set to current time on every save
- DeletedAt: For soft deletes (optional)
Step 2: Define Your Repository¶
Repositories embed dynorm.Repository[T] and handle all database operations:
// UserRepository - table name auto-derived as "users"
type UserRepository struct {
dynorm.Repository[User]
}
// Global repository instance
var UserRepo = &UserRepository{}
Custom table names
Override the auto-derived table name with a struct tag:
Step 3: Use It¶
func main() {
// Create a new user
user := &User{
Email: "john@example.com",
Username: "johndoe",
FirstName: "John",
LastName: "Doe",
Status: "active",
}
// Save - ID and timestamps are set automatically
err := UserRepo.Save(user)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created user with ID: %s\n", user.ID)
// Output: Created user with ID: 01HQ3K4N5M6P7Q8R9S0T1U2V3W
// Find by ID
found, err := UserRepo.Find(user.ID)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found: %s %s\n", found.FirstName, found.LastName)
// Query with fluent API
activeUsers, err := UserRepo.
Where("Status", "=", "active").
OrderBy("CreatedAt", "desc").
Limit(10).
GetAll()
fmt.Printf("Found %d active users\n", activeUsers.Count())
}
Complete Example¶
Here's a full working example:
package main
import (
"fmt"
"log"
"os"
"github.com/go-gamma/dynorm"
)
// Entity
type Product struct {
dynorm.Entity
Name string `dynorm:"gsi:ByName"`
Category string `dynorm:"gsi:ByCategory"`
Price float64
InStock bool
Description string
}
// Repository
type ProductRepository struct {
dynorm.Repository[Product]
}
var ProductRepo = &ProductRepository{}
func main() {
// Ensure AWS region is set
if os.Getenv("AWS_REGION") == "" {
os.Setenv("AWS_REGION", "us-east-1")
}
// Create
product := &Product{
Name: "Laptop",
Category: "Electronics",
Price: 999.99,
InStock: true,
Description: "High-performance laptop",
}
if err := ProductRepo.Save(product); err != nil {
log.Fatal(err)
}
fmt.Printf("Created product: %s\n", product.ID)
// Read
found, err := ProductRepo.Find(product.ID)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found: %s - $%.2f\n", found.Name, found.Price)
// Update
product.Price = 899.99
if err := ProductRepo.Save(product); err != nil {
log.Fatal(err)
}
fmt.Println("Price updated")
// Query
electronics, err := ProductRepo.
Where("Category", "=", "Electronics").
Where("InStock", "=", true).
OrderBy("Price", "asc").
GetAll()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d electronics in stock\n", electronics.Count())
// Delete
if err := ProductRepo.Delete(product); err != nil {
log.Fatal(err)
}
fmt.Println("Product deleted")
}
Architecture Overview¶
graph TB
subgraph "Your Code"
Entity[Entity Structs]
Repo[Repository]
end
subgraph "Dynorm"
BaseEntity[dynorm.Entity]
BaseRepo[dynorm.Repository]
Query[Query Builder]
Schema[Schema Parser]
Marshal[Snake Case Marshaller]
end
subgraph "AWS"
DDB[(DynamoDB)]
end
Entity --> BaseEntity
Repo --> BaseRepo
BaseRepo --> Query
BaseRepo --> Schema
Query --> Marshal
Marshal --> DDB Next Steps¶
- Configuration - Set table prefixes and options
- Entity Pattern - Deep dive into entities
- Repository Pattern - Repository operations
- Query Builder - Fluent query API