Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/al3rez/beers-srv

Enjoy serving 🍻 through micro-services using gRPC
https://github.com/al3rez/beers-srv

ddd golang grpc

Last synced: 27 days ago
JSON representation

Enjoy serving 🍻 through micro-services using gRPC

Awesome Lists containing this project

README

        

# beers
Enjoy serving 🍻 through micro-services using gRPC




[![asciicast](https://asciinema.org/a/XXLhQTinGqJdn7F5YR00Rp171.svg)](https://asciinema.org/a/XXLhQTinGqJdn7F5YR00Rp171)
## Stack

- **CI/CD**: Github Actions (e.g Review, Release automation `goreleaser`, etc)
- **Testing**: Go `testing` package
- **Serialization**: Protocol Buffers `protobuf`
- **OCI orchestration**: Kubernetes, Docker

## Architecture
Following DDD principles I've separated bounded contexts and behaviors (e.g. adding, removing, serializing, gRPC) into
multiple packages using service objects, database Layer abstraction, repository pattern, command-line interfaces so that
decoupling behaviors make both testing and shipping new features easier.

## Installation
Installing `beers-srv` is easy, using go get you can install the cmd line app `beers-cli` to interact with gRPC server. First you'll need Google's Protocol Buffers installed.
```
$ brew install protobuf
$ go get -u github.com/azbshiri/beers-srv/...
```

### Docker
If you have Docker already installed then you don't need Go or Protocol Buffers you just need to run below command:

```
$ docker build -t beers-srv:0.1.0 .
Sending build context to Docker daemon 360.4kB
Step 1/11 : FROM golang:1.13-alpine as builder
---> 4acab7f5278b
Step 2/11 : COPY . /go/src/github.com/azbshiri/beers
...

$ docker run -d --rm --name=beers-srv beers-srv:0.1.0
$ docker exec -i beers-srv grpc-health-probe -addr=:8000
status: SERVING

```

## Testing
I haven't used any external libraries for testing/diffing so if you already have the latest version Go installed,
just simply run `go test -v ./...` :
```
ok github.com/azbshiri/beers-srv/pkg/adding (cached)
=== RUN Test_Add
=== RUN Test_Add/adds_beer
=== RUN Test_Add/checks_against_blank_name
=== RUN Test_Add/fails_due_to_service
--- PASS: Test_Add (0.00s)
--- PASS: Test_Add/adds_beer (0.00s)
--- PASS: Test_Add/checks_against_blank_name (0.00s)
--- PASS: Test_Add/fails_due_to_service (0.00s)
PASS
ok
```

## Kubernetes
Deploying gRPC applications to K8s and the best way to configure health checks is using `grpc-health-probe` and GRPC Health Checking Protocol
https://kubernetes.io/blog/2018/10/01/health-checking-grpc-servers-on-kubernetes/

```
$ kubectl apply -f deployment.yml
$ kubectl get svc beers-srv
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
beers-srv NodePort 10.110.71.154 8000:30768/TCP 15m
```

executing β€œgrpc_health_probe” will call our gRPC server over localhost (Cluster IP):

```
$ minikube status
kubectl: Correctly Configured: pointing to minikube-vm at 192.168.122.118

$ grpc-health-probe --addr=192.168.122.118:30768
status: SERVING
```

kaboom!

## Event sourcing
I'd go with Google Pub/Sub API to keep track of events on the cluster in an event store and https://watermill.io/ API to make it easier to deal with events/subscribers.

- BeerRemoved
- BeerAdded
- BeerNameChanged
etc

## Cloud nativeness
Using Kubernetes and Docker you can easily run the application on cloud with ease scaling up/down, integrate different databases and, etc.

## The Twelve-Factor App

> I. Codebase
One codebase tracked in revision control, many deploys
II. Dependencies
Explicitly declare and isolate dependencies

Using Go modules I've isolated dependencies as well as moved out shared code into separate codebase with semantic versioning also since this is a small micro-service I decided to use one branch `master` for deployment and build and adding features using separate branches using PRs.

> III. Config
Store config in the environment

There's not much of a configuration in this micro-service, but I've used env variables to set up the minimum.

> IV. Backing services
Treat backing services as attached resources

There's no backing services (database, etc) but otherwise still using env vars and service discovery tools one can easily decouple attached services and their communication.

> V. Build, release, run
Strictly separate build and run stages

`master` is now reliable stage for building and release also using GitHub actions I run linting, testing and building for each PR.

> VI. Processes
Execute the app as one or more stateless processes

Since I'm using in-memory allocation there's no database involved so this app is not stateless but because
I've already implemented database abstraction layer and repository pattern it's just so easy to configure databases and other backing services using env vars / service discovery to make this app stateless. (e.g. store removing, adding into separate process pg, etc)

> VII. Port binding
Export services via port binding

By setting `PORT` you can easily change port of services (in this case only gRPC server)

> VIII. Concurrency
Scale out via the process model

OCI orchestration is a factor here as you can scale down/up easily on clusters (GCP, AWS) using Docker and K8s.

> IX. Disposability
Maximize robustness with fast startup and graceful shutdown

Using K8s and Docker I've implemented gRPC health protocol so K8s can easily restart/replace service instances when needed.

> X. Dev/prod parity
Keep development, staging, and production as similar as possible

As I have only one `master` branch and, many feature branches this is also true

> XI. Logs
Treat logs as event streams

Unfortunately I haven't had time to make this consistent but, I guess in some cases I'm treating logs like streams?!

> XII. Admin processes
Run admin/management tasks as one-off processes

When run using Docker it's simple as running `docker exec` to do admin tasks also when developing using `beers-cli` and `grpc-health-probe` to make sure everything is working fine.

## Things that are missed

- [ ] Move protocol buffers generation/linting to Github actions
- [ ] Fix GoReleaser on Github actions (checkout action doesn't sync tags)
- [ ] Do dependency injection for logging and make it consistent
- [ ] More human-friendly errors when dealing with
- [ ] Add GCP/AWS integration using Terraform
- [ ] Manage protocol buffers using [buf](https://buf.build/docs/introduction?ref=producthunt) or [gunk](https://github.com/gunk/gunk)
- [ ] Service discovery using etcd, consul or etc
- [x] Implement fault-tolerant mechanisms https://github.com/afex/hystrix-go
- [ ] Move data storage to a stateful service