From Shared Course Links to a Production LMS: The Architecture Behind MBT Institute’s Learning Platform
Building a learning management system sounds straightforward at first. You need courses, videos, payments, and a way for students to log in.
But once you start building for a real education business, you quickly realise that the difficult part isn't putting everything on a screen. It's making sure everything works together.
Who should have access to a course after making a payment? What happens when a student changes their phone? How do you prevent a paid course from becoming a freely shared login? What happens when a payment succeeds but the enrolment isn't activated? And how do administrators manage live classes, recordings, payments, and certificates without juggling multiple tools?
These were some of the engineering questions behind the MBT Institute LMS, built and shipped by Tensorik Technologies.
We set out to build more than a mobile application. We wanted to bring course enrolments, payment processing, content access, live classes, recordings, certificates, and administration into one connected platform.
The application launched on Google Play in October 2026, ahead of its inauguration at MBT Institute's fifth-anniversary student meet-up in Vijayawada on 24 October.
Since then, I've had people reach out asking about the architecture, the technology choices, and how they could build something similar.
So, rather than treating this as just a product announcement, I wanted to share some of the engineering decisions behind the platform, how its main components fit together, and what I'd recommend to someone building a similar system.
1. The Problem Was Bigger Than Delivering Course Videos
Managing digital education involves much more than uploading study material.
Course access needs to be controlled. Payments need to map correctly to enrolments. Students need a reliable way to attend classes and revisit recordings. Administrators need visibility into what's happening across the platform.
When these activities are spread across disconnected tools, routine operations become complicated.
For MBT Institute, we needed to support two commercial categories — hardware courses and software courses — with distinct payment rules. We also needed to reduce unauthorised account sharing without making legitimate students struggle whenever they changed devices.
We identified five core requirements:
-
Identity and access: Authenticate learners and verify their entitlement to individual courses.
-
Payment integrity: Validate transactions, enforce category-specific pricing and coupons, and maintain traceable billing records.
-
Content protection: Bind enrolments to authorised devices and restrict access to protected media.
-
Learning delivery: Integrate live classes, recordings, notifications, and certificates into the learner experience.
-
Operational control: Give administrators the tools to manage courses, device transfers, payments, and support.
The key design decision was to treat these as connected parts of one system, not as separate features with independent business logic.
2. The Architecture: Three Applications, One Shared Backend
We structured the platform around three independently deployable applications: a Flutter mobile app, a NestJS backend API, and a Next.js administration console.
Supabase PostgreSQL provides the relational data layer, with supporting services for authentication, media, payments, live classes, notifications, and asynchronous processing.
Here's a high-level view of how the different components fit together.
View the MBT Institute LMS Architecture Diagram
Figure: High-level system architecture of the MBT Institute LMS, built by Tensorik Technologies.
The architecture separates the learner experience, administrative workflows, backend business logic, and data layer while connecting them through clearly defined interfaces.
Why separate the applications?
Flutter owns the learner experience, including course access, checkout initiation, live-class schedules, and playback interfaces.
NestJS owns the core business rules, validating requests and coordinating enrolments, payments, device access, and learning resources.
Next.js provides the administration interface for authorised staff to manage courses, payments, device transfers, and support.
Supabase PostgreSQL maintains the relationships connecting learners, courses, enrolments, payments, invoices, and device associations.
The key principle is that the mobile app and admin console are separate clients, not separate implementations of the same business rules.
A frontend can improve usability, but it should not be the final authority on whether a payment is valid or whether a learner can access protected content.
3. The Technology Stack and Why It Fits
Here is the stack we used and the responsibilities it serves.
| Component | Technology | Responsibility |
|---|---|---|
| Learner application | Flutter, Dart | Cross-platform mobile development, with Android as the shipping target |
| Backend | NestJS, TypeScript | Modular API and centralised business logic |
| Administration | Next.js | Course management and operational workflows |
| Database | Supabase PostgreSQL | Relational data, transactions, and persistent state |
| Authentication | Supabase Auth | OTP and Google sign-in |
| Access controls | PostgreSQL RLS, database functions, application authorization | Enforce permissions at appropriate layers |
| Payments | Razorpay | Checkout and payment processing |
| Asynchronous processing | Redis, BullMQ | Background jobs and queued work |
| Live classes | Zoom OAuth integration | Session creation and scheduling |
| Notifications | Firebase Cloud Messaging and local notifications | Learner communication |
| Media delivery | Cloudinary and object storage | Media and resource delivery through authorised access paths |
The choices were driven by the product's requirements rather than a desire to use as many technologies as possible.
For example, PostgreSQL fits a system with closely related entities such as orders, payments, enrolments, courses, and devices. Those relationships need to remain consistent as the application evolves.
NestJS gives us a modular structure for keeping those business domains organised. Flutter gives us a dedicated mobile application, while Next.js serves the operational interface.
Redis and BullMQ are useful for work that doesn't need to block a user-facing request. However, they aren't substitutes for PostgreSQL transactions or durable business records.
One architectural distinction worth highlighting: the system of record should remain separate from the mechanism used to process background work. Queue a notification or a post-payment task when appropriate, but don't make a queue message the only evidence that a payment or enrolment exists.
4. Designing the Data Model Around the Business
A large part of the engineering work is getting the relationships right.
At a conceptual level, the platform needs entities for:
-
Users and profiles: Who the learner is.
-
Courses and lessons: What educational content exists.
-
Orders and order items: What the learner intends to purchase.
-
Payments: What the payment provider reports about a transaction.
-
Enrolments: Which courses the learner is entitled to access.
-
Devices and transfer requests: Which device is associated with an enrolment and how that association can change.
-
Invoices: The billing records associated with purchases.
-
Sessions and recordings: How live teaching and recorded content relate to courses.
The important relationships are more valuable than any individual table.
A payment should be traceable to the appropriate order. An enrolment should represent an entitlement, not merely a successful frontend redirect. A device association should be checked against that entitlement. An invoice should be traceable to the underlying transaction.
This separation makes the system easier to reason about when something goes wrong.
For example, if a learner reports paying for a course but cannot access it, an administrator should be able to investigate the order, payment state, enrolment, and device association independently.
That is considerably more reliable than treating a successful checkout screen as proof that every downstream operation completed correctly.
5. The Most Important Flow: Payment to Enrolment
Payment processing is one of the places where seemingly simple applications become complicated.
Consider this sequence:
-
A learner selects a course and initiates checkout.
-
The backend validates the course, price, applicable commercial rules, and coupon.
-
An order is created and associated with the appropriate payment track.
-
The learner completes checkout through Razorpay.
-
The backend verifies the payment result using trusted provider information.
-
The system records the verified payment and activates the appropriate enrolment.
-
The transaction's invoice and related records become available to the administrative workflow.
The frontend redirect alone must not activate an enrolment. A client can be interrupted, a network request can fail, or a user can manipulate client-side state.
A production implementation should also account for duplicate provider notifications, retries, and interrupted processing.
That means designing for idempotency: processing the same verified payment event more than once should not create duplicate enrolments or invoices.
Database constraints and transactions should protect the critical relationships. If some downstream work must happen asynchronously, use a reliable handoff pattern so that a committed payment doesn't leave required work silently lost.
The precise implementation depends on the payment provider's event semantics and the application's transaction boundaries, but the principle is universal: payment verification and entitlement activation need an explicit, recoverable workflow.
For an LMS, this isn't merely a checkout concern. It determines whether a paying learner can access what they purchased.
6. Content Protection: From Login to Enforced Entitlement
Content protection was one of the central requirements of the project.
A login proves that someone has authenticated. It doesn't automatically prove that they have purchased a particular course, or that their current device is authorised to access it.
We addressed this through several complementary controls.
Device-bound enrolments
Each enrolment is associated with one active device. The device fingerprint is stored in secure storage, and the backend verifies the association during session access.
A second phone does not automatically receive access just because the same credentials are used.
The important part is server-side enforcement. A device check performed only in Flutter could potentially be bypassed by modifying the client or calling the API directly.
Device identifiers also have limitations: they should not be treated as cryptographic proof of identity. The backend must validate the relevant session, enrolment, and device association rather than blindly trusting an identifier supplied by a client.
Controlled device transfers
Real students replace phones, lose devices, or need help recovering access.
We implemented an administrator-approved device-transfer workflow so that a legitimate change doesn't require unrestricted multi-device access.
The workflow makes the change reviewable and gives administrators a way to manage exceptions.
This is an important product lesson: security controls need a recovery path. Otherwise, a feature designed to prevent abuse can become a source of legitimate customer frustration.
Protected media delivery
Course media and resources are served through authorised, short-lived URLs rather than intentionally exposed public links. Protected playback surfaces also use screenshot restrictions where supported by the platform.
These controls reduce casual copying and limit the usefulness of leaked links.
They don't make piracy impossible. Another device can record a screen, and determined attackers may find ways around client-side restrictions.
The goal is to make unauthorised access harder through layered enforcement: authenticate the learner, verify the entitlement, validate the device, and restrict access to the media itself.
Where RLS fits into the design
Supabase Row Level Security is another important layer, but it serves a different purpose from application-level authorization.
RLS can restrict which rows a database role or user is allowed to access. Application authorization can enforce domain-specific rules, such as whether a particular enrolment permits access to a particular lesson.
Neither should be treated as a substitute for the other.
For anyone building a similar platform, review every table exposed to client access, enable appropriate RLS policies, and ensure privileged server credentials never reach the mobile app or browser. Database functions and privileged backend operations should also be narrowly scoped and validated.
The objective is to have multiple appropriate enforcement points, not to rely on a single hidden button or client-side check.
7. Keeping Hardware and Software Payments Separate
MBT Institute offers hardware and software courses under different commercial rules. We separated their payment configurations so that pricing, coupons, and reporting could be managed independently.
Coupons are scoped to their relevant category and validated by the backend before discounts are applied.
This avoids making the frontend the authority on which discount a learner can use.
The system also maintains transaction-level payment and enrolment records, with invoices generated for individual purchases. This provides a more traceable foundation for billing reconciliation.
For engineers building a similar product, I'd recommend defining the order, payment, enrolment, and invoice relationships before implementing the checkout interface.
A checkout can look complete while leaving difficult questions unanswered:
-
What happens if payment succeeds but the client times out?
-
What happens if a payment event is delivered twice?
-
What happens if an order is cancelled or refunded?
-
How can an administrator investigate a payment that doesn't match an enrolment?
Thinking through those cases early usually saves time later.
8. Live Classes, Recordings, and Background Work
The learning experience also needed to support scheduled classes and recorded sessions.
We integrated Zoom through OAuth so administrators can create and schedule live classes. Students can discover their sessions through the application, while recordings can be published back into the corresponding course with chaptered playback.
This keeps the learning journey connected instead of distributing the live-class experience and revision material through unrelated links.
Redis and BullMQ support asynchronous processing where it makes sense. For example, notification delivery and other retryable background operations can be separated from the request that initiated them.
That separation improves responsiveness, but it also introduces operational responsibilities: jobs need appropriate retry policies, failures need visibility, and repeated execution must be safe for operations that can have side effects.
A queue doesn't automatically make a workflow reliable. Reliability comes from designing how the system records state, retries work, and recovers from partial failure.
9. Taking the Platform Into Production
The application launched on Google Play in October 2026 and was inaugurated during MBT Institute's fifth-anniversary student meet-up in Vijayawada on 24 October.
The early production period gave us useful signals about adoption and system activity.
In the snapshots reviewed on 27 October 2026:
-
500+ Google Play downloads, alongside a 5.0-star rating and 123 reviews in the captured listing.
-
Multi-lakh revenue across hardware and software course categories, reflecting commercial transactions processed through the platform.
-
1.97 million+ Supabase API Gateway requests reported over 20–27 October, with 13,937 response errors and a displayed response speed of 223.19 ms.
-
219,435 requests reported by the Render backend over its seven-day reporting window.
These figures measure different things. Downloads are not active learners, revenue is not profit, and API requests are not a measure of learning engagement. The service metrics also come from separate systems and reporting windows, so they should not be added together.
The numbers are useful as operational signals, but they need context before being used to draw conclusions about performance or growth.
The launch reinforced a lesson that applies to any production application: releasing the product is only one milestone. You also need observability, capacity planning, resource alerts, and a clear process for investigating unexpected traffic.
10. If I Were Building a Similar LMS From Scratch
For developers and founders who have asked me how they could build something similar, I'd start with the domain model and the critical business flows, not the UI.
Here's the order I'd recommend.
Step 1: Define the core entities
Model users, courses, lessons, orders, payments, enrolments, devices, and invoices. Define their relationships and the constraints that must always hold.
Step 2: Build authentication and authorization
Implement sign-in, learner identity, administrative roles, enrolment checks, and RLS policies. Establish which operations are available to the learner, administrator, and backend.
Step 3: Build the payment lifecycle
Create server-side order validation, provider verification, idempotent payment processing, enrolment activation, and reconciliation workflows. Test failed and duplicate events before adding more features.
Step 4: Add course delivery
Implement course structures, lesson access, protected media delivery, and the device-binding workflow. Keep the authorization decision on the server.
Step 5: Add the operational console
Build the tools administrators need to manage courses, device transfers, billing, and support. Don't leave essential operational tasks dependent on direct database edits.
Step 6: Integrate live classes and notifications
Connect the live-session provider and implement notification delivery. Introduce background jobs where asynchronous processing provides a clear benefit.
Step 7: Prepare for production
Add structured logs, service metrics, alerts, backups, restore testing, and load testing. Validate the behaviour of the system under retries, partial failures, and unexpected traffic.
You don't need to begin with every integration in place. A smaller, well-designed system with correct payment and access semantics is a better foundation than a feature-rich application with inconsistent business rules.
11. What We Learned Along the Way
A few lessons stood out during the project.
Security belongs in the data model. Device associations, enrolments, and transfer workflows are closely connected. Treating them as separate afterthoughts makes the system harder to secure and maintain.
Business rules need a single authority. The mobile app and administration console should not implement independent versions of payment validation or course entitlement. Centralising those rules reduces inconsistency.
Operational workflows are part of the product. Payment reconciliation, device recovery, and support tools may be less visible than the learner interface, but they determine how manageable the system is in production.
Observability should precede the launch spike. Metrics and alerts are much more useful when the team understands them before unexpected traffic arrives.
Security must account for legitimate users. A restrictive access model without a practical recovery process can create problems for the very learners it is meant to protect.
These lessons extend beyond EdTech. They apply to subscription platforms, digital content products, training businesses, and any system where payments control access to protected resources.
Final Thoughts
The MBT Institute LMS was a reminder that the most meaningful engineering work often happens behind the features users can see.
A course page is only one part of the product. Behind it are questions about identity, payment verification, enrolment state, device access, media delivery, retries, and operational recovery.
At Tensorik Technologies, our job was to make those pieces work together in a way that was useful to students and manageable for the institute.
We brought the core workflows into one platform, shipped the Android application to Google Play, and took it into production.
For me, the most valuable part of the project wasn't simply choosing Flutter, NestJS, or Supabase. It was making the boundaries between those technologies and the business rules explicit enough that the system could operate as one product.
The launch was an important milestone. Building, measuring, and improving the platform from here is the work that follows.
