Back to projects

Case study · 2026

Lernumi

An academic-integrity learning platform. My role was the architecture: the data model, the flow, and the contract that lets outside tools plug into the system.

Live demo🏆 People's Choice — Sustainability Showcase 2026
Role
System Architect
Team
2 people
Stack
Next.js · AWS · Prisma
Theme
UN SDG 4 & 8

The problem

How do you tell whether a student actually did their work? In a world of copy-paste and generated answers, a finished document tells you almost nothing. The interesting signal isn't the final state — it's how it got there. Did the code grow over ten minutes, or appear in a single second?

Why event sourcing, not snapshots

The obvious approach is to save the document every so often. But snapshots throw away the exact thing we care about: a series of snapshots can't tell whether a paragraph was typed or pasted. So the platform doesn't store states — it stores an append-only log of edit operations. From that log, any moment of the work can be replayed step by step.

Snapshotsgaps — no idea what happened betweenOperation logevery edit recorded → replay the whole session
Snapshots capture a few frozen states; the op-log captures every step, so the whole session can be replayed.

A contract between the platform and its tools

A code editor and a diagram tool record very different things, so I didn't want the platform to understand any of them. Instead I designed a tool-platform contract: the platform owns identity, storage and permissions and stores opaque event batches; each tool is an independent browser app that plugs in through one published API. Adding a new tool changes nothing in the platform — the same idea IDEs use for extensions.

Code editorDiagram toolFuture toolpublished HTTP API (one contract)Platformidentity · storage · permissions · opaque event batches
The platform stays generic; tools give the data meaning.

Who can do what

I designed the database schema and the access model myself. It started as a single table of roles and permissions; once I noticed roles were reusable I split them apart — which, I later learned, is exactly role-based access control. Because some permissions only make sense for specific resources, I added scoping: a role is either institution-wide or scoped to a particular resource, so it's always clear who is allowed to do what.

What I took from it

The parts I'm proudest of aren't lines of code — they're decisions: log operations instead of states, keep the platform ignorant of what tools mean, scope permissions to resources. Good architecture is mostly choosing the right thing to make simple.

Back to all projects