# Building and Deploying a Go Clean Architecture Application: A Complete Guide

> Easily build and deploy a Go clean architecture application. Compile the binary, configure MySQL with Viper, and run or containerize it using multi-stage Docker builds.

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

---

**You can build and deploy a Go clean architecture application by compiling the binary from [`cmd/app/main.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/cmd/app/main.go), configuring MySQL credentials via Viper in [`config/config.yml`](https://github.com/manakuro/golang-clean-architecture/blob/main/config/config.yml), and running the resulting binary or containerizing it with a multi-stage Docker build.**

The `manakuro/golang-clean-architecture` repository demonstrates how to structure a production-ready Go web service using clean architecture principles. This guide explains the layered architecture that separates domain logic from infrastructure concerns, then walks you through compiling and deploying the application.

## Understanding the Clean Architecture Layers

The project organizes code into concentric layers following the **Dependency Rule**, where inner layers contain business logic and outer layers handle technical details like HTTP and databases.

### Domain Layer

At the core, [`pkg/domain/model/user.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/domain/model/user.go) defines the `User` struct—a plain Go struct with GORM tags representing the business entity. This layer contains no imports from external frameworks or database drivers, ensuring business rules remain isolated from technical implementations.

### Use-Case Layer

Located in [`pkg/usecase/usecase/user.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/usecase/usecase/user.go), this layer defines the `UserUsecase` interface exposing `List()` and `Create()` operations. The implementation orchestrates repositories and wraps database operations in transactions using the `Transaction` method from the underlying `DBRepository` interface.

### Adapter Layer

Adapters translate between external systems and use cases. The HTTP controller in [`pkg/adapter/controller/user.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/adapter/controller/user.go) decodes JSON requests and invokes use-case methods, while [`pkg/adapter/repository/user.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/adapter/repository/user.go) implements the repository interface using GORM for MySQL persistence.

### Infrastructure and Wiring

The `pkg/infrastructure` package contains [`router/router.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/router/router.go) (Echo HTTP server setup) and [`datastore/db.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/datastore/db.go) (MySQL connection logic). The [`pkg/registry/registry.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/registry/registry.go) handles dependency injection, assembling controllers, use cases, and repositories so that [`cmd/app/main.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/cmd/app/main.go) remains a thin entry point that merely boots the server.

## Building the Application Locally

Follow these steps to compile and run the service on your development machine.

### Prerequisites and Configuration

Clone the repository and create a YAML configuration file at [`config/config.yml`](https://github.com/manakuro/golang-clean-architecture/blob/main/config/config.yml):

```yaml
database:
  user: "dbuser"
  password: "dbpass"
  net: "tcp"
  addr: "127.0.0.1:3306"
  dbname: "mydb"
  allowNativePasswords: true
  params:
    parseTime: "true"
server:
  address: "8080"

```

The [`pkg/config/config.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/config/config.go) loader uses Viper to parse this file into the global `config.C` struct at startup, constructing a MySQL DSN using `mysql.Config`.

### Compilation and Execution

Build the binary and start the server:

```bash
go build -o app ./cmd/app
./app

```

The console outputs `Server listen at http://localhost:8080` when successful. Test the API endpoints:

```bash
curl http://localhost:8080/users
curl -X POST http://localhost:8080/users \
     -H "Content-Type: application/json" \
     -d '{"name":"Alice","age":"30"}'

```

## Deploying with Docker

For production deployments, package the binary using a multi-stage Dockerfile to minimize image size and attack surface.

### Multi-Stage Dockerfile

Create a `Dockerfile` in the project root:

```dockerfile
FROM golang:1.19-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app ./cmd/app

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/app .
COPY config/config.yml ./config/config.yml
EXPOSE 8080
CMD ["./app"]

```

Build and run the container:

```bash
docker build -t go-clean-arch .
docker run -p 8080:8080 -v $(pwd)/config:/root/config go-clean-arch

```

