Backend and scheduler for Risa, the IT asset and maintenance system used across Pelindo III’s Kalimantan region. [Golang, MongoDB].
Risa is what IT staff and vendors across seven Pelindo III branches in Kalimantan used to track equipment, maintenance history, daily checks, and stock. This is its backend: a Golang REST API on Fiber, MongoDB for storage, and a scheduler watching device health in the background. The Flutter app is the other half.
The whole system is a rewrite of ITventory from the year before, whose backend was Flask. MongoDB stayed; everything above it was rebuilt. Knowing the domain already is what made it worth moving to Golang. The requirements were not in question, so the rewrite could be about the architecture.
gen_unit, a projection collection mirroring the fields every device has in common, so that a single search field could find anything in the estate regardless of its category.The request was easy to state and awkward to implement. Staff wanted to type a device name or an IP address into one box and find it, without first deciding whether they were looking for a CCTV camera, a computer, or an application. Each category has its own shape and its own collection, so no single query could reach across all of them.
gen_unit answers that by keeping one slim entry per device (name, IP, category, branch), written alongside every create, edit, and delete, and refreshed whenever a device’s history changes. Nobody writes to it directly; it is maintained behind the scenes. The vocabulary I would use now is read model, and I would build it again. What I would not repeat is the search itself: it scans, so it slows as the estate grows. Moving it to Elasticsearch was already the plan on record.
history as a status lifecycle (info, in progress, awaiting approval, pending, complete) with nested entries, so one incident can be replayed and reported over a date range.Every incomplete history entry increments a cases counter on that device’s gen_unit entry, and completing it decrements the counter. Same trade as above: the write path does more work so a list of devices can show which ones need attention without a second query per row.
An item flagged as a problem does not vanish when the check is submitted. It reappears on the next one, and keeps reappearing until somebody resolves it. checklist_cctv is deliberately separate: vendor maintenance runs on its own cadence and is done by different hands.
qty field on write.MongoDB makes this shape cheap and it suited the volume. A stock item and its movements are almost always read together and never read by anyone else, so there was nothing to gain by splitting them.
The ping results come from a separate small service called pingers. Keeping that out of the API meant the long-running network work never sat in the request path.
Handler → Service → Dao, with a separate client package for outside services.
This is the ancestor of the golden path repository I set up years later at Hukumonline, with the same instinct to keep business logic away from the database driver, arrived at before I had the vocabulary for it.
One dependency has aged badly: JWT signing went through dgrijalva/jwt-go, which was later abandoned by its author and superseded by the maintained golang-jwt/jwt fork. Anything still building on it today should move.