Chapter 14 Coding our Microservice (Part 4)
Explaining the shared package

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
- Chapter 11- Coding our Microservice (Part 1)
- Chapter 12 — Coding our Microservice (Part 2)
- Chapter 13 — Coding our Microservice (Part 3)
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”.
Intruduction
In this chapter, we delve into a pivotal package of our microservice architecture, the “shared” package. This package serves as the cornerstone for shared utilities and functionalities that are indispensable across various layers of our microservices ecosystem.
We will explore two key sub-packages within the “shared” package, “error” and “logger.” Through meticulous examination, we’ll elucidate their significance, functionalities, and how they contribute to a robust and cohesive microservices infrastructure.
...
│ └── shared/
│ ├── error/
│ │ └── error.go
│ └── logger/
│ └── logger.go
...Next, describe and analyze each one of the files defined under this package.
/shared/error/error.go
package error
import (
"errors"
"net/http"
)
var (
RecordNotFound = errors.New("record not found")
)
type TemplateError struct {
Status int `json:"status"`
Message string `json:"msg"`
Err error `json:"error"`
}
type errorResponse struct {
Msg string `json:"msg"`
}
func (e TemplateError) Error() string {
return e.Err.Error()
}
func NewRecordNotFoundError(msg string, err error) TemplateError {
return TemplateError{
Status: http.StatusNotFound,
Message: msg,
Err: err,
}
}
func NewUnespectedError(msg string, err error) TemplateError {
return TemplateError{
Status: http.StatusInternalServerError,
Message: msg,
Err: err,
}
}
func NewBadRequestError(msg string, err error) TemplateError {
return TemplateError{
Status: http.StatusBadRequest,
Message: msg,
Err: err,
}
}
func NewValidationError(msg string, err error) TemplateError {
return TemplateError{
Status: http.StatusBadRequest,
Message: msg,
Err: err,
}
}
func NewErrorResponse(message string) errorResponse {
return errorResponse{
Msg: message,
}
}Before describing the preceding code, it is important to mention why it is good practice to handle errors and return the correct response based on the following points:
User Experience When an HTTP request error occurs, such as a bad request or failed authentication, providing an appropriate response helps users understand and fix the problem, improving the user experience and the reliability of your application.
Security Handling errors properly prevents attackers from obtaining sensitive information about your application or system, such as database details or the internal structure of your application, since we can format errors by showing only the information needed to understand the error that occurred.
Debugging and Monitoring Proper error handling makes it easy to identify and fix problems in your application. Detailed logs and meaningful error responses are essential for monitoring and troubleshooting.
Compliance with rules and regulations Depending on your application context, you may need to comply with certain standards and regulations that require proper error handling.
In general, handling errors and giving adequate responses in a Gin-Gonic handler is essential to guarantee a reliable, secure, and maintainable application, in addition to improving the user experience.
Explaining the code: The provided code establishes a collection of error definitions and introduces a structured mechanism, TemplateError, for handling and responding to errors systematically.
This approach proves particularly valuable in web applications, where the delivery of precise HTTP responses in error scenarios is pivotal. The functions NewRecordNotFoundError, NewUnespectedError, NewBadRequestError, and NewValidationError serve the purpose of instantiating TemplateError objects, each assigned a distinct HTTP status code based on the nature of the error. This standardized error-handling approach promotes consistency throughout the application, facilitating the delivery of well-structured error responses.
For our use case and code example those functions are referenced on the repositories and handlers logic. On the repositories side, we are sending back the correct and formatted error, on the other hand, the helper function (previously analyzed) gets the error, after that validates the type of custom error, and then sets the correct response, adding the message and the response code.
/shared/logger/logger.go
package logger
import "github.com/sirupsen/logrus"
var logger *logrus.Logger
func NewLogger() *logrus.Logger {
if logger == nil {
logger = logrus.New()
logger.SetFormatter(&logrus.JSONFormatter{})
}
return logger
}Before delving into the details of the code found in logger.go, it’s crucial to emphasize the significance of logs in any application, particularly within the context of microservices. Logs play a multifaceted role, offering several key benefits, such as the following:
Debugging and Troubleshooting Logs serve as a vital tool for debugging and troubleshooting in microservices. When errors or unexpected behavior occur, logs act as a crucial resource for identifying and resolving issues. They offer essential insights into the microservice’s behavior, including the state of variables, input and output values, and any exceptions that were raised. These details are invaluable for pinpointing the root cause of problems and implementing effective solutions.
Audit Logs play a pivotal role in enabling comprehensive auditing of all actions executed within the microservice. This functionality is indispensable for adhering to regulatory compliance standards and conducting thorough security audits.
Monitoring and Alerts Logs are instrumental in continuous monitoring processes, as monitoring systems can scrutinize them for unusual patterns or critical errors. These systems trigger timely alerts upon detecting anomalies, enabling swift responses to resolve issues before they impact end users.
History Tracking LLogs provide a historical record of all operations performed on the microservice. This historical record proves invaluable for tracking past events, dissecting the root causes of issues, and maintaining a comprehensive activity history.
Documentation in Real-time Logs effectively serves as real-time documentation, providing an up-to-the-minute of the microservice’s activities. This proves particularly beneficial for developers who rely on a clear understanding of the service’s behavior during maintenance or debugging tasks.
Explaining the code: In the provided code, the logrus library is employed to handle logs within the application. It adopts a straightforward approach by creating a single application-wide shared log instance. The rationale behind this practice is to avoid the proliferation of multiple logging instances throughout the application. Utilizing a shared instance, named “logger” in this context, prevents resource duplication and ensures uniformity in logging practices across the entire application.
Now, let’s dive into a step-by-step explanation of the code.
In the provided code, the logrus library is employed to handle logs within the application. It adopts a straightforward approach by creating a single application-wide shared log instance. The rationale behind this practice is to avoid the proliferation of multiple logging instances throughout the application. Utilizing a shared instance, named “logger” in this context, it prevents resource duplication and ensures uniformity in logging practices across the entire application.
logrus import The “github.com/sirupsen/logrus” library is imported which provides the advanced logging functionality in JSON format.
Declares the “logger” variable This variable will be used to maintain a single shared instance of logging across the entire application.
“NewLogger” function This function is the one used to get an instance of the registry. It works in the following way:
- Check if the “logger” variable has already been initialized. If it hasn’t, create a new log instance using “logrus.New()”.
- Set the log format so that logs are generated in JSON format using “logger.SetFormatter(&logrus.JSONFormatter{})”.
- Returns the logger variable.
The function and variable are initialized in “api.go” and passed to the components that need it.
Next readings …
Wait for Chapter 14 “Coding our Microservice (Part 5) — Implementing Unit Testing”.






