A flexible and extensible Database Abstraction Layer (DAL) for Go applications that provides a clean separation between business logic and database operations.
- Database Agnostic: Write your business logic once, switch databases without changing application code
- Repository Pattern: Clean separation of concerns with repository-based data access
- Provider Architecture: Pluggable database providers (PostgreSQL, MySQL, MongoDB, etc.)
- Migration Support: Built-in database migration capabilities
- Transaction Support: Consistent transaction handling across different databases
- Type-Safe: Leverages Go's type system for compile-time safety
go get github.com/gnemade360/go-dalpackage models
import "time"
type User struct {
ID string `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Email string `json:"email" db:"email"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
func (u *User) GetID() string {
return u.ID
}
func (u *User) SetID(id string) {
u.ID = id
}import (
"context"
"github.com/gnemade360/go-dal/dal/providers/postgres"
)
// Create PostgreSQL provider
provider := postgres.New()
provider.SetDSN("postgres://user:pass@localhost:5432/mydb?sslmode=disable")
// Connect to database
ctx := context.Background()
if err := provider.Connect(ctx); err != nil {
log.Fatal("Failed to connect:", err)
}
defer provider.Close()import "github.com/gnemade360/go-dal/dal/repository"
// Create repository
userRepo := repository.NewUserRepository(provider)
// Create a new user
user := &models.User{
Name: "John Doe",
Email: "john@example.com",
}
if err := userRepo.Create(ctx, user); err != nil {
log.Fatal("Failed to create user:", err)
}
// Find user by ID
found, err := userRepo.FindByID(ctx, user.ID)
if err != nil {
log.Fatal("Failed to find user:", err)
}
// Update user
user.Name = "Jane Doe"
if err := userRepo.Update(ctx, user); err != nil {
log.Fatal("Failed to update user:", err)
}
// List all users
users, err := userRepo.FindAll(ctx)
if err != nil {
log.Fatal("Failed to list users:", err)
}
// Delete user
if err := userRepo.Delete(ctx, user.ID); err != nil {
log.Fatal("Failed to delete user:", err)
}go-dal/
├── dal/
│ ├── interfaces/ # Core interfaces
│ │ ├── database.go # Database, Transaction, Dialect interfaces
│ │ └── repository.go # Repository interface
│ │
│ ├── providers/ # Database-specific implementations
│ │ ├── postgres/ # PostgreSQL provider
│ │ ├── mysql/ # MySQL provider (planned)
│ │ └── mongodb/ # MongoDB provider (planned)
│ │
│ ├── repository/ # Repository implementations
│ │ └── simple.go # Basic repository implementation
│ │
│ └── migrations/ # Migration support
│ └── migrator.go # Database migration utilities
│
└── cmd/ # Example applications
└── basic-crud/ # Basic CRUD example
The repository includes a complete Basic CRUD example that demonstrates the DAL in action.
- Go 1.21+
- Podman or Docker
- Make (optional)
Using Podman (recommended):
make up
# or directly:
./scripts/podman-postgres.sh startUsing Docker:
make docker-up
# or directly:
docker-compose up -dmake run
# or directly:
go run cmd/basic-crud/main.go# Health check
curl http://localhost:8080/health
# Create a user
curl -X POST http://localhost:8080/users \
-H "Content-Type: application/json" \
-d '{"name":"Alice Cooper","email":"alice@example.com"}'
# List all users
curl http://localhost:8080/users
# Get specific user (replace {id} with actual UUID)
curl http://localhost:8080/users/{id}
# Update user
curl -X PUT http://localhost:8080/users/{id} \
-H "Content-Type: application/json" \
-d '{"name":"Alice Updated","email":"alice.new@example.com"}'
# Delete user
curl -X DELETE http://localhost:8080/users/{id}The Provider is responsible for database-specific operations. It implements the Database interface and handles:
- Connection management
- Query execution
- Transaction handling
- Database-specific SQL dialect
The Repository pattern provides a collection-like interface for accessing domain objects. It abstracts the data access logic and provides a more object-oriented view of the persistence layer.
Entities are domain objects that have a distinct identity. In go-dal, entities should implement methods for ID management:
type Entity interface {
GetID() string
SetID(string)
}To add support for a new database:
- Create a new provider package in
dal/providers/ - Implement the
Databaseinterface:
type Database interface {
Connect(ctx context.Context) error
Close() error
Execute(ctx context.Context, query string, args ...interface{}) (Result, error)
Query(ctx context.Context, query string, args ...interface{}) (Rows, error)
QueryRow(ctx context.Context, query string, args ...interface{}) Row
Begin(ctx context.Context) (Transaction, error)
GetDialect() Dialect
}- Implement the
Dialectinterface for database-specific SQL generation - Create provider-specific repository implementations if needed
The DAL includes migration support through the dal-migrate command:
# Run migrations
go run cmd/dal-migrate/main.go up
# Rollback migrations
go run cmd/dal-migrate/main.go down
# Create a new migration
go run cmd/dal-migrate/main.go create <migration_name>The DAL can be configured through environment variables:
| Variable | Description | Default |
|---|---|---|
DATABASE_URL |
Database connection string | Provider-specific |
MAX_CONNECTIONS |
Maximum database connections | 25 |
MAX_IDLE_CONNECTIONS |
Maximum idle connections | 5 |
CONNECTION_TIMEOUT |
Connection timeout in seconds | 30 |
You can extend repositories with custom methods:
type UserRepository struct {
*repository.BaseRepository
}
func (r *UserRepository) FindByEmail(ctx context.Context, email string) (*User, error) {
query := "SELECT * FROM users WHERE email = $1"
// Implementation...
}Create custom entities by implementing the Entity interface:
type Product struct {
ID string `db:"id"`
Name string `db:"name"`
Price float64 `db:"price"`
}
func (p *Product) GetID() string { return p.ID }
func (p *Product) SetID(id string) { p.ID = id }-
Verify your connection string format:
- PostgreSQL:
postgres://user:pass@host:port/db?sslmode=disable - MySQL:
user:pass@tcp(host:port)/db?parseTime=true
- PostgreSQL:
-
Check if the database server is running:
# For Podman podman ps # For Docker docker ps
-
Verify network connectivity to the database
- Ensure the migrations table exists
- Check migration file naming (must follow timestamp pattern)
- Verify SQL syntax for your specific database
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
- PostgreSQL provider
- Basic repository pattern
- Migration support
- MySQL provider
- MongoDB provider
- SAP HANA provider
- Redis cache layer
- Query builder
- Soft deletes
- Audit logging
- Connection pooling optimization
This project is licensed under the MIT License - see the LICENSE file for details.
For issues, questions, or contributions, please visit the GitHub repository.