A multi-tenant system is one where several institutions share a single software core while each keeps its own data and brand. The real decision is where record ownership is written; carried on the record, the boundary gives one answer to every screen that reads it. An institution joining the Vipkurum core finds the features ready and the data empty; timetables, student records and exam content are entered by the institution itself.
Why seven institutions did not want seven installations
Buy an off-the-shelf panel and the institution ends up wearing the software’s brand. The address belongs to the vendor, the name at the top of the screen is the vendor’s name, and the institution’s own name gets a corner. On the Vipkurum core that seven institutions share under their own domains and brands, the requirement ran the other way: each institution would sign in at its own address, appear under its own name, and the core would still be one. What shaped the system was not a technical preference. It was that brand requirement.
The seven are not variations on one type either. A special education center, an exam-prep school, a distance learning center and a school all sit on the same software. Each has its own students, its own timetable and its own payment records. What they hold in common is the software, not the data. That distinction is worth stating up front, because “they use the same system” is usually heard as “they write into the same pool”.
The alternative was obvious enough: seven institutions, seven installations. Each gets a copy of its own, lives on its own, and nobody can reach anyone else’s data.
The bill for that route arrives on the feature side. Timetables, exams, students, guardians, payments and reporting sit in one platform; seven installations would mean seven copies of every one of those features. Work done once on the shared software would instead be spread across seven places, verified seven times, and would slowly drift into seven versions that no longer match.
It is also worth noticing that a branding requirement produced a structural decision. An institution treats the question of whose name its students and guardians see as a marketing matter. In this build it decided how the data and the addresses would be laid out. The wish to stay visible under its own name arrived at the table not as an interface preference but as a condition the software had to carry.
Where the tenant boundary actually lives
In Vipkurum, a student, parent or payment record never crosses the boundary of its institution. One institution’s record does not appear inside the neighbouring tenant. The weight of that sentence comes from where the boundary is kept: isolation is not a visibility filter applied in the interface, it is a boundary carried by the record itself.
The practical difference between those two arrangements is easy to state. If the boundary were a filter, every screen, every list and every report would have to remember it separately. A single surface that forgot would puncture the boundary at that surface. When the boundary sits on the record, the question is answered once: which institution does this record belong to? Because the answer travels with the record, every place that reads it arrives at the same answer.
The model is shared and the records are not. Timetables, classes, branches, exams and payment records live in one data model. The seven institutions use the same definitions: a branch means the same thing everywhere, an exam has the same shape everywhere. What is common is that definition layer. The records themselves stay tied to one institution.
The second face of the boundary shows up in reporting. Invoices, payments and analytics reports are produced from the administrator’s own tenant data. That is the same boundary expressed in the reporting layer. Reporting is where boundaries loosen most easily in any system, because a report is produced by aggregating records, and aggregation is the operation most likely to step over a line. If another institution’s payment slipped into a month-end table, nothing would flag it. The administrator would take the figure as given.
Two faces, one question, two addresses. The first is the moment a record is read, the second is the moment records are summed. A system that builds only one of them can behave correctly all month and still produce a wrong number at the end of it.
How the incoming domain decides which institution is served
Each of the seven institutions signs in from its own address: egitakademi.com, ozdilakademi.com, mergenkampus.com, edebiyatuzem.com, dekaakademi.com, turkcefm.com and statuakademi.com.tr. Tenant isolation is drawn at the domain level. Tenant identity follows the address a user arrives from rather than a choice the user makes from a list.
That looks like a small decision and it settles a large one. A single shared address with an institution picker at sign-in was possible. In that arrangement the user meets the platform first and locates their own institution second. The brand requirement wanted precisely the opposite. An institution’s users see their own institution’s address and brand rather than the Vipkurum name; the fact that the core is single never surfaces for them.
There is a second consequence: identity stops depending on user behaviour. A picker admits a wrong pick. A user selects a neighbouring institution, the sign-in attempt lands at the wrong door, and “which institution are you signing in from” becomes a routine support question. On the address side that step does not exist. The user is already following their own institution’s link.
Keeping the domain with the institution also means the institution keeps its own communications. Its address is what goes on its brochures, its signage and its social accounts. Joining the platform does not mean handing that address over to a platform address. An address that has been circulating for years keeps working.
It is worth noting that the address does two jobs at once here. It shows the user the institution’s brand, and it tells the system which tenant it is serving. In most builds the branding wish and the isolation requirement are treated as two problems and solved in two places. Here both answers come out of one fact: the address the request arrived from. That equivalence looks obvious in hindsight and does not appear on its own.
What web and mobile do when the brand changes per tenant
The admin panels were built with React and the mobile side with React Native (Expo). Both surfaces connect to the same Laravel application. Surfaces multiply and the operation does not: a student’s timetable is read from one record whether it is opened in a browser or on a phone.
The record layer is single too. MySQL and MongoDB hold records, Redis carries the cache, and none of that changes with the surface. The conclusion is worth stating plainly: brand differentiation never reaches the data layer. The brand belongs to the tenant, while lessons, exams, payments and reporting run once, in the shared software.
What belongs to an institution is more than visibility. The content management layer lets an institution build its own lessons, tests, questions and exams. It also defines the assessment type itself, which is why European and Turkish criteria can sit side by side in one infrastructure. The question we ask when building the publishing layer where content and brand switch per tenant is always the same one: who makes this definition, us or the institution? When the answer is the institution, the definition is written as a record rather than into code.
That answer also decides how long the shared software lasts. Had the assessment type been compiled in, every new criterion would have become a development request, and the requests of seven institutions would have queued behind each other. With the definition in the institution’s hands, nobody waits for anybody.
More surfaces do not mean more boundaries either. Because the mobile app connects to the same Laravel application, the record it reads already carries the tenant boundary on it; the surface does not need an answer of its own. In a design where the boundary is redefined at each surface, every new screen would restate the same rule. Every restatement is a chance to drift from the original, and drift only becomes visible when somebody notices it.
Four roles, one timetable record
The second boundary runs inside an institution. A teacher, a student, a parent and an administrator all look at the same timetable record, and each reaches it within their own permission scope. The record is not duplicated per role.
It helps to spell out what duplication would cost. When a lesson moves an hour, the change would have to reach four copies. The moment it reaches only three, the copies part ways: a parent sees one hour, a teacher sees another, and which is right can only be settled by talking. When the difference between roles sits as permission on one record, that divergence has nowhere to start.
The same boundary applies to messaging. Announcements, messages and notifications move inside an institution. A notification sent to one institution’s parent does not land in a neighbouring institution’s feed, and inside the institution it carries only as much as the role is meant to see.
In daily use the role boundary shows up as different slices of one record. All four roles stand on the same timetable record and each sees the slice their permission reaches. What decides the slice is not a separate record opened per role; it is how far each of them can reach into the shared one. So when somebody’s permission changes, what they see changes and the record stays where it was.
Naming the two boundaries separately is useful. The one described earlier runs between institutions; this one runs inside an institution. Separating tenants is not enough on its own, because the inside of a tenant has to be separated too. Miss the first and the system mixes institutions together. Miss the second and everyone inside an institution can see everything, which is the quieter failure, because no screen ever looks broken.
Why adding an institution is a record rather than an installation
Adding an institution to Vipkurum is a tenant definition rather than a new installation. The institution is defined on top of the existing core and starts signing in from its own domain. The same features become usable along with the definition, because the shared software already carries them.
The point is not the size of the work but its kind. An installation is a project: it has a start, an end and a calendar of its own. A definition is a record operation. Once joining the platform falls into the second category, the decision to join stops depending on a software project’s schedule.
Seen from the joining institution, the question is what it gives up and what it gets. In the off-the-shelf panel world, what it gives up is visibility: it starts appearing under the software’s brand and leaves its own address behind. That price is not paid here. Because the institution appears under its own domain and brand, joining the platform does not mean handing the brand to the platform.
What it gets is the operating behaviour the shared software carries. Its timetables, exams, student and guardian records and payments stay inside its own boundary, and it does not have to commission the features that hold them from scratch. The features it uses on day one are the ones the other institutions on the platform have been using for years.
It is fair to say what the definition does not cover. A tenant definition opens features; it does not fill them with data. The institution still has to write its own timetable, enter its own students and guardians, and build its own exam content. The work in front of a joining institution is therefore not an installation project but the work of establishing its own data. Those are two different things: the first sits with the software team, the second sits on the institution’s own desk.
Separate installs or one core: where the difference accumulates
The difference does not show up in a single moment. It accumulates. The timetables, exams, students, guardians, payments and reporting of seven institutions sit in one platform, and work done there lives in one place instead of spreading across seven installations.
On the data side the difference is this: on one core, an institution’s record does not appear inside another tenant, and that separation comes from the tenant boundary on the record rather than from a physical copy. Because the separation rests on a boundary and not on a copy, the three changes in the table below did not open a new installation.
| Change | How the single core absorbed it | Did a new install or purchase open |
|---|---|---|
| A new institution joining the platform | A new tenant definition on the existing core; the institution starts signing in from its own domain and appearing under its own brand | No — no new installation; joining happens at the level of a record |
| Adding a cropping and matching tool to question preparation | A Python image processing service was connected through Node.js to the content management layer of the core | No — the tool was not set up as a separate product; it was built under the same roof |
| A change in the layout of an optical answer sheet | The adaptation happens in software, on the Python side; the result is written to the exam record in the core | No — no new device and no device-specific form purchase enters the picture |
What the three rows say is that three very different kinds of change were absorbed in the same place: one an institution joining, one a new tool, one a change in the layout of a piece of paper.
A single core is not the right answer everywhere. When tenants operate in ways the shared software cannot hold, the core strains, and every exception opened for one tenant slowly splits one codebase into pieces that no longer resemble each other. At Vipkurum the overlap is real, because all seven institutions work in education: a timetable, an exam and a payment mean the same thing in each of them. When we work on consolidating multi-branch and multi-institution operations onto one core, the first thing we look at is whether the way those organizations operate genuinely overlaps, not which technology carries it.
Why a tool added to the core needs no separate rollout
The platform carries more than institutions. Tools added to the core later are distributed the same way. Two were built under the Vipkurum umbrella: question cropping and optical mark reading. Neither was set up and shipped as a separate product.
In the question cropping tool, added to the core once and opened to every institution without a per-tenant install, a Python image processing service connects through Node.js to the content management layer on the Laravel side. The tool joined the core as a service; no second system was stood up beside it. On the user’s side that reads plainly: the person preparing content at an institution does this work from the place they already work in, without moving somewhere else.
In the optical mark reading tool, which every tenant runs over its own data without a second deployment, forms are uploaded from the browser and results are written to a MySQL record on the Laravel side. The answer key and the student’s marking meet on the same exam record. Access to the tool does not come from an installation; it comes from the web face of the platform itself.
What would have changed had they been separate products? Every institution would have acquired the tool separately, and moving what the tool produced into its own exam record would have required a transfer step. Like any step done by hand, a transfer carries its own error rate along with it. When the tool is a layer of the core, the result is already created inside the record.
The question that follows is less about who joins the platform than about what joins it. An institution joins the core as a tenant; a tool joins it as a service. In both cases the same thing is being asked: does this piece fit inside the definitions the shared software already carries, or does it want an installation of its own? If it fits, joining comes down to a record operation. If it does not, a single core was the answer to the wrong question, and forcing the piece in is how you manufacture the counter-case above for yourself.