Microservices in Go: Architecture and Practice for Beginners

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Microservices in Go: From Idea to Production



Go (Golang) has earned a reputation as the ideal language for building microservice architecture. Thanks to built-in concurrency support, fast compilation, minimal memory consumption, and simple syntax, Go allows you to create lightweight and high-performance services. In this article, we will break down the key principles of building microservices in Go, examine practical examples and best practices that will help you avoid common mistakes.



Why Go is the Ideal Choice for Microservices?



Before diving into the architecture, let's understand why Go has become the de facto standard for microservices:



  • Fast Startup: Go compiles into a single binary file, simplifying deployment and scaling.
  • Built-in Concurrency: goroutines and channels allow efficient handling of thousands of requests without complex threads.
  • Minimal Memory Consumption: each microservice in Go consumes significantly fewer resources than similar ones in Java or Python.
  • Ease of Maintenance: static typing and a strict compiler reduce the number of errors at runtime.


Microservice Architecture in Go: Basic Template



Let's look at a typical structure of a microservice in Go. For example, we will create a simple user management service.



Project Structure



user-service/├── cmd/│   └── server/│       └── main.go├── internal/│   ├── handler/│   │   └── user.go│   ├── service/│   │   └── user.go│   ├── repository/│   │   └── postgres.go│   └── model/│       └── user.go├── pkg/│   └── middleware/│       └── logging.go├── config/│   └── config.go├── go.mod└── Dockerfile


This template follows the principles of Clean Architecture: dependencies point inward, and business logic is isolated from infrastructure.



Example Main File (cmd/server/main.go)



package main

import ( "log" "net/http" "os" "user-service/internal/handler" "user-service/internal/repository" "user-service/internal/service")

func main() { // Dependency initialization db, err := repository.NewPostgresDB("postgres://user:pass@localhost:5432/users") if err != nil { log.Fatal(err) } defer db.Close()

userRepo := repository.NewUserRepository(db) userSvc := service.NewUserService(userRepo) userHandler := handler.NewUserHandler(userSvc)

// Route setup mux := http.NewServeMux() mux.HandleFunc("GET /users/{id}", userHandler.GetByID) mux.HandleFunc("POST /users", userHandler.Create) mux.HandleFunc("PUT /users/{id}", userHandler.Update)

// Server startup port := os.Getenv("PORT") if port == "" { port = "8080" } log.Printf("Server starting on :%s", port) log.Fatal(http.ListenAndServe(":"+port, mux))}


Implementing Microservice Layers



Let's split the code into three main layers: handler (transport), service (business logic), and repository (data access).



Handler Layer: Handling HTTP Requests



// internal/handler/user.gopackage handler

import ( "encoding/json" "net/http" "user-service/internal/model" "user-service/internal/service")

type UserHandler struct { svc *service.UserService}

func NewUserHandler(svc *service.UserService) *UserHandler { return &UserHandler{svc: svc}}

func (h *UserHandler) GetByID(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") user, err := h.svc.GetByID(r.Context(), id) if err != nil { http.Error(w, err.Error(), http.StatusNotFound) return } json.NewEncoder(w).Encode(user)}

func (h *UserHandler) Create(w http.ResponseWriter, r *http

Blogs

Book Recommendations