-
Notifications
You must be signed in to change notification settings - Fork 24
Dev's Guide: Backend basic concepts
Ashar Fuadi edited this page May 17, 2023
·
17 revisions
This section explains the basic knowledge required for understanding Judgels codebase on the backend side.
- Tech stack
- Database design
- Authentication and authorization
- REST application layers
- Example request-response flow
- Further readings
- Java 8
- Gradle as build tool
- Dropwizard as the Java REST / server backend framework
- MySQL for database
- JPA implemented with Hibernate for ORM
- Caffeine for cache
- Dagger for dependency injection
- Immutables for objects immutability
- Guava for collections
In the application code, we logically divide the resources into several modules / packages:
- Jophiel: users, roles, sessions
- Sandalphon: problems, lessons
- Uriel: contests
- Jerahmeel: courses, problemsets
Those module names are also reflected in the database table names as prefixes. For example, users are stored in the jophiel_user
table.
Judgels adapts the database design explained here: Phabricator Database Schema. In particular:
- Here, by "objects" we mean objects as in REST resources. For example: users, problems, contests.
- Each object in Judgels has a database-generated auto-increment ID. We (generally) don't use this ID for referencing objects.
- Instead, each object in Judgels has a unique identifier called JID (Judgels ID) in the form of JIDXXXXYYYYYYYYYYYYYYYYYYYY, where X is object type code and Y is a shortened UUID. For example: JIDUSER7uMucIkm1exJTu7sJvxR.
- JIDs as unique identifiers enables easy backup or migration between different Judgels instances.
- In addition to JID, each object may have user-friendly ID. For example: usernames, problem slugs, contest slugs.
- Complex properties are stored either on disk database as JSON strings. For example: grading result details.
- Additionally, each object may have the following columns:
- createdBy, createdAt, createdIp: user, time, and IP when this object is first created.
- updatedBy, updatedAt, updatedIp: user, time, and IP when this object is last updated.
- Jophiel stores the session of a logged-in user as a token in the form of random string.
- An HTTP request to a server endpoint may be accompanied by an
Authorization
header, in the form ofBearer <token>
, meaning that the request was initiated by the user represented by the bearer token. - Jophiel converts the bearer token in the authorization header into a user JID.
- Each module (Jophiel, Uriel, Sandalphon, Jerahmeel) has its own authorization scheme whether or not to allow certain users to hit certain endpoints, based on roles.
-
Service: declares the REST API endpoints. Example:
ContestService
. -
Resource: implements the REST API endpoints. Example:
ContestResource
. -
Store: manages CRUD operations of business objects. Example:
ContestStore
. -
Dao: declares the CRUD operations in database. Example:
ContestDao
. -
HibernateDao: implements the CRUD operations by executing SQL queries. Example:
ContestHibernateDao
. -
Model: represents a row in a database. Example:
ContestModel
.
Say we hit the server with this HTTP request:
curl -XGET -H "Authorization: Bearer token123" http://localhost:9101/api/v2/contests/JIDCONTabcdefghijklmnopqrst
The following is a simplified sequence of events that will happen.
- The endpoint lives as a JAX-RS annotated method
getContest()
inContestService
interface. - The implementation class,
ContestResource
, is registered as a Jersey resource via Dropwizard. - The resource is then invoked, with the authorization header passed as an argument.
- Using
ActorChecker
, the authorization containing bearer token is converted into user JID representing the currently logged-in user (actor). - The resource method asks
ContestRoleChecker
, whether the actor is allowed to view the contest. - The resource method asks the store
ContestStore
to get theContest
by its JID. - The store asks the DAO
ContestDao
to get the database row in the contest table by its JID. - The DAO implementation,
ContestHibernateDao
, executes the appropriate SQL query to get the row. - The store gets the row, and converts the row (
ContestModel
) into a business object (Contest
). - The store returns the
Contest
to the resource method. - The resource method returns the
Contest
. - The return value is then converted into an HTTP response, with a JSON body representing the contest object.
It is important to also read the documentation of these libraries in order to help understand Judgels backend codebase:
-
Dropwizard
- Jersey, JAX-RS
- configs, bundles
-
Dagger:
- components, modules, providers, singletons
- how dependency injections work
-
Immutables
- immutability, builders, precondition checks