Chapter 11 - Coding our Microservice (Part 1)
Building a Microservice With Go

The following list is the previous chapters of this series:
- Chapter 1 — Introduction to Microservices
- Chapter 2 — Introduction to Microservices (Part 2)
- Chapter-3 Domain-driven design and microservices
- Chapter -4 Advantages of using Go for web development
- Chapter — 5 Understanding HTTP Protocols and REST APIs
- Chapter-6 HTTP Package in Golang
- Chapter 7 — Step-by-step guide on building a simple web service using Go
- Chapter 8 “ Documenting our APIs — Introduction to OpenAPI”
- Chapter 9 -Building a Microservice with Go (Defining the technologies )
- Chapter 10-Structuring our project
I recommend you take a look at the previous chapters if you have not read them yet. That will help you to get more knowledge in this wonderful world of “Microservices architecture”.
Structure
In this chapter, the following topics will be covered:
- Defining packages
- Best Practices
- Defining Components
- Coding the principal components used by the microservices according to their Domain.
Introduction
Our journey begins with the construction of a highly comprehensive microservice — the Shopping Cart. Throughout this meticulously planned process, we will methodically progress, step by step, by assembling each integral component of our final solution. Starting with the creation of the directory that houses our code, we will proceed to establish the essential DDD structure. We’ll then define the logic, conduct unit testing, and culminate with the deployment of the service encapsulated within a Docker container.
This endeavor promises not only a deep exploration of the “Shopping Cart” microservice but also an illustration of best practices and a comprehensive approach to microservices development. By the end, we’ll have not just a functional service but also a valuable knowledge resource for crafting robust microservices in the future used in this series or in the real world.
Starting with the code
Creating root folder
To start building this microservice let’s start creating the “root folder” named “shopping cart”:
mkdir shopping-cartAfter having the root folder we have to start the golang module:
go mod init github.com/go-microservices/shopping-cart-serviceOnce we have the root folder and the go mod generated is time to start building the project structure, in that step each one of the packages and files will be defined for our code implementation.
Shopping-Cart packages structure
In this step, we will dive into the structure and fundamental components of the Shopping Cart microservice. We will examine in detail how this microservice has been designed and organized, along with the packages that play a crucial role in its functioning. This in-depth understanding lays the foundation for effective implementation and efficient management of the shopping cart within our microservices ecosystem.
shopping-cart/
├── cmd/
│ └── main.go
├── internal/
│ ├── app/
│ │ └── api
│ │ ├── dto/
│ │ │ ├── shopping_cart.go
│ │ │ └── product.go
│ │ ├── handler/
│ │ │ ├── product/
│ │ │ │ └── handler.go
│ │ │ └── shopping_cart/
│ │ │ └── handler.go
│ │ ├── api.go
│ │ └── routes.go
│ ├── domain/
│ │ ├── shopping_cart/
│ │ │ ├── shopping_cart.go
│ │ │ ├── shopping_cart_repository.go
│ │ │ └── shopping_cart_service.go
│ │ └── product/
│ │ ├── product.go
│ │ ├── product_repository.go
│ │ └── product_service.go
│ ├── infrastructure/
│ │ ├── persistence/
│ │ │ ├── shopping_cart_repository.go
│ │ │ └── product_repository.go
│ │ └── database/
│ │ ├── db_connection.go
│ │ └── database_config.go
│ └── shared/
│ ├── error/
│ │ └── error.go
│ └── logger/
│ └── logger.go
└── go.modOnce the project structure is defined, and all the files and packages are placed it is time to start writing the needed code for each one of them. It’s worth noting that, at this stage, each of the “go” files can remain empty, solely defining the package with the same name as the containing directory.
Don’t worry if there is no description about what each file is, that description is part of the following steps where the content of each file will be developed.
Keep in mind, this exploration unveiled the critical packages and files that form the backbone of this microservice, this comprehensive understanding serves as the cornerstone for the seamless implementation and effective management of the shopping cart within our study case. With this foundation in place, we are well-equipped to embark on the journey of building and optimizing this vital application component.
Developing the domain components
After defining the code structure is time to start defining the domain components. In this case, it can be seen that two subdomains are evaluated as “shopping cart” and “products” (maybe there could be more but for this example, it is decided to keep it abstract and easy to develop). Each one of the subdomains is defined as a package with the name “shopping_cart” and “product” both as part of the domain package:
...
│ ├── domain/
│ │ ├── shopping_cart/
│ │ │ ├── shopping_cart.go
│ │ │ ├── shopping_cart_repository.go
│ │ │ └── shopping_cart_service.go
│ │ └── product/
│ │ ├── product.go
│ │ ├── product_repository.go
│ │ └── product_service.go
...This step is focused on defining the content and the logic related to each one of the domains.
let’s start developing the shopping_cart:
/domain/shopping_cart/shopping_cart.go
package shoppingcart
import (
uuid "github.com/satori/go.uuid"
)
type ShoppingCart struct {
ID *uuid.UUID `json:"id,omitempty"`
UserID *uuid.UUID `json:"user_id"`
}
func NewShoppingCart(id *uuid.UUID, userID *uuid.UUID) ShoppingCart {
return ShoppingCart{
ID: id,
UserID: userID,
}
}Explaining the code: This code establishes the “ShoppingCart” entity within the realm of Domain-Driven Design (DDD). The presence of this entity, along with its constructor, plays a pivotal role in crafting a shopping cart component within a DDD-oriented system. It empowers the modeling and control of shopping cart-related data, fostering robust functionality and adherence to DDD principles.
This entity represents a shopping cart and has two main attributes:
The “ID” property is a unique identifier (UUID) that is used to uniquely identify each shopping cart. It’s optional in the JSON response, which means you don’t need to provide it when creating a new cart, as it will usually be automatically generated on the server.
“UserID” property is a unique identifier (UUID) that represents the user to whom the shopping cart belongs. This attribute is required and cannot be empty.
Identifier properties have been declared to be of type UUID, since using UUIDs instead of integers or strings as identifiers improves application scalability and security by avoiding ID collisions and reducing the chance of guessing valid identifiers.
The NewShoppingCart function is used to create a new instance of the “ShoppingCart” entity. It takes two parameters: a UUID for the ID and a UUID for the userID, and returns an instance of ShoppingCart with the supplied values.
/domain/shopping_cart/shopping_cart_service.go
package shoppingcart
import (
uuid "github.com/satori/go.uuid"
"github.com/sirupsen/logrus"
)
type shoppingCartService struct {
logger *logrus.Logger
shoppingCartRepo ShoppingCartRepository
}
func NewShoppingCartService(scRepo ShoppingCartRepository, logger *logrus.Logger) shoppingCartService {
return shoppingCartService{
logger: logger,
shoppingCartRepo: scRepo,
}
}
func (sc shoppingCartService) Create(spc ShoppingCart) error {
sc.logger.Info("On Create Shopping")
//more logic here ...
return sc.shoppingCartRepo.Create(spc)
//more logic here ...
}
func (sc shoppingCartService) GetByUserID(userId *uuid.UUID) (*ShoppingCart, error) {
sc.logger.Info("On Get Shopping Cart for user")
//more logic here ...
shoppingCart, err := sc.shoppingCartRepo.GetByUserID(userId)
if err != nil {
sc.logger.Errorf("On Get Shopping Cart for user - error quering db: %d", err)
return nil, err
}
//more logic here ...
return shoppingCart, nil
}Explaining the code: This code exemplifies a “shoppingCartService,” an integral part of a DDD system. Within a DDD architecture, services like these serve as intermediaries between the application and persistence layers, encapsulating domain and application-specific functionalities within reusable components.
Services play a pivotal role in DDD by ensuring a clear separation of responsibilities and enabling a more coherent and efficient modeling of the system based on core business domain concepts. They are indispensable components of a DDD-based software architecture.
In this specific instance, the shoppingCartService offers methods for creating shopping carts and retrieving them by user ID. These methods abstract the intricacies of business logic and database interactions. Additionally, a logging mechanism is utilized to track crucial service activities and events, enhancing system observability.
A detailed explanation of this service and its purpose is provided here:
Function NewShoppingCartService It receives two parameters: scRepo which represents a shopping cart repository and logger which references the singleton logger created for the application. This function allows initializing the service with the necessary dependencies.
Create function This function is used to create a new shopping cart. It takes an object of type ShoppingCart as an argument, which contains relevant information about the shopping cart to be created userID.
This function calls the Create method of the shopping cart repository “shoppingCartRepo” to perform the creation in the persistence layer. If the create operation is successful, no error is returned. Otherwise, an error is propagated.
GetByUserID function This function looks for a shopping cart based on the provided user ID. Receives a UUID representing the user as input. Similar to the “Create” function, it calls the “GetByUserID” method of the shopping cart repository to retrieve the cart related to the user.
If the operation is successful and a cart is found, the “ShoppingCart” object is returned. If any error occurs during the database lookup, an error message is logged and the error is propagated.
/domain/shopping_cart/shopping_cart_repository.go
package shoppingcart
import uuid "github.com/satori/go.uuid"
type ShoppingCartRepository interface {
Create(spc ShoppingCart) error
GetByUserID(userId *uuid.UUID) (*ShoppingCart, error)
}Explaining the code: This interface serves as a contract that outlines the database access methods required for interacting with the shopping cart. What’s crucial to note is that this interface finds its concrete implementation in the “infrastructure” package, where all the underlying logic is meticulously defined. This implementation will then be seamlessly injected into the dependencies specified within the structure outlined in “shopping_cart_service.go,” namely, the “shoppingCartService.” This separation of concerns and implementation details ensures a clean and maintainable architecture for the shopping cart module.
Now is the time to develop product logic:
/domain/product/product.go
package product
import (
uuid "github.com/satori/go.uuid"
)
type Product struct {
ID *uuid.UUID
Name string
Quantity int
Description string
ShoppingCartID *uuid.UUID
}
func NewProduct(name string, quantity int, description string,
shoppingCartID *uuid.UUID) Product {
return Product{
Name: name,
Quantity: quantity,
Description: description,
ShoppingCartID: shoppingCartID,
}
}
Explaining the code: This code establishes the “Product” entity within the realm of Domain-Driven Design (DDD). The presence of this entity, along with its constructor, plays a pivotal role in crafting a shopping cart component within a DDD-oriented system. It empowers the modeling and control of product data, fostering robust functionality and adherence to DDD principles.
The provided code represents an entity within the context of DDD related to products in a shopping cart system. This entity, called “Product,” is essential for implementing a DDD-based shopping cart system as it allows modeling and managing information related to products in the cart.
The “Product” entity has the following characteristics:
- ID: A unique identifier for each product, typically represented by a UUID (Universal Unique Identifier) to ensure its uniqueness.
- Name: The name of the product, which provides a brief and meaningful description of the item.
- Description: Provides a brief description and lists the product characteristics.
- Quantity: The quantity of units of this product in the shopping cart.
The code defines the function NewProduct, which allows for the convenient creation of product instances by providing all the necessary attributes. This entity encapsulates data related to products in the shopping cart and can be used to perform calculations and operations related to product management in a shopping cart.
/domain/product/product_service.go
package product
import (
uuid "github.com/satori/go.uuid"
"github.com/sirupsen/logrus"
)
type productService struct {
logger *logrus.Logger
productRepo ProductRepository
}
func NewProductService(productRepo ProductRepository, logger *logrus.Logger) productService {
return productService{
logger: logger,
productRepo: productRepo,
}
}
func (ps productService) Create(product Product) error {
ps.logger.Info("On Create product service")
if err := ps.productRepo.Create(product); err != nil {
return err
}
return nil
}
func (ps productService) GetAll(shoppingCartId uuid.UUID) ([]Product, error) {
ps.logger.Info("On GetAll product service")
return ps.productRepo.Get(shoppingCartId)
}
func (ps productService) Delete(productId uuid.UUID) error {
ps.logger.Info("On Delete product service")
err := ps.productRepo.Delete(productId)
if err != nil {
return err
}
return nil
}
func (ps productService) UpdateQuantity(productID uuid.UUID, quantity int) error {
ps.logger.Info("On Update product service")
err := ps.productRepo.UpdateQuantity(productID, quantity)
if err != nil {
return err
}
return nil
}Explaining the code: This code exemplifies a “ProductService,” an integral part of a Domain-Driven Design (DDD) system. Within a DDD architecture, services like these serve as intermediaries between the application and persistence layers, encapsulating domain and application-specific functionalities within reusable components.
Services play a pivotal role in DDD by ensuring a clear separation of responsibilities and enabling a more coherent and efficient modeling of the system based on core business domain concepts. They are indispensable components of a DDD-based software architecture.
In this specific instance, the “ProductService” offers methods to manage the products related to a shopping cart. These methods abstract the intricacies of business logic and database interactions. Additionally, a logging mechanism is utilized to track crucial service activities and events, enhancing system observability.
A detailed explanation of this service and its purpose are provided below:
The structure “productService” represents the product management service. Its function, “NewProductService” accepts two parameters: “productRepo” which is of the product repository interface type, and “logger” which is of the logrus.Logger type.
The “NewProductService” function creates a new instance of the product management service. It receives a product repository and an event logger as parameters, which are then stored in the “productService” structure.
The “Create” method is used to create a new product. It logs an informative event in the logger and then calls the “Create” method of the product repository. If the creation process is successful, it doesn’t return errors; otherwise, it returns an error if any issues occur.
The “GetAll” method is used to retrieve all products associated with a specific shopping cart, identified by a “shoppingCartId”. It logs an informative event in the logger and then calls the “Get” method of the product repository to obtain the list of products. It returns the list of products and a possible error.
The “Delete” method is used to delete a product by its `productId`. It logs an informative event in the logger and then calls the “Delete” method of the product repository. It returns an error if the deletion is unsuccessful and no error if it succeeds.
The “UpdateQuantity” method is used to update the quantity of a product. It logs an informative event in the logger and then calls the “UpdateQuantity” method of the product repository. It returns an error if the update is unsuccessful and no error if it succeeds.
This service acts as an intermediate layer that encapsulates business logic related to products and serves as an interface between the application layer and the product repository, enabling a clear separation of responsibilities in Domain-Driven Design-based software design.
/domain/product/product_repository.go
package product
import uuid "github.com/satori/go.uuid"
type ProductRepository interface {
Create(product Product) error
Get(shoppingCartID uuid.UUID) ([]Product, error)
Delete(productID uuid.UUID) error
UpdateQuantity(productID uuid.UUID, quantity int) error
}Explaining the code: This interface serves as a contract that outlines the database access methods required for interacting with the products related to a shopping cart. What’s crucial to note is that this interface finds its concrete implementation in the “infrastructure” package, where all the underlying logic is meticulously defined. This implementation will then be seamlessly injected into the dependencies specified within the structure outlined in “product_service.go,” namely, the “productService.” This separation of concerns and implementation details ensures a clean and maintainable architecture for the shopping cart module.
Next readings …
Wait for Chapter 12 “Coding our Microservice (Part 2) — Developing the Database Connection”.
https://readmedium.com/chapter-12-building-a-microservice-with-go-part-2-0a488f58ff57





