Hands-On Software Architecture with Golang starts with a brief introduction to architectural elements, Go, and a case study to demonstrate architectural principles. You’ll then move on to look at code-level aspects such as modularity, class design, and constructs specific to Golang and implementation of design patterns. As you make your way through the chapters, you’ll explore the core objectives of architecture such as effectively managing complexity, scalability, and reliability of software systems. You’ll also work through creating distributed systems and their communication before moving on to modeling and scaling of data. In the concluding chapters, you’ll learn to deploy architectures and plan the migration of applications from other languages. – source

Engineering principles

High-level design

This is the decomposition of the system into high-level components. This serves as the blueprint that the product and code need to follow at every stage of the product development life cycle. For example, once we have a layered architecture (see the following section), then we can easily identify for any new requirement to which layer each new component should

Quality attributes

We want high quality code, and this means no code checking would be allowed without unit tests and 90% code coverage

Product velocity

The product has a bounded value in time and, to ensure that there is high developer productivity, the team should build Continuous Integration / Continuous Deployment (CICD) pipelines from the start.

A/B testing

Every feature should have a flag, so that it can be shown only to an x percentage of users

Software Architecture


The role of the architect



The basic concept of a microservice is simple—it’s a simple, standalone application that does one thing only and does that one thing well. The objective is to retain the simplicity, isolation, and productivity of the early app. A microservice cannot live alone; no microservice is an island—it is part of a larger system, running and working alongside other microservices to accomplish what would normally be handled by one large standalone application.

Each microservice is:



The Go programming language was conceived in late 2007 by Robert Griesemer, Rob Pike, and Ken Thompson, as an open source programming language that aims to simplify programming and make it fun again. It’s sponsored by Google, but is a true open source project—it commits from Google first, to the open source projects, and then the public repository is imported internally.

The language was designed by and for people who write, read, debug, and maintain large software systems. It’s a statically-typed, compiled language with built-in concurrency and garbage collection as first-class citizens.

TODO Introduction

Ideas how to structure a Golang intro session

Object orientation

For polymorphic behavior, Go uses interfaces and duck typing:

“If it looks like a duck and quacks like a duck, it’s a duck.”

Duck typing:


A class is a blueprint, or a template for objects that share the same behavior and properties. Being a template, it can be used as a specification to create objects.


The individual constructs (or functions) by which you can invoke behavior on the object are called methods.


Encapsulation is the key guiding principle for class design. It implies exposing a contract for the behavior of objects and hiding volatile implementation details. The private attributes and methods are hidden inside a capsule according to a need-to-know basis

Encapsulation is defined as the wrapping up of data under a single unit. It is the mechanism that binds together code and the data it manipulates. In a different way, encapsulation is a protective shield that prevents the data from being accessed by the code outside this shield. – Encapsulation in Golang


This ability of an interface method to behave differently based on the actual object is called polymorphism and is key to many design patterns


An alternative to inheritance is to delegate behavior, also called composition. Instead of an is a, this is a has a relationship. It refers to combining simple types to make more complex ones.

Over Inheritance

Main concept in Golang:

Classes implement an interface—which is the contract the base class offers. Functionality reuse happens through having references to objects, rather than deriving from classes. This is why many people, including people who code in Go, have the Composition Over Inheritance principle

Good example

  package main

  import (

  type author struct {
      firstName string
      lastName  string
      bio       string

  func (a author) fullName() string {
      return fmt.Sprintf("%s %s", a.firstName, a.lastName)

  type post struct {
      title   string
      content string

  func (p post) details() {
      fmt.Println("Title: ", p.title)
      fmt.Println("Content: ", p.content)
      fmt.Println("Author: ", p.fullName())
      fmt.Println("Bio: ",

  func main() {
      author1 := author{
          "Golang Enthusiast",
      post1 := post{
          "Inheritance in Go",
          "Go supports composition instead of inheritance",

Whenever one struct field is embedded in another, Go gives us the option to access the embedded fields as if they were part of the outer struct. This means that in line no. 11 of the above code can be replaced with p.fullName()

Design patterns

Design patterns are solutions to recurring problems in software engineering. Rather than a comprehensive solution, a design pattern is a description of a problem and a template of how to solve it. This template then becomes usable in many different contexts.


Design principles



Creational design patterns are design patterns that deal with object creation mechanisms in a safe and efficient manner and decouple clients from implementation specifics. With these patterns, the code using an object need not know details about how the object is created, or even the specific type of object, as long as the object adheres to the interface expected.


Behavioral design patterns are design patterns that identify communication patterns among objects and provide solution templates for specific situations. In doing so, these patterns increase the extensibility of the interactions


In Software Engineering, Structural Design Patterns are Design Patterns that ease the design by identifying a simple way to realize relationships between entities – Source

Scaling applications

Distributed algorithms

Google’s MapReduce

Scalability bottlenecks

Scaling systems

The Art of Scalability (Book)

Figure 2: 3D scalability model / (c) The scalability Book

Figure 2: 3D scalability model / (c) The scalability Book

Distributed systems


Distributed system quirks

In 1994, Peter Deutsch, who worked at Sun Microsystems, wrote about common wrong assumptions that developers/architects make, which cause things to go wrong in distributed systems. In 1997, James Gosling added to this list to create what is commonly known as the eight fallacies of distributed computing. They are described here.

Distributed architectures



This architectural style can be thought of as an inverted pyramid of reuse, where each layer aggregates the responsibilities and abstractions of the layer directly beneath it. When the layers are on different machines, they are called tiers. The most common example of strict layering is where components in one layer can interact only with components in the same layer or with components from the layer directly below it.


Distributed computations

EDA (Event-driven Architecture)

Figure 6: (c) Jyotiswarup Raiturkar

Figure 6: (c) Jyotiswarup Raiturkar


A messaging system can be judged on its performance in four aspects—scalability, availability, latency, and throughput.


This is how the system is able to handle increases in load without noticeable degradation of the other two factors, latency or availability. Here, load can mean things such as the number of topics, consumers, producers, messages/sec, or average message size


In a distributed system, a variety of problems can occur at a unit level (servers, disks, network, and so on). The system’s availability is a measure of how resilient the system is to these failures so that it is available to end users


This is how much time it takes for a message to get to a consumer from a producer


This is how many messages can be processed per second by the messaging system

Broker-based messaging

A broker is a component that acts as the intermediary in a messaging system. Here, the clients connect to the broker and not to each other directly. Whenever clients want to send and receive messages, they need to specify a mailbox/topic/queue on the broker. Producers connect to the broker and send messages to a specific queue. Consumers connect to the broker and specify queue name from which they want to read messages.



Integration patterns

Using Golang channels




Richardson Maturity Level

Anti-fragile systems

Engineering reliability