/broadcaster

Broadcasting using Redis as message queue and websockets to broadcast the messages

Primary LanguageGoGNU General Public License v3.0GPL-3.0

Tech Challenge - Broadcaster

This code challenge consist on to implement two services following these instructions:

## Create two microservices for real time messages using websockets, and at least one demo subscriber.

### Functionality
1. The first service should listen for incoming messages through the websocket protocol and when a new one arrives, the message should be published into message queue
1. The second service should listen for incoming messages through the message queue and when a new message arrives, the message should be published to all the subscribers through the websocket protocol

### Other Requirements
* At least one of the services should have tests
* Make sure your code is well structured and maintainable (including tests)
* You can use frameworks and technologies by your choice, but the language for the microservices should be Javascript (node) or Go (or both).
* The source code should be hosted online using github (or similar service)

Note, this is your chance to impress us, take this opportunity to show us what you can do! You can include authentication, use any CI, container based deployment or whatever you think will grab our attention.

Big Picture

I've implemented two services:

  • Service 1/Listener: This service is in charge of receiving messages to be broadcasted to subscribers, and publish them to a message queue.

  • Service 2/Publisher: This service is in charge of accepting subscribers, and reading messages from the message queue and broadcast them to the subscribers.

big picture

Architecture

To implement the two services, I've applied these architectural patterns:

  • Basic Auth (service 1)
  • Hexagonal architecture (services 1 and 2)
  • CQRS (service 2)
  • Event-Driven (service 2)

How to start it locally with docker-compose

There is a Make file in the root for that. Only do:

  make docker-run
  make docker-logs

These commands will start three container:

  • A Redis container wich will act as message queue

  • A listener service (first service) in charge of receive messages by a websocket connection and push them to the message queue (Redis).

  • A publisher service (second service) in charge of read messages from the messages queue and broadcast them to the subscribers.

How to deploy it to local k8s cluster with Minikube

These are the steps to deploy the broadcaster to a local k8s:

  1. Install and configure Minikube

  2. Install kubctl tool

  3. Start Minikube

  minikube start
  1. Deploy k8s objects
  devops/k8s/deploy.sh
  1. Start port forwards to listener and publisher services
  ﬌ kubectl port-forward service/listener 8080:80
  Forwarding from 127.0.0.1:8080 -> 80
  Forwarding from [::1]:8080 -> 80
  Handling connection for 8080
  ﬌ kubectl port-forward service/publisher 8081:81
  Forwarding from 127.0.0.1:8081 -> 81
  Forwarding from [::1]:8081 -> 81
  Handling connection for 8081

How to try it

  1. First open a websocket connection to the first service. To do that I use the websocat tool This service uses Basic-Authorization (by default user/pwd)j
    websocat -H="Authorization: Basic dXNlcjpwd2Q="  ws://localhost:8080/websocket
  1. Open so many subscriber as you want. To do that, open a websocket connection to the second service (publisher):
    websocat  ws://localhost:8081/websocket
  1. From this moment you can start passing messages to the listener and see how all the subscribers receive these messages.

screenshot

How to test it

The two services includes unit tests:

  • Service 1:
    cd listener
    make test-unit
  • Service 2:
    cd publisher
    make test-unit

CI

I've added an CI action to pass the unit test for each pushing to repo.

Repo layout

  • assets - images of this document

  • listener - service 1, in charge of receiving message by websocket connection and pushing them to a message queue

  • publisher - service 2, in charge of read messages from messages queue and broadcast them to the subscribers

  • For each service:

    • cmd - where the main.go is
    • internal - used to reduce the public API surface
    • internal/app - CQRS layer, application services
    • internal/domain - where the domain entities and business rules lives
    • internal/helpers - misc helpers used to improved the code reading
    • internal/infra - infrastructure layer

There are also other files used for development purposes:

  • docker-compose.yml
  • Makefile

Tooling and libs used

To implement the solution I've used:

  • Go libs:

    • github.com/getlantern/httptest v0.0.0-20161025015934-4b40f4c7e590
    • github.com/go-chi/chi v1.5.4
    • github.com/go-redis/redis/v7 v7.4.1
    • github.com/gobwas/ws v1.1.0
    • github.com/rs/cors v1.8.3
    • github.com/stretchr/testify v1.8.1
    • github.com/google/uuid v1.3.0
    • github.com/theskyinflames/cqrs-eda v1.2.5
  • Tooling:

    • MacOS Ventura 13.1
    • Go 1.19.4
    • Docker 20.10.22
    • GNU Make 4.3
    • websocat 1.11.0

Dockerfile building strategy

I've opted for optimizing the Docker image size by multi-stage building. So only the binary is added in the resulting image.