Skip to content

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

Modules

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.

Database design

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.

Authentication and authorization

  • 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 of Bearer <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.

REST application layers

  • 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.

Example request-response flow

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.

  1. The endpoint lives as a JAX-RS annotated method getContest() in ContestService interface.
  2. The implementation class, ContestResource, is registered as a Jersey resource via Dropwizard.
  3. The resource is then invoked, with the authorization header passed as an argument.
  4. Using ActorChecker, the authorization containing bearer token is converted into user JID representing the currently logged-in user (actor).
  5. The resource method asks ContestRoleChecker, whether the actor is allowed to view the contest.
  6. The resource method asks the store ContestStore to get the Contest by its JID.
  7. The store asks the DAO ContestDao to get the database row in the contest table by its JID.
  8. The DAO implementation, ContestHibernateDao, executes the appropriate SQL query to get the row.
  9. The store gets the row, and converts the row (ContestModel) into a business object (Contest).
  10. The store returns the Contest to the resource method.
  11. The resource method returns the Contest.
  12. The return value is then converted into an HTTP response, with a JSON body representing the contest object.

Further readings

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