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.
- This project is really about making a small domain model durable by anchoring every relationship to an ID.
- Its plain-text serialization is a strength because it is inspectable and dependency-free, but that same simplicity makes parsing fragile.
- The OOP hierarchy matters here because it supports persistence and validation, not because it is the main event.
- The college controller behaves like a control plane, which turns input checks and prerequisite rules into part of the architecture.
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
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.
| Approach | Persistence | Debuggability | Safety | Learning value |
|---|---|---|---|---|
| ID links + plain text | Easy to save and reload | Very high | Fragile if delimiters leak into data | High, because every step is visible |
| Pointer graph in memory | Harder to persist cleanly | Medium | Risky if objects move or die | Good for runtime modeling, weaker for storage |
| Library-first JSON or similar | Strong and well-supported | High | Much safer parsing | Lower 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.
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.
| Role | What it does | Why it helps | Where it can hurt |
|---|---|---|---|
| Repository | Stores and retrieves typed records | Keeps persistence concerns separate | Can become thin if too much logic leaks upward |
| College | Coordinates validation and relationships | Encodes business rules in one place | Can grow too central if the project expands |
| Entity hierarchy | Standardizes the record shape | Makes polymorphism predictable | Can 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
| Style | Best at | Weak spot | Best use |
|---|---|---|---|
| This repo’s style | Making the data model explicit | Delimiter sensitivity and manual upkeep | Learning how persistence and identity fit together |
| Library-first storage | Robust parsing and fewer edge cases | Can hide the mechanics | Production-leaning tools and larger systems |
| Pointer-driven modeling | Fast in-memory relationships | Harder reload and safer lifetime management | Transient 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.