Aarya7306/OOP: The C++ College System That Stores Relationships as IDs, Not Pointers

A deceptively small project that uses inheritance, templates, validation, and hand-rolled serialization to keep academic data coherent in plain text.

7 min read • View on GitHub • More from Aarya7306

A desk scene with ledger cards for students, courses, teachers, and departments connected by numbered links instead of wires. It explains that the system keeps relationships stable through IDs, which makes persistence and reconstruction simpler.
The project’s core move is not a fancy class tree. It is the decision to treat identity as a first-class object and keep the links readable.
Key Takeaways

Most student C++ projects show off inheritance first. This one is more interesting than that. It quietly behaves like a tiny persistence layer, because the real design choice is not the class tree. It is the decision to model academic relationships with IDs, then save the whole thing as plain text.

The real trick: everything is linked by ID

The system is easier to reason about when objects do not point at each other directly. They point through stable IDs, and College resolves the relationships when it needs them.

Course c(101, "Databases");
Student s(42, "Amina", "Khan");

college.addCourse(c);
college.addStudent(s);
college.enrollStudentInCourse(42, 101);

// The link is an ID, not a raw pointer.
// That makes the relationship easy to save and reload.

Why that choice makes the whole system easier to persist

The repo’s persistence layer is deliberately low-tech. Entities expose string-based save and load logic, so records can be written to disk, inspected by hand, and reconstructed without an external format library. That is useful in a classroom project because the data model stays visible instead of disappearing behind a serializer.

ApproachPersistenceDebuggabilitySafetyLearning value
ID links + plain textEasy to save and reloadVery highFragile if delimiters leak into dataHigh, because every step is visible
Pointer graph in memoryHarder to persist cleanlyMediumRisky if objects move or dieGood for runtime modeling, weaker for storage
Library-first JSON or similarStrong and well-supportedHighMuch safer parsingLower visibility into the mechanics

The tradeoff is obvious once you look at the format. A custom delimiter scheme is light and transparent, but it is also brittle if real names or notes contain separator characters. That is not a bug in the idea. It is the price of owning the whole pipeline.

A close-up of a single text record split into fields on the left, then reassembled into structured boxes on the right. A cracked delimiter marks the fragile point in the pipeline, showing both the elegance and risk of custom serialization.
This is the cleanest tradeoff in the repository. You get total control over the file format, but you also inherit every edge case in the parser.

The OOP scaffolding is there to support the model

The inheritance structure is solid, but it is in service of the data model. `Entity` defines a shared contract, `Person` carries common fields, and `Student`, `Teacher`, `Course`, and `Department` specialize from there. That hierarchy matters because it gives the repository and the controller a consistent way to treat records without collapsing everything into one blob.

class Entity {
public:
    virtual int getId() const = 0;
    virtual std::string getRole() const = 0;
    virtual void printDetails() const = 0;
    virtual ~Entity() = default;
};

class Person : public Entity {
protected:
    int id;
    std::string name;
};

College is the coordinator, and that matters

If there is a single control plane in the project, it is `College`. It wires repositories together, checks prerequisites, and decides whether input is acceptable before anything is written into the model. That centralization has a cost. It can become a god class. But in a small educational system, it also keeps the rules legible.

RoleWhat it doesWhy it helpsWhere it can hurt
RepositoryStores and retrieves typed recordsKeeps persistence concerns separateCan become thin if too much logic leaks upward
CollegeCoordinates validation and relationshipsEncodes business rules in one placeCan grow too central if the project expands
Entity hierarchyStandardizes the record shapeMakes polymorphism predictableCan feel academic if not tied to real workflows

Input validation is part of the architecture

The repo treats bad input as a structural problem, not just a UI annoyance. Integer parsing is guarded, names are checked for shape, and IDs must stay positive. That is a better lesson than most CLI demos teach. Data integrity starts at the boundary, not after the object is already broken.

int value = college.getIntInput("Enter ID: ");
if (value <= 0) {
    std::cout << "Invalid ID\n";
}

if (!std::isupper(name[0])) {
    std::cout << "Name must start with a capital letter\n";
}

That design choice makes the whole system feel sturdier than a typical school assignment. The code is not just validating for convenience. It is protecting the model from becoming inconsistent.

What this teaches better than a library-first approach

StyleBest atWeak spotBest use
This repo’s styleMaking the data model explicitDelimiter sensitivity and manual upkeepLearning how persistence and identity fit together
Library-first storageRobust parsing and fewer edge casesCan hide the mechanicsProduction-leaning tools and larger systems
Pointer-driven modelingFast in-memory relationshipsHarder reload and safer lifetime managementTransient graphs that never leave RAM

The educational value here is not that the project reinvented storage. It is that it makes the invisible parts visible. You can see where identity comes from, where validation happens, and how records survive a round trip to disk.


The limits are part of the lesson

The same choices that make the repo easy to understand also limit it. A delimiter-based file format is brittle. The architecture is centralized. There is no formal build system. None of that ruins the project. It just marks the line between an educational system and a production one.

That line is useful. It shows exactly how far you can get in C++ when you treat identity, validation, and persistence as first-class design problems.