### Configuration Management

Viper automatically maps environment variables prefixed with `ENV_` to configuration fields, allowing you to override [`config.yml`](https://github.com/manakuro/golang-clean-architecture/blob/main/config.yml) values in containerized environments without rebuilding the image. Mount your configuration as a volume or inject secrets via environment variables.

## Request Flow and Transaction Handling

Understanding how requests traverse the layers ensures you can extend the codebase without violating architectural constraints.

When a POST request hits `/users`, the `CreateUser` method in [`pkg/adapter/controller/user.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/adapter/controller/user.go) binds the JSON payload to a `model.User` struct and delegates to the use-case:

```go
func (uc *userController) CreateUser(ctx Context) error {
    var params model.User
    if err := ctx.Bind(&params); err != nil {
        return err
    }
    u, err := uc.userUsecase.Create(&params)
    if err != nil {
        return err
    }
    return ctx.JSON(http.StatusCreated, u)
}

```

The use-case in [`pkg/usecase/usecase/user.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/usecase/usecase/user.go) wraps the creation logic in a database transaction:

```go
data, err := uu.dBRepository.Transaction(func(i interface{}) (interface{}, error) {
    u, err := uu.userRepository.Create(u)
    return u, err
})

```

This ensures atomicity when persisting to MySQL via the GORM implementation in [`pkg/adapter/repository/user.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/adapter/repository/user.go), while keeping transaction control at the business logic layer rather than in HTTP handlers.

## Summary

- The **domain layer** remains pure with no external dependencies, as seen in [`pkg/domain/model/user.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/domain/model/user.go).
- **Use-case** logic in [`pkg/usecase/usecase/user.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/usecase/usecase/user.go) handles business rules and transaction coordination through `dBRepository.Transaction`.
- **Adapters** isolate HTTP handling ([`pkg/adapter/controller/user.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/adapter/controller/user.go)) and database implementation details ([`pkg/adapter/repository/user.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/adapter/repository/user.go)).
- **Infrastructure** packages provide concrete implementations for routing ([`pkg/infrastructure/router/router.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/infrastructure/router/router.go)) and data storage ([`pkg/infrastructure/datastore/db.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/infrastructure/datastore/db.go)).
- The **registry** pattern in [`pkg/registry/registry.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/registry/registry.go) enables clean dependency injection without framework magic.
- Build the application with `go build -o app ./cmd/app` and deploy using multi-stage Docker builds for minimal production images.

## Frequently Asked Questions

### What is the Dependency Rule in clean architecture?

The Dependency Rule states that source code dependencies must point only inward toward higher-level policies. In this repository, [`pkg/adapter/controller/user.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/adapter/controller/user.go) imports `pkg/usecase/usecase`, but the use-case layer knows nothing about HTTP or Echo, ensuring business logic remains framework-agnostic and testable without spinning up web servers.

### How does transaction handling work across layers?

The use-case layer calls `Transaction` from the repository interface defined in `pkg/usecase/repository`, passing a function that executes within a database session. This allows the business logic to coordinate multiple repository operations atomically without leaking database connection details to controllers or exposing transaction semantics to the domain model.

### Can I replace GORM with a different ORM or driver?

Yes. The repository interface in `pkg/usecase/repository` abstracts all persistence operations including `FindAll`, `Create`, and `Transaction`. You can implement a new adapter in `pkg/adapter/repository` using SQLx, raw `database/sql`, or a NoSQL driver without modifying the use-case or domain layers, provided you satisfy the interface contracts.

### How should I manage secrets in production deployments?

Viper supports environment variable overrides automatically. Rather than baking credentials into [`config/config.yml`](https://github.com/manakuro/golang-clean-architecture/blob/main/config/config.yml) inside the Docker image, mount the configuration file as a volume or inject environment variables prefixed with `ENV_` to override sensitive values at runtime, keeping secrets out of the container layer and version control.