gomicro is a Go microservices architecture using goserve micro framework. The blogging platform example is built using Kong API gateway, NATS, Mongo, Redis, and Docker. It implements authentication, authorization, and apikey protection.
APACHE-2.0 License
This project creates a blogging API service using goserve micro framework. In this project Kong is used as the API gateway and NATS for the interservice communication. Each service has its own Mongo database and Redis database (Note: a single mongo and redis server is used for multiple databases).
This project breaks down the monolithic go blog backend project provided at goserve repository. It uses the goserve REST API framework to build the auth_service, and blog_service.
More details on the REST part can be found at goserve github repo
Helper/Optional Directories
Request Flow
apikey-auth-plugin
calls http://auth:8000/verify/apikey
within docker networkAuthentication
Authorization
This Authentication and Authorization implementation gives freedom to individual services to decide on the public, protected, and restricted APIs on its own.
1. Without Load Balancing
2. With Load Balancing
vscode is the recommended editor - dark theme
1. Get the repo
git clone https://github.com/unusualcodeorg/gomicro.git
2. Generate RSA Keys
go run .tools/rsa/keygen.go
3. Create .env files
go run .tools/copy/envs.go
4. Run Docker Compose Install Docker and Docker Compose. Find Instructions Here.
Without Load Balancing
docker-compose up --build
OR
With Load Balancing
docker-compose -f docker-compose-load-balanced.yml up --build
You will be able to access the api from http://localhost:8000
If having any issue make sure 8000 port is not occupied
Information about the framework
API framework details can be found at goserve github repo
To communicate among services through nats a message struct is required
package message
type SampleMessage struct {
Field1 string `json:"field1,omitempty"`
Field2 string `json:"field2,omitempty"`
}
func EmptySampleMessage() *SampleMessage {
return &SampleMessage{}
}
func NewSampleMessage(f1, f2 string) *SampleMessage {
return &SampleMessage{
Field1: f1,
Field2: f2,
}
}
micro.Controller
from github.com/unusualcodeorg/goserve/arch/micro
MountNats
is used to mount the endpoints that other services can call through natsMountRoutes
is used to mount the endpoints for http clientspackage sample
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/unusualcodeorg/gomicro/microservice2/api/sample/message"
"github.com/unusualcodeorg/goserve/arch/micro"
"github.com/unusualcodeorg/goserve/arch/network"
)
type controller struct {
micro.BaseController
service Service
}
func NewController(
authMFunc network.AuthenticationProvider,
authorizeMFunc network.AuthorizationProvider,
service Service,
) micro.Controller {
return &controller{
BaseController: micro.NewBaseController("/sample", authMFunc, authorizeMFunc),
service: service,
}
}
func (c *controller) MountNats(group micro.NatsGroup) {
group.AddEndpoint("ping", micro.NatsHandlerFunc(c.pingHandler))
}
func (c *controller) MountRoutes(group *gin.RouterGroup) {
group.GET("/ping", c.getEchoHandler)
group.GET("/service/ping", c.getServicePingHandler)
}
func (c *controller) pingHandler(req micro.NatsRequest) {
fmt.Println(string(req.Data()))
msg := message.NewSampleMessage("from", "microservice2")
c.SendNats(req).Message(msg)
}
func (c *controller) getEchoHandler(ctx *gin.Context) {
c.Send(ctx).SuccessMsgResponse("pong!")
}
func (c *controller) getServicePingHandler(ctx *gin.Context) {
msg := message.NewSampleMessage("from", "microservice2")
received, err := c.service.GetSampleMessage(msg)
if err != nil {
c.Send(ctx).MixedError(err)
return
}
c.Send(ctx).SuccessDataResponse("success", received)
}
micro.RequestBuilder[message.SampleMessage]
is used to call other services to get SampleMessage
through natspackage sample
import (
"github.com/unusualcodeorg/gomicro/microservice2/api/sample/dto"
"github.com/unusualcodeorg/gomicro/microservice2/api/sample/message"
"github.com/unusualcodeorg/gomicro/microservice2/api/sample/model"
"github.com/unusualcodeorg/goserve/arch/micro"
"github.com/unusualcodeorg/goserve/arch/mongo"
"github.com/unusualcodeorg/goserve/arch/network"
"github.com/unusualcodeorg/goserve/arch/redis"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type Service interface {
FindSample(id primitive.ObjectID) (*model.Sample, error)
GetSampleMessage(data *message.SampleMessage) (*message.SampleMessage, error)
}
type service struct {
network.BaseService
sampleQueryBuilder mongo.QueryBuilder[model.Sample]
infoSampleCache redis.Cache[dto.InfoSample]
sampleRequestBuilder micro.RequestBuilder[message.SampleMessage]
}
func NewService(db mongo.Database, store redis.Store, natsClient micro.NatsClient) Service {
return &service{
BaseService: network.NewBaseService(),
sampleQueryBuilder: mongo.NewQueryBuilder[model.Sample](db, model.CollectionName),
infoSampleCache: redis.NewCache[dto.InfoSample](store),
sampleRequestBuilder: micro.NewRequestBuilder[message.SampleMessage](natsClient, "microservice1.sample.ping"),
}
}
func (s *service) GetSampleMessage(data *message.SampleMessage) (*message.SampleMessage, error) {
return s.sampleRequestBuilder.Request(data).Nats()
}
func (s *service) FindSample(id primitive.ObjectID) (*model.Sample, error) {
filter := bson.M{"_id": id}
msg, err := s.sampleQueryBuilder.SingleQuery().FindOne(filter, nil)
if err != nil {
return nil, err
}
return msg, nil
}
NatsClient should be created to connect and talk to nats
natsConfig := micro.Config{
NatsUrl: env.NatsUrl,
NatsServiceName: env.NatsServiceName,
NatsServiceVersion: env.NatsServiceVersion,
Timeout: time.Second * 10,
}
natsClient := micro.NewNatsClient(&natsConfig)
More details on nats can be found at nats-io/nats.go. goserve creates a simple wrapper over this library.
micro.Module[module]
should used for instance creation in place of network.Module[module]
micro.NewRouter
should be used in place of network.NewRouter
micro.BaseController
should be used in place of network.BaseController
micro.Controller
should be used in place of network.Controller
Subscribe to the YouTube channel UnusualCode
for understanding the concepts used in this project:
Please feel free to fork it and open a PR.