Table of Contents
- Introduction
- Prerequisites
- Setting Up the Project
- Creating the Core Domain
- Implementing the Use Cases
- Building the Infrastructure
- Creating the Web Application
- Conclusion
Introduction
In this tutorial, we will learn how to build a web application in Go using the principles of Clean Architecture. Clean Architecture is a software design pattern that emphasizes separation of concerns and a robust domain model. By the end of this tutorial, you will have a clear understanding of how to structure and develop a web application in Go with Clean Architecture.
Prerequisites
Before starting this tutorial, you should have basic knowledge of Go programming language and web development concepts. You should also have Go installed on your machine. If you haven’t already, you can download and install Go from the official website: https://golang.org/
Setting Up the Project
To begin, let’s set up the project structure and dependencies. Open your terminal, navigate to your desired project directory, and create a new directory for your project. We will call it “go-webapp-clean-architecture”.
$ mkdir go-webapp-clean-architecture
Next, go into the project directory and initialize a new Go module.
$ cd go-webapp-clean-architecture
$ go mod init github.com/your-username/go-webapp-clean-architecture
Now, we need to install some required dependencies. We will be using the “gorilla/mux” package for routing and the “gorm” package for database interactions. Run the following commands to install these dependencies:
$ go get -u github.com/gorilla/mux
$ go get -u gorm.io/gorm
$ go get -u gorm.io/driver/sqlite
Creating the Core Domain
The first step in building a web application with Clean Architecture is to define the core domain of your application. The core domain represents the fundamental concepts and business rules of your application.
Create a new directory named “domain” within the project directory.
$ mkdir domain
Inside the “domain” directory, create a new file named “user.go”. This file will define the User entity in our application.
package domain
type User struct {
ID int
Name string
Email string
Password string
}
Next, create a file named “repository.go” in the same “domain” directory. This file will define the interfaces for interacting with the User repository.
package domain
type UserRepository interface {
FindByID(id int) (*User, error)
FindByEmail(email string) (*User, error)
Create(user *User) error
Update(user *User) error
Delete(id int) error
}
Implementing the Use Cases
Use cases represent the actions or operations that can be performed on the core domain entities. In our case, we will create use cases for user registration, login, and updating user profile.
Create a new directory named “usecase” within the project directory.
$ mkdir usecase
Inside the “usecase” directory, create a new file named “user_uc.go”. This file will define the interface and implementation for our user-related use cases.
package usecase
import "github.com/your-username/go-webapp-clean-architecture/domain"
type UserUseCase interface {
RegisterUser(name, email, password string) error
LoginUser(email, password string) (*domain.User, error)
UpdateUserProfile(id int, name, email string) error
}
type userUseCase struct {
userRepository domain.UserRepository
}
func NewUserUseCase(userRepository domain.UserRepository) UserUseCase {
return &userUseCase{
userRepository: userRepository,
}
}
func (uc *userUseCase) RegisterUser(name, email, password string) error {
// Implement user registration logic
}
func (uc *userUseCase) LoginUser(email, password string) (*domain.User, error) {
// Implement user login logic
}
func (uc *userUseCase) UpdateUserProfile(id int, name, email string) error {
// Implement updating user profile logic
}
Building the Infrastructure
The infrastructure layer is responsible for handling external dependencies, such as the database. In this tutorial, we will use SQLite as our database.
Create a new directory named “repository” within the project directory.
$ mkdir repository
Inside the “repository” directory, create a new file named “user_repo.go”. This file will define the implementation of the UserRepository interface.
package repository
import (
"github.com/your-username/go-webapp-clean-architecture/domain"
"gorm.io/gorm"
)
type userRepository struct {
db *gorm.DB
}
func NewUserRepository(db *gorm.DB) domain.UserRepository {
return &userRepository{
db: db,
}
}
func (r *userRepository) FindByID(id int) (*domain.User, error) {
// Implement finding a user by ID
}
func (r *userRepository) FindByEmail(email string) (*domain.User, error) {
// Implement finding a user by email
}
func (r *userRepository) Create(user *domain.User) error {
// Implement creating a new user
}
func (r *userRepository) Update(user *domain.User) error {
// Implement updating a user
}
func (r *userRepository) Delete(id int) error {
// Implement deleting a user
}
Creating the Web Application
Now that we have defined the core domain, implemented the use cases, and built the infrastructure layer, it’s time to create the web application layer.
Create a new directory named “delivery” within the project directory.
$ mkdir delivery
Inside the “delivery” directory, create a new file named “handler.go”. This file will define the HTTP handlers for our web application.
package delivery
import (
"encoding/json"
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/your-username/go-webapp-clean-architecture/domain"
"github.com/your-username/go-webapp-clean-architecture/usecase"
)
type UserHandler struct {
userUseCase usecase.UserUseCase
}
func NewUserHandler(userUseCase usecase.UserUseCase) *UserHandler {
return &UserHandler{
userUseCase: userUseCase,
}
}
func (h *UserHandler) RegisterUser(w http.ResponseWriter, r *http.Request) {
// Implement registering a new user using the user use case
}
func (h *UserHandler) LoginUser(w http.ResponseWriter, r *http.Request) {
// Implement user login using the user use case
}
func (h *UserHandler) UpdateUserProfile(w http.ResponseWriter, r *http.Request) {
// Implement updating user profile using the user use case
}
func (h *UserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, `{"error": "Not Found"}`)
}
func SetUpRoutes(userHandler *UserHandler) *mux.Router {
router := mux.NewRouter()
router.HandleFunc("/register", userHandler.RegisterUser).Methods("POST")
router.HandleFunc("/login", userHandler.LoginUser).Methods("POST")
router.HandleFunc("/update-profile/{id}", userHandler.UpdateUserProfile).Methods("PUT")
router.NotFoundHandler = userHandler
return router
}
Finally, create the main.go file in the project directory for starting the web server.
package main
import (
"log"
"net/http"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"github.com/your-username/go-webapp-clean-architecture/delivery"
"github.com/your-username/go-webapp-clean-architecture/repository"
"github.com/your-username/go-webapp-clean-architecture/usecase"
)
func main() {
// Set up the database connection
db, err := gorm.Open(sqlite.Open("database.db"), &gorm.Config{})
if err != nil {
log.Fatal(err)
}
// Migrate the database tables
db.AutoMigrate(&domain.User{})
// Initialize the repositories
userRepository := repository.NewUserRepository(db)
// Initialize the use cases
userUseCase := usecase.NewUserUseCase(userRepository)
// Initialize the HTTP handlers
userHandler := delivery.NewUserHandler(userUseCase)
// Set up the routes
router := delivery.SetUpRoutes(userHandler)
// Start the server
log.Println("Server started at http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", router))
}
Conclusion
Congratulations! You have successfully built a Go web application using Clean Architecture. We covered the process of structuring your project using the core domain, use cases, infrastructure, and web application layers. You also learned how to create HTTP handlers and set up routes using the Gorilla Mux package. Feel free to expand upon this application by adding more entities, use cases, and functionality.