loading...

Golang, Microservices, and Monorepo

bastianrob profile image Robin ・2 min read

I've been coding in golang for the last 2 years (2018 - 2020), building microservices and committing it to a monorepo.
These years have got me thinking on how to structure Go project based on those 2 criteria: microservice and monorepo

Why Microservice

Business comes and go quickly, especially at the early phase of a startup.
The benefits of using microservice are:

  1. Each microservice is treated as 1 business domain
  2. Easier to spawn a new microservice without having any dependency to other services
  3. It's also easier to take down a microservice in case the business initiative failed, without having to refactor any code

But there are also some downsides on using microservice architecture.
In my experience, code sharing is one of the issues. Which can be tackled by using monorepo

Why Monorepo

One benefit of monorepo is code sharing.
But what the f is code sharing???

Imagine a scenario without monorepo:

  • There's an e-commerce named TOKOPA'EDI
  • They have 2 microservice named payment and invoice
  • Everytime a payment is made, it needs to copy the payment information into invoice

When invoice service accept payment information, invoice service do not have type Payment struct in its repository.
Therefore invoice service have to do some duplication of payment struct.

With monorepo, payment struct can be shared across multiple microservices

Project Structure

project
├── cmd                     # contains microservices 
│   ├── '{microservice a}'  # "microservice a" project which contains "main" function to start app
│   │   └── main.go
│   └── '{microservice b}'  # "microservice b" project, also only contains "main" function to start app
│       └── main.go
├── docker                  # dockefile for each microservices
│   ├── '{microservice a}'
│   └── '{microservice b}'
├── internal                # internal package of each microservice. codes inside internal package only visible to its own microservice and cannot be shared
│   ├── '{microservice a}'
│   │   ├── rest            # HTTP entry point for REST API
│   │   ├── grpc            # gRPC entry
│   │   ├── repo            # repository / connector to database
│   │   └── service         # business logic layer which is handled by the microservice
│   └── '{microservice b}'
│       ├── rest
│       ├── grpc
│       ├── repo
│       └── service
├── pkg                     # public package directory which can be shared across project / microservices
│   └── models              # data model which usually 1:1 relation with database
│       ├── something.go
│       └── another.go
└── readme.md

With above project structure, every object / code inside pkg is fair game and can be shared across microservices without having to copy-pasting object from another repository.

CMIIW ✌️

Posted on by:

bastianrob profile

Robin

@bastianrob

Coffee to Code Transpiler

Discussion

pic
Editor guide
 

Hi Robin, great introductory write up. I'm going to raise a counter point to the mono repo and specifically your point on sharing the payment struct.

In my experience, the biggest benefit of microservice comes from the decoupling of code. Sharing a struct between two services makes them really tightly coupled.

I've always preferred having separate data models, and using unit/integration tests to make sure the contract isn't broken.

 

Hi James, thanks for commenting.

I am well aware of the coupling/de-coupling by sharing data model.
Perhaps sharing struct is not the perfect example for this.

Another sharable code is about role & permission checking.
In my workplace we implemented an RBAC system with each microservice / business domain having it's own rule set.
The RBAC engine is shared across microservices in a single package.

 

Hi Robin, thanks for taking the time to reply.

Completely agree with that use case! If the same permission/role checking needs to happen with every service that screams our for shared code.

I think I'd still use an external reference (NuGet in the case of .NET) rather than direct refs to the local source files. But I do see your point now.

Thankyou for clarifying :-)

 

Hi Robin, nice write about monorepo.

As James I am to counter the benefits of monorepo. This problem that you listen up, share struct between microservices could be easily solved with private repository with payment or invoice models and imported into your microservice. Go modules is here to help you.

As James pointed, microservices is to decouple code not only APIs. Be aware, because coupling can bring a lot of problems already knows in software development community.