# How to Add New Use Cases for Business Logic in Go Clean Architecture

> Learn to add new use cases for business logic in Go using clean architecture. Implement interfaces, manage dependencies, and expose functionality efficiently.

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

---

**To add new use cases for business logic in Go, define an interface in `pkg/usecase/usecase`, implement it with repository dependencies, wire it through `pkg/registry`, and expose it via `pkg/adapter/controller`.**

The `manakuro/golang-clean-architecture` repository demonstrates strict Clean Architecture principles in Go, separating domain logic from infrastructure concerns. When adding new use cases for business logic in Go, you must follow the dependency rule: inner layers define interfaces that outer layers implement. This ensures your core business rules remain framework-agnostic and testable.

## Understanding the Clean Architecture Layers

The repository organizes code into concentric layers with strict dependency direction:

| Layer | Responsibility | Key Packages |
|-------|----------------|--------------|
| **Domain** | Core business entities and rules | `pkg/domain/model` |
| **Use-case** | Application-specific business logic | `pkg/usecase/usecase` & `pkg/usecase/repository` |
| **Adapter** | Delivery mechanisms (HTTP, DB) | `pkg/adapter/controller` & `pkg/adapter/repository` |
| **Infrastructure** | External frameworks (Gin, GORM) | `pkg/infrastructure/router` |
| **Registry** | Dependency injection wiring | `pkg/registry` |

## Step-by-Step Guide to Adding a New Use Case

### Define the Use Case Interface

Create a new interface in `pkg/usecase/usecase` that describes the operation. This contract defines what the use case does without specifying how.

```go
// pkg/usecase/usecase/user_profile.go
type UserProfileUpdater interface {
    UpdateProfile(id uint, data *model.User) (*model.User, error)
}

```

### Implement the Business Logic

Create a struct that implements the interface. It receives repository interfaces via constructor injection, maintaining the dependency inversion principle.

```go
type userProfileUsecase struct {
    userRepo repository.UserRepository
    dbRepo   repository.DBRepository
}

func NewUserProfileUsecase(u repository.UserRepository, d repository.DBRepository) UserProfileUpdater {
    return &userProfileUsecase{u, d}
}

func (up *userProfileUsecase) UpdateProfile(id uint, data *model.User) (*model.User, error) {
    result, err := up.dbRepo.Transaction(func(i interface{}) (interface{}, error) {
        user, err := up.userRepo.FindByID(id)
        if err != nil {
            return nil, err
        }
        user.Name = data.Name
        user.Age = data.Age
        return up.userRepo.Update(user)
    })
    if err != nil {
        return nil, err
    }
    return result.(*model.User), nil
}

```

### Extend Repository Contracts

If the use case requires new persistence operations, add them to the interface in `pkg/usecase/repository`. The use case layer defines what it needs; the adapter layer will implement it.

```go
// pkg/usecase/repository/user.go
type UserRepository interface {
    FindAll([]*model.User) ([]*model.User, error)
    Create(*model.User) (*model.User, error)
    FindByID(uint) (*model.User, error)   // new method
    Update(*model.User) (*model.User, error) // new method
}

```

### Wire Dependencies in the Registry

The [`pkg/registry/registry.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/registry/registry.go) file acts as the composition root. Add a factory method that instantiates the new use case with its dependencies.

```go
// pkg/registry/registry.go
type registry struct {
    db *gorm.DB
}

func (r *registry) NewUserProfileUsecase() usecase.UserProfileUpdater {
    repo := r.NewUserRepository()
    dbRepo := r.NewDBRepository()
    return usecase.NewUserProfileUsecase(repo, dbRepo)
}

```

### Expose via the Controller

Add a handler in `pkg/adapter/controller` that receives the use case interface and invokes it based on HTTP requests.

```go
// pkg/adapter/controller/user.go
func (c *UserController) UpdateProfile(ctx *gin.Context) {
    var payload model.User
    if err := ctx.ShouldBindJSON(&payload); err != nil {
        ctx.JSON(http.StatusBadRequest, err.Error())
        return
    }
    id, _ := strconv.ParseUint(ctx.Param("id"), 10, 64)
    updated, err := c.usecase.UpdateProfile(uint(id), &payload)
    if err != nil {
        ctx.JSON(http.StatusInternalServerError, err.Error())
        return
    }
    ctx.JSON(http.StatusOK, updated)
}

```

### Register the Route

Finally, map the new endpoint in [`pkg/infrastructure/router/router.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/infrastructure/router/router.go).

```go
// pkg/infrastructure/router/router.go
userGroup.PUT("/:id", appCtrl.User.UpdateProfile)

```

## Complete Code Example: UpdateUserProfile Use Case

Here is the full implementation flow for adding an `UpdateUserProfile` use case to the `manakuro/golang-clean-architecture` project:

**1. Interface Definition** ([`pkg/usecase/usecase/user_profile.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/usecase/usecase/user_profile.go)):

```go
package usecase

import (
    "golang-clean-architecture/pkg/domain/model"
    "golang-clean-architecture/pkg/usecase/repository"
)

type UserProfileUpdater interface {
    UpdateProfile(id uint, data *model.User) (*model.User, error)
}

type userProfileUsecase struct {
    userRepo repository.UserRepository
    dbRepo   repository.DBRepository
}

func NewUserProfileUsecase(u repository.UserRepository, d repository.DBRepository) UserProfileUpdater {
    return &userProfileUsecase{u, d}
}

func (up *userProfileUsecase) UpdateProfile(id uint, data *model.User) (*model.User, error) {
    result, err := up.dbRepo.Transaction(func(i interface{}) (interface{}, error) {
        user, err := up.userRepo.FindByID(id)
        if err != nil {
            return nil, err
        }
        user.Name = data.Name
        user.Age = data.Age
        return up.userRepo.Update(user)
    })
    if err != nil {
        return nil, err
    }
    return result.(*model.User), nil
}

```

**2. Repository Extension** ([`pkg/usecase/repository/user.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/usecase/repository/user.go)):

```go
type UserRepository interface {
    FindAll([]*model.User) ([]*model.User, error)
    Create(*model.User) (*model.User, error)
    FindByID(uint) (*model.User, error)
    Update(*model.User) (*model.User, error)
}

```

**3. Registry Wiring** ([`pkg/registry/registry.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/registry/registry.go)):

```go
func (r *registry) NewUserProfileUsecase() usecase.UserProfileUpdater {
    repo := r.NewUserRepository()
    dbRepo := r.NewDBRepository()
    return usecase.NewUserProfileUsecase(repo, dbRepo)
}

```

**4. Controller Handler** ([`pkg/adapter/controller/user.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/adapter/controller/user.go)):

```go
func (c *UserController) UpdateProfile(ctx *gin.Context) {
    var payload model.User
    if err := ctx.ShouldBindJSON(&payload); err != nil {
        ctx.JSON(http.StatusBadRequest, err.Error())
        return
    }
    id, _ := strconv.ParseUint(ctx.Param("id"), 10, 64)
    updated, err := c.usecase.UpdateProfile(uint(id), &payload)
    if err != nil {
        ctx.JSON(http.StatusInternalServerError, err.Error())
        return
    }
    ctx.JSON(http.StatusOK, updated)
}

```

**5. Route Registration** ([`pkg/infrastructure/router/router.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/infrastructure/router/router.go)):

```go
userGroup.PUT("/:id", appCtrl.User.UpdateProfile)

```

## Summary

- **Define interfaces** in `pkg/usecase/usecase` to declare what operations your business logic performs without specifying implementation details.
- **Implement use cases** as structs that receive repository interfaces via constructor injection, keeping business rules independent of databases and frameworks.
- **Extend repository contracts** in `pkg/usecase/repository` when new persistence operations are needed, letting the use case layer define its dependencies.
- **Wire dependencies** in [`pkg/registry/registry.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/registry/registry.go) to compose use cases with their concrete repositories, maintaining a single composition root.
- **Expose functionality** through `pkg/adapter/controller` handlers and register routes in [`pkg/infrastructure/router/router.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/infrastructure/router/router.go) to make the use case accessible via HTTP.

## Frequently Asked Questions

### What is the difference between a use case and a repository in Clean Architecture?

In Clean Architecture, the **use case** contains application-specific business logic and orchestrates the flow of data to and from entities. It defines *what* the application does. The **repository** is an interface defined in the use case layer but implemented in the adapter layer; it handles *how* data is persisted and retrieved. The use case depends on the repository interface, not the concrete implementation, ensuring business logic remains decoupled from database details.

### How do I handle database transactions in a Go use case?

Database transactions are handled through the `DBRepository` interface, which provides a `Transaction` method. In your use case implementation, wrap multiple repository operations inside the transaction function passed to `dbRepo.Transaction()`. If any operation returns an error, the transaction rolls back automatically; if all succeed, it commits. This pattern ensures atomicity for complex business operations that modify multiple entities.

### Can I group multiple use cases in a single file?

While the repository structure suggests one use case per file (e.g., [`user.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/user.go) for user-related operations), you can group closely related use cases in a single file if they share the same domain entity and repository dependencies. However, as your application grows, separating distinct business operations into individual files (such as [`user_profile.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/user_profile.go) for profile updates) improves maintainability and makes the codebase easier to navigate.

### Why is the registry pattern used for wiring dependencies?

The **registry** pattern centralizes dependency injection in [`pkg/registry/registry.go`](https://github.com/manakuro/golang-clean-architecture/blob/main/pkg/registry/registry.go), acting as the single composition root for the application. It instantiates concrete repositories and use cases, injecting them according to the interfaces defined in inner layers. This approach keeps framework-specific initialization code (like GORM or Gin) isolated from business logic, makes testing easier by allowing mock injection, and ensures that dependency direction always points inward toward the domain layer.