Forum
GitHub: https://github.com/soondubu137/go-forum
This educational project demonstrates (monolith) web development with Go. This project implements the backend of an online forum.
NOTE
A frontend is not implemented with this project.
Introduction
This is a typical forum project. Users can register and publish posts. Each post belongs to a specific community, users can query for a list of posts (paginated) under a specific community.
There is also a voting mechanism. Users can vote up or down on a specific post, and this will affect the "score" of a post. Time also affect the "score" of a post (newer posts have higher scores). When users query for a list of paginated posts, they can specify the sorting order - by newest or by highest score. Scores are stored in Redis for improved performance.
Project Architecture
This project strictly follows the CSR (controller-service-repository) layered architecture.
TIP
Sometimes "service" is used interchangeably with "logic", and "repository" with "DAO".
Controller: entrance to the underlying service, responsible for handling routes, parameter validation, and redirection.
Service: responsible for handling specific business logic.
Repository: responsible for interacting with the underlying databases.
Technologies & Tools
In this project, we use the popular web-development framework Gin. The choice of framework is certainly not definitive,
Be an engineer, not a frameworker.
Database: MySQL. In this project, we simply use sqlx to write pre-compiled SQL statements to do CRUD, although we can also use GORM to simplify CRUD operations.
Votes caching: Redis.
Distributed ID generation: snowflake is a popular distributed ID generation algorithm. It ensures global uniqueness and monotonicity.
Authentication: JWT (JWT generation: jwt-go).
Logging: Zap.
Config management: Viper.
API testing: Bruno.
Features
Users
JWT
User authentication is done with single-token JWT (i.e. only authentication token, no refresh token). JWT authentication is implemented as a middleware in Gin, intercepting all auth-required requests. If JWT authentication is successful, the user ID will be injected into the Gin context. See
middleware/auth.go
for details.Salted password hashing
Password hashing alone is not sufficient due to its susceptibility to dictionary attack. Salted password hashing increases the brute-force attack's time complexity from
to , where is the dictionary size and is the number of the compromised password hashes.
Posts
CRUD
Create a post. Query for a list of posts (paginated) under a specific community. When querying for posts, users can specify the order in which the list of posts are returned. Imagine there is a button on the frontend, you can toggle it between "by newest" and "by hottest". I implemented a simple scoring system for the posts, both voting and time can affect the score of a post, which then affects how "hot" a post is.
Communities
CRUD
Create a community. Query for a list of communities (paginated).
Voting
- There are two
ZSET
s in Redis:post::time
where KV-pairs contain post ID and creation time, andpost::score
where KV-pairs contain post ID and score. In addition, for each post, there is aZSET
keeping track of the voting status. When users create a post or vote for a post, theZSET
s are updated accordingly to ensure the scores are always up-to-date. This functionality is implemented with Redis instead of MySQL to improve performance.
- There are two
Engineering
- Graceful shutdown.
- Centralized response management.
- Centralized error management.
Learning Outcomes
Through this project, I
- gained practical experience with Go,
- gained familiarity with MySQL and Redis operations,
- gained familiarity with Gin and other popular web-dev components (zap, viper, etc.),
- gained familiarity with CRS architecture,
- accumulated project design and development experience.
Extensions
Here I list some potential extensions to this project.
Extension | Explanation |
---|---|
Refresh Token | Add a refresh token to JWT authentication. |
Post Deletion | Allows users to delete a post. However, this is just more CRUD. If you are already familiar with basic CRUD with sqlx, this task is unenlightening. |
Comment | Allows users to post comments on others' posts. This may be slightly more interesting than post deletion, but essentially it is still CRUD. |
References
- This project is based on Wenzhou Li's Go web development course.
- https://textbook.cs161.org/crypto/passwords.html#148-password-hashing-done-right