Skip to content
Revoech
··/··
Work / 06

Designing a university community platform

UniPal is a full-stack platform built as a final year project. The engineering challenge was designing a MongoDB schema for three distinct features that share users but have different data shapes, and making them feel like one coherent product.

Date
2024–2025
Role
Final year project · team of four
Stack
React · Node.js · Express.js · MongoDB
Scale
University project · personal development
Status
Completed 2025
01Problem

UniPal is a community platform for university students. Three features: course and professor reviews, a book exchange where students can donate or request textbooks, and a shared repository for study materials. Each feature is distinct. All three share the same users and the same codebase.

The early question was how to model this in MongoDB. A relational database makes the structure obvious: normalized tables, foreign keys, joins. MongoDB does not enforce any of that, which is either an advantage or a trap depending on the decisions you make in the first week.

The book exchange was the most interesting design problem. A book does not exist as a single entity. It exists as something a student is offering, or something a different student is requesting, and the platform needs to match one to the other. Getting the data model wrong here means the matching logic becomes a workaround for a structural mistake.

02Constraints
  1. MongoDB only. The project scope was fixed before data modeling began. No relational fallback.

  2. The three features had to share the same user collection. A student posting a book donation and leaving a course review are the same person. The data model had to reflect that without duplicating user records.

  3. The course and professor hierarchy is inherently relational: universities have departments, departments have courses, courses have professors. This does not map naturally to documents.

  4. A four-person team. The schema decisions had to be legible enough for everyone to build against without constant coordination.

03Investigation

The book exchange was the clearest design problem. The naive model embeds book details directly in a listing document: a donor listing and a requester listing each contain a copy of the book description. Matching then requires comparing text fields across two collections, which is unreliable and hard to query.

A better model separates the book itself from the listing. A Book document describes the title, author, and subject. A Listing document references the Book by ID, records whether it is an offer or a request, and points to the user. Matching becomes a join on Book ID between offers and requests -- one query, no text comparison.

For the course hierarchy, embedding was wrong. A course document that embeds all its reviews grows unboundedly. Reviews instead reference the course by ID and are stored in their own collection. The hierarchy (department to course to section) uses references rather than embedding at every level because any of those entities can be queried independently.

MongoDB's flexibility is a trap if you make embedding versus referencing decisions late. The query patterns have to come before the schema decisions, not after.

04Decision

The data model settled on three main domains: Users, Academic (universities, departments, courses, reviews), and Exchange (books, listings). Each domain uses references rather than embedding for anything that needs to be queried or updated independently.

Book exchange matching works as follows: a student creates a Request listing referencing a Book document. The API queries Listings where type is Offer and book ID matches. Results include the donor's contact information. No text matching. No duplication.

Study materials are the simplest domain: a Material document stores the file metadata and a reference to the course it belongs to. File storage uses the server filesystem for the project scope. A production version would swap that for object storage.

On the frontend, the three features are separate pages but share a navigation and user context. React components are organized by feature, with shared UI primitives extracted once they appeared in more than two places.

05Outcomefinal year project
Features shipped
course reviews, book exchange, study materials
Book matching
ID-based reference, no text comparison
Data model
reference-based across all three domains
Team size
four, with shared schema from week one
06ReflectionWhat I'd do differently

The embedding versus referencing decisions were made early and held up well. The one I got wrong was study materials: storing files on the server filesystem made sense for the project scope but made deployment complicated. Object storage (S3 or similar) should be the default for any user-uploaded content from the start.

MongoDB's schema-less nature is not an excuse to skip schema design. Every collection should have a documented shape before anyone writes a query against it. On this project we wrote that down late, which meant two team members built against slightly different assumptions about the review document structure. Reconciling that cost a day.

The book exchange matching logic is correct but it was the last thing we built. The data model supports it cleanly, but if the model had been designed without thinking about the matching query first, it would have been a different story. Query-driven schema design is the lesson.