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