Build a Basic Real-Time Competition App With Go

An end-to-end implementation

Dilara Görüm
Better Programming

--

Photo by Dimon Blr on Unsplash

Most of us love knowledge contests, right? There are many applications to slake our thirst by making us answer questions coming from different professions.

In this article, I will explain how I implemented a real-time competition app with Golang.

Application Flow

Figure: Architecture Flow

There are some business rules needed to follow.

  • When the number of connected users reaches two, the competition will start automatically within 3 seconds.
  • There are three states in our competition. These are NOT_STARTED, STARTED, FINISHED.
  • Questions have four options, and Users must answer a question within 10 seconds.
  • After the competition ends, the leaderboard is shown to users to see the results of the competition.

My Architecture Decisions

In this section, I will try to explain why I made some decisions and try to develop some points of view on our project before we begin. This section will be like a movie spoiler. 🎥

  • Websocket is the essential protocol for implementing real-time applications. It provides bidirectional communication between client and server. There are many technical articles to introduce its concepts, so that I won't go into details. I used it to send questions and get connected users' answers.
  • I used a unique id (like session-id) for each connected user. In doing so, I can easily differentiate users. In our case, we store our users with their session IDs, and our server manages read and write operations using them.
  • To support concurrent read and write operations, I used sync.Map . I used sessionID as the key and Client struct as the value shown below. The client structs consist of two fields a client WebSocket connection to write and read and totalScore to calculate the leaderboard.
Figure: Clients Map Model
  • Broadcast is a special term to represent a method of transferring a message to all recipients simultaneously. Unfortunately, there is no broadcast method in gorilla/websocket; therefore, we will use our custom broadcast method to send messages to all users.
Figure: Broadcast method
  • To send questions to our users, we need to define a model. In our application, I don't want to use the Question struct because it has the correct_answer field. I want to hide this information from the connected users, so I created another model called QuestionDTO, as shown below.
Figure: Question DTO

Competition Flow

I used to manage competition flow with RunCompetition() method as shown below.

I invoked this function with the main flow of the application by creating a goroutine.

Figure: Competition Flow

There are three CompetitionState in RunCompetition() method.

  • CompetitionNotStartedState: To start the competition, there must be two users. When the numberOfClients is equal to 2, the state is changed as CompetitionStartedState.
  • CompetitionStartedState: In this state, we send questions to all connected users every 10 seconds.

When sending questions, we convert our Question struct to questionDTO to hide correct_answer from the connected users to prevent cheating.

After 10 seconds, we change IsTimeout as true because the time given to the question is up.

  • CompetitionFinish: After all the questions are sent, CompetitionState is changed to CompetitionFinish. We must create and send LeaderBoard to all connected users when the competition is over.

Handle Client Answer Flow

We need to get answers from users to check and calculate their scores.

Users can get a score (+10) when they correctly answer the question within the specified time interval.

There are some important points here:

  • IsTimeout: specified time interval
  • Comparison question.ID and ClientMsg.QuestionId : determine whether it is asked question
  • Comparison ClientMsg.Answer and question.CorrectAnswer : determine whether the user gives the correct answer.

After these conditions are met, the score of the user is stored on the map by using sessionID. We invoke this function in our ws()method.

To keep our application simple, the competition will start with two users. If the user’s number is greater than two, our app will send http.StatusBadRequest to user.

Source Code

https://github.com/dilaragorum/real-time-competation-go

Thank you for reading. If you want to read my previous articles, you can reach their links below.

--

--