# Configuring and Using Different Database Dialects with GORM: A Complete Guide

> Learn to configure and use different database dialects with GORM in Go. Our guide covers exposing dialect fields, importing drivers, and building DSNs for flexible data management.

- Repository: [manato/golang-clean-architecture](https://github.com/manakuro/golang-clean-architecture)
- Tags: how-to-guide
- Published: 2026-03-06

---

**You can configure multiple database dialects in a GORM-based Go application by exposing a `dialect` field in your configuration, importing the necessary driver packages, and using a switch statement in your connection factory to build dialect-specific DSN strings.**

The `manakuro/golang-clean-architecture` repository demonstrates Clean Architecture principles in Go, but its database layer currently hard-codes MySQL support. When configuring and using different database dialects with GORM, you need to modify only the infrastructure layer while keeping domain logic and use cases completely untouched.

## Understanding the Current Database Implementation

The project creates database connections in **[`pkg/infrastructure/datastore/db.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/infrastructure/datastore/db.go)** through the `NewDB()` function. Currently, this function hard-codes the MySQL dialect and driver:

```go
func NewDB() *gorm.DB {
    DBMS := "mysql"
    mySqlConfig := &mysql.Config{ … }
    db, err := gorm.Open(DBMS, mySqlConfig.FormatDSN())
    // …
}

```

The configuration values (user, password, host) are loaded from **[`pkg/config/config.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/config/config.go)**, which uses Viper to parse YAML files. However, the dialect itself is not exposed as a configurable parameter, requiring code changes to switch databases.

## Extending the Configuration Structure

To support multiple dialects, first add a `Dialect` field to the Database configuration struct in **[`pkg/config/config.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/config/config.go)**:

```go
type config struct {
    Database struct {
        Dialect  string // "mysql", "postgres", "sqlite3", "mssql"
        User     string
        Password string
        Net      string
        Addr     string
        DBName   string
        AllowNativePasswords bool
        Params   struct {
            ParseTime string
        }
    }
    // … Server configuration
}

```

Update your **[`config/config.yml`](https://github.com/manakuro/golang-clean-architecture/blob/main/config/config.yml)** to include the dialect selection:

```yaml
database:
  dialect: postgres          # Options: mysql, postgres, sqlite3, mssql

  user:     "myuser"
  password: "secret"
  net:      "tcp"
  addr:     "localhost:5432"
  dbname:   "mydb"
  allowNativePasswords: true
  params:
    parseTime: "True"

```

## Importing Multiple GORM Drivers

GORM uses side-effect imports to register database drivers. Update **[`pkg/infrastructure/datastore/db.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/infrastructure/datastore/db.go)** to import all dialects you intend to support:

```go
import (
    "log"
    "fmt"

    "golang-clean-architecture/pkg/config"

    // Driver side-effect registrations
    _ "github.com/jinzhu/gorm/dialects/mysql"
    _ "github.com/jinzhu/gorm/dialects/postgres"
    _ "github.com/jinzhu/gorm/dialects/sqlite"
    _ "github.com/jinzhu/gorm/dialects/mssql"

    "github.com/jinzhu/gorm"
)

```

These blank imports invoke `init()` functions that register each driver with GORM's dialect registry, making them available at runtime.

## Implementing Dialect-Aware Connection Logic

Replace the hard-coded `NewDB()` implementation with a switch statement that constructs the appropriate DSN for each supported database:

```go
func NewDB() *gorm.DB {
    dialect := config.C.Database.Dialect
    if dialect == "" {
        dialect = "mysql"
    }

    var dsn string
    switch dialect {
    case "mysql":
        myCfg := &mysql.Config{
            User:                 config.C.Database.User,
            Passwd:               config.C.Database.Password,
            Net:                  config.C.Database.Net,
            Addr:                 config.C.Database.Addr,
            DBName:               config.C.Database.DBName,
            AllowNativePasswords: config.C.Database.AllowNativePasswords,
            Params: map[string]string{
                "parseTime": config.C.Database.Params.ParseTime,
            },
        }
        dsn = myCfg.FormatDSN()

    case "postgres":
        // Using lib/pq style DSN
        dsn = fmt.Sprintf(
            "host=%s port=5432 user=%s password=%s dbname=%s sslmode=disable",
            config.C.Database.Addr,
            config.C.Database.User,
            config.C.Database.Password,
            config.C.Database.DBName,
        )

    case "sqlite3":
        // SQLite uses file path as DSN
        dsn = config.C.Database.DBName

    case "mssql":
        // SQL Server connection string
        dsn = fmt.Sprintf(
            "sqlserver://%s:%s@%s?database=%s",
            config.C.Database.User,
            config.C.Database.Password,
            config.C.Database.Addr,
            config.C.Database.DBName,
        )

    default:
        log.Fatalf("unsupported DB dialect: %s", dialect)
    }

    db, err := gorm.Open(dialect, dsn)
    if err != nil {
        log.Fatalln(err)
    }
    return db
}

```

Each dialect requires its own DSN format: MySQL uses `mysql.Config`, PostgreSQL uses key-value pairs, SQLite expects a file path, and SQL Server uses URL-style connection strings.

## Maintaining Clean Architecture Boundaries

The architectural flow remains unchanged when adding multi-dialect support:

```

config → datastore.NewDB → GORM DB → repositories → use-cases → controllers

```

The `*gorm.DB` instance created by `NewDB()` is injected into the registry in **[`cmd/app/main.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/cmd/app/main.go)**, then passed to repositories via **[`pkg/registry/registry.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/registry/registry.go)**. Because the database handle is an interface, repositories and use cases remain agnostic to the underlying dialect. Only `NewDB()` and the configuration structure require modification, preserving the dependency rule that keeps business logic independent of infrastructure concerns.

## Summary

- **Expose the dialect** in [`pkg/config/config.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/config/config.go) to make database selection configurable without code changes.
- **Import all required drivers** in [`pkg/infrastructure/datastore/db.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/infrastructure/datastore/db.go) using blank imports to register them with GORM.
- **Construct dialect-specific DSNs** using a switch statement in `NewDB()` to handle MySQL, PostgreSQL, SQLite, and SQL Server connection formats.
- **Preserve Clean Architecture** by keeping the `*gorm.DB` injection unchanged; no modifications are needed in repositories, use cases, or controllers.

## Frequently Asked Questions

### How do I add support for an additional GORM dialect not shown in the examples?

Add a new blank import for the driver (such as `_ "github.com/jinzhu/gorm/dialects/foundation"`), then add a new case in the `NewDB()` switch statement with the correct DSN construction logic. Ensure your configuration struct includes any dialect-specific parameters.

### Can I switch between PostgreSQL and MySQL without rebuilding the application?

Yes. By externalizing the dialect into [`config/config.yml`](https://github.com/manakuro/golang-clean-architecture/blob/main/config/config.yml), you can change the `dialect` value and restart the application. The `NewDB()` factory reads the configuration at runtime, so no recompilation is necessary unless you need to add a completely new database type.

### Which GORM dialects are supported by this approach?

GORM v1 supports MySQL, PostgreSQL, SQLite3, and SQL Server (MSSQL) through official dialect packages. The same pattern works for community-maintained dialects (such as ClickHouse or TiDB) by importing their respective driver packages and constructing their required DSN formats.

### Should I use build tags instead of runtime configuration for dialect selection?

Runtime configuration via the method described above offers more flexibility for deployments, while build tags reduce binary size by compiling only the needed driver. For most applications, the runtime approach with the switch statement provides the best balance of flexibility and maintainability, adding only minimal overhead from unused driver imports.