/ShortCx

Polygot Microservice based URL shortener using gRPC

Primary LanguageGoMIT LicenseMIT

ShortCx

Polygot Microservice-based URL shortener using gRPC.

About

ShortCx is a personal project and demonstration of the usage of gRPC in a polygot microservice environment. All interservice communication (except database access) is done via gRPC, including communication with the client (via gRPC-web). Shared .proto files define all messages and service definitions a service needs to communicate with the system.

Polygot Services

As part of this project, I wanted to use as many different languages as I could to implement the services. This was to demonstrate the flexibility and interoperability that gRPC and contract-based API design offers. Since the server and client code is autogenerated for each service, we can focus on implementing our business logic in each service rather than worrying about inconsistencies between them. Realistically, I wouldn't recommend doing this for a serious project, each gRPC implementation has its idiosyncrasies that will throw you off, so it may be best to focus on only a few implementations. I think this could be very useful for a larger service that needs to incorporate services that must be written in another language, for example, if you had a larger set of services mostly written in Go, you could still add in a service written in Python to take advantage of machine-learning libraries, and it would integrate into the bigger picture easily because of gRPC.

How It Works

The client is built with Vanilla javascript and webpack, we use gRPC-Web to generate client stubs to communicate with our API. gRPC-web and the Envoy Proxy layer help us take advantage of gRPC in the browser, which was previously impossible. gRPC-web was really easy to set up, and the Envoy Proxy worked perfectly right out of the box ( or should I say, container ;) ).

All communication with the backend services is mediated by APIService (Go), this services is responsible for routing requests to downstream services, kind of like a reverse-proxy. Some requests are send "as is" from the client, such as GetShortcutRequests which require no authentication. The individual services beneath APIService are then responsible for validating and processing these requests, the response or error is then sent back to the client through APIService.

Operations that require the user to be logged in (Creating shortcuts, deleting shortcuts, etc.) are first authenticated by AuthService before being forwarded to the relevant service. For example, see this condensed snippet from APIService:

func (s *Server) CreateShortcut(request *api.CreateShortcutRequest) (*api.CreateShortcutResponse) {
	sessionState := s.getSession(request.AuthToken)
	...
	internalCreate := &shortcut.InternalCreateShortcutRequest{
		AuthUserId: sessionState.UserId,
		TargetUrl:  request.TargetUrl,
	}
	resp := s.ShortcutClient.CreateShortcut(context.Background(), internalCreate)
	...
	return resp
}

As you can see, we first make a call to AuthService to validate the auth token from the client, then we include this information to our request to ShortcutService which can make its own decision about whether the validated user is allowed to perform the operation. I have omitted some details such as error handling for readability, but you can see the full file here.

Services

Client: NGINX + Webpack + gRPC-web
Proxy: Envoy Proxy
API Gateway Service: Go
Authorization Service: Python
Shortcut Management Service: Go
User Management Service: C#
Database: MySQL
Session Management: Redis

Architecture

Services

Architecture

Database

ERD

License

Go Crazy.
This Project is licensed under the MIT license. see LICENSE.txt for more information.

Acknowledgements

  • Languages
    • Go
    • Python
    • C#
    • SQL
    • JavaScript
  • Tools
    • Docker
    • GNU Make
    • Bcrypt
  • Programs
    • Redis
    • MySQL
    • Envoy
    • NGINX