cd ../case-studies
~/case-studies/tutors-hub.md

Tutors Hub

Shipped
2026-02-1817 min
Next.jsSpring BootPostgreSQLSupabaseFirebase
open live project
Tutors Hub landing page

Tutors Hub: Building a Tutor Marketplace That Is Actually an Operations System

Why I Built It This Way

When I first framed Tutors Hub, it looked like a familiar marketplace problem: parents need tutors, tutors need opportunities, and the platform sits in the middle. That framing was wrong.

The real problem was not discovery. The real problem was operational entropy.

In Dhaka, a tutoring lead is not a clean e-commerce transaction. A parent does not usually browse, compare, click, and pay. They describe a moving target: curriculum, grade, area, gender preference, budget, schedule, urgency, and often a set of unstated social constraints. On the other side, tutors are not interchangeable supply. They have curriculum constraints, location preference, institution credibility, availability windows, document verification requirements, and reliability histories that matter more than a glossy profile page.

So I stopped thinking of Tutors Hub as a listing site and started treating it as a decision-support system for a high-friction, partially manual service business.

That decision changed the entire architecture.

Instead of building only pages and forms, I built a system that tries to reduce uncertainty at every handoff:

  • parent inquiry to lead intake
  • lead intake to executive assignment
  • executive assignment to vacancy creation
  • vacancy to tutor shortlist
  • shortlist to invitation
  • invitation to guardian contact release
  • guardian contact to demo verification
  • demo verification to commission and payout

The product looks like a tutoring SaaS on the surface. Underneath, it is really a controlled workflow engine for matching, trust, and operational accountability.

The Core Product Problem I Am Solving

The problem statement that matters is this:

How do you convert messy, real-world tutoring demand into a structured, secure, auditable pipeline without pretending the business is fully automatable?

That one sentence drove nearly every technical decision.

Most early-stage marketplaces fail because they model the happy path and ignore the human recovery path. In this domain, the recovery path is the business. Parents give incomplete information. Staff compete for the same lead. Tutors overstate fit. Demos get misreported. Sensitive contact information leaks too early. A "simple" admin panel becomes the place where the company either scales or drowns.

So I designed Tutors Hub around three principles:

  1. Human-in-the-loop is a first-class architectural constraint, not a temporary inconvenience.
  2. Sensitive data should be revealed only after trust milestones, not merely after login.
  3. Workflow state matters more than page rendering, because the business is mostly transitions, not screens.

What the System Became

At the infrastructure level, the stack is intentionally split:

Next.js 14 frontend
  -> server-side API proxy and middleware
  -> Spring Boot backend
  -> PostgreSQL / Supabase
  -> Firebase push, Google Drive, bKash-oriented payout layer

That split matters because the frontend is optimized for onboarding and operator UX, while the backend owns state transitions, authorization, scoring, and security-sensitive workflows.

I ended up with several bounded business zones:

  • Lead intake and hire requests
  • Executive assignment and operational routing
  • Vacancy creation and approval
  • Tutor application and profile verification
  • Match scoring and shortlist generation
  • Invitation and T&C acceptance
  • Demo scheduling and fraud-adjacent verification
  • Commission ledger and payout controls

This is why I no longer describe it as "a tutor website." It is closer to a vertical workflow platform for an education placement business.

The Most Important Design Shift: Model the Business as State Machines

The biggest senior-level lesson in this project was that CRUD thinking was too weak for the domain.

For example, a hire request is not just a record. It is a state machine with operational meaning:

new_
-> in_progress
-> negotiated
-> pending_admin_approval
-> matched

or

new_
-> requires_admin_assignment

or

new_
-> duplicate
-> closed

That looks simple until you factor in timing, concurrency, assignment failure, retry semantics, and escalation. Once I treated lead flow as explicit state transitions rather than generic updates, the rest of the backend became much easier to reason about.

The same thing happened with invitations. I did not want a tutor to get guardian contact details just because they clicked "accept." That is too much trust granted too early. So invitation handling became its own controlled lifecycle:

pending
-> portal T&C acceptance
-> WhatsApp code reconciliation
-> tc_accepted
-> guardian contact unlocked

That extra step is not product theater. It is a trust boundary.

The Operational Problems Hidden Behind "Simple Features"

1. Lead Assignment Is Not a Notification Problem

At first glance, assigning a new lead to a staff member sounds trivial. In reality, it is a coordination problem under latency and partial human failure.

When a hire request is created, the backend asynchronously routes it to the least-loaded executive. If nobody is available, the system does not pretend the lead is "assigned." It degrades into a visible requires_admin_assignment state. That matters because silent failure is worse than explicit operational debt.

The next complexity layer was timeout handling. If a lead sits too long without action, the system reassigns it, tracks attempted executives, and avoids cycling the same stale lead back to the same person. If all candidates are exhausted, it escalates to admin handling.

This is where a lot of "self-taught developer" codebases break: they optimize for the first assignment but not the second, third, or failure-driven reassignment. I learned quickly that reliability in operations systems is mostly about what happens after the first intended path fails.

2. Duplicate Detection Is a Business Primitive, Not a Form Validation Rule

Parents do not always submit once. They call, message, fill the form, ask a relative to fill it again, and then follow up on WhatsApp.

If I treated each inquiry as a new lead, the same household would pollute the queue, distort executive workload, and make conversion metrics meaningless. So I added duplicate detection keyed around recent phone history with a 30-day lookback, and I exposed that result in the intake flow instead of burying it in the database.

That is a pattern recognition problem disguised as data entry.

The product is constantly trying to infer whether a new event is actually a new demand signal or merely another expression of an existing one.

3. Matching Tutors Is Not "Search"; It Is Controlled Heuristics

I deliberately avoided pretending I had machine learning just because ranking exists.

The matching system is currently a weighted heuristic model:

  • curriculum fit: 40
  • subject overlap: 30
  • location fit: 15
  • gender preference: 5
  • confirmation proxy: 5
  • reliability: 5

This is important for two reasons.

First, it is interpretable. Executives can see why a tutor is surfaced.

Second, it respects the domain truth that the platform should assist judgment, not replace it. A false sense of algorithmic authority would be dangerous here because the data is sparse, partially subjective, and operationally noisy.

The interesting part is not the weights themselves. The interesting part is that I separated hard filters from soft scoring:

  • hard filters remove obviously ineligible tutors
  • soft scoring ranks the remaining supply

That distinction keeps the system explainable and prevents nonsensical recommendations from ever entering the shortlist.

4. Contact Information Is a Controlled Asset

One of the most important trust decisions in the platform is that guardian contact information is not immediately visible to every authenticated tutor.

A tutor must move through invitation flow and fully accept terms before those details are released. This is not just about privacy. It is about preventing the platform from leaking its highest-value operational asset before a minimum trust ceremony is complete.

This is the kind of design decision that comes from pattern recognition in service businesses: the real attack surface is often not server compromise. It is workflow abuse.

Security Model: What I Protected, Why I Protected It, and Where It Still Hurts

Security in Tutors Hub is not a single feature. It is a stack of trust boundaries.

Authentication and Session Shape

The backend uses Supabase as the identity provider, while Spring Security validates JWTs as an OAuth2 resource server. Roles are derived from JWT metadata, which keeps the authorization model aligned with identity issuance instead of duplicating role truth in too many places.

I intentionally favored httpOnly cookies over local storage because I did not want browser JavaScript to have direct access to session tokens. The frontend proxy layer forwards cookies to the backend and normalizes Set-Cookie handling, which makes the cross-origin story manageable between frontend hosting and API hosting.

This is one of those places where the implementation detail matters: if token handling becomes casual early, every future security decision gets harder.

RBAC and Data Isolation

The system is aggressively role-scoped:

  • tutor routes for self-service and personal data
  • executive routes for lead handling and matching workflows
  • admin routes for approval, settings, payouts, and cross-cutting visibility

The critical point is that RBAC is not only about screen visibility. It is enforced on the backend with method-level authorization and workflow gating. A tutor cannot simply request another tutor's data because the problem is not whether the menu shows a link. The problem is whether the backend accepts the action.

Sensitive Credential Storage

One of the more serious security decisions in the platform is how payout credentials are handled.

I did not want bKash-related credentials stored as plain configuration secrets retrievable by anyone with database access. So the settings layer encrypts sensitive fields using AES-256-GCM, with keys derived from:

  • admin payout PIN
  • server-side master key
  • per-secret salt

This matters because it changes the attack model. A database leak alone is not sufficient to recover the credential material. The design forces the attacker to compromise both persistent data and the operational secret context.

That is the kind of defense-in-depth pattern I care about because it acknowledges that perimeter assumptions fail.

File Upload Trust

Tutor document upload is another place where the naive implementation would be dangerous. MIME type checks alone are easy to spoof. So the document layer also validates magic bytes for PDFs, JPEGs, and PNGs before pushing files into storage.

Again, this is not glamorous work, but it is senior work. Mature systems survive by distrusting user input in concrete ways.

Rate Limiting and Abuse Surfaces

There is route-level rate grouping for high-risk actions like:

  • login
  • registration
  • hire request submission
  • tutor application
  • commission payout approval

This was important because the product has public intake surfaces and operator-triggered financial actions. The risk profiles are different, so the thresholds should be different.

The Honest Weak Spots

Now the part I care about most in a case study: what still bothers me.

There are at least three security and reliability debts I consider real:

  1. CSRF is disabled in the backend security configuration.
  2. Rate limiting is instance-local and in-memory.
  3. The payout execution layer is still structurally real but operationally stubbed.

The CSRF point is the sharpest one. Because the platform relies on cookies for authenticated interaction, simply saying "the cookie is httpOnly" is not enough. httpOnly protects against token theft via JavaScript, not request forgery. So while the current posture is better than local-storage auth, it is not the end state I want. A proper CSRF strategy or a stricter session transport model is a necessary next hardening step.

The rate limiter is also honest MVP infrastructure. It works on a single node, but it does not become globally correct once the system is horizontally scaled. The moment I add multiple application instances, that limiter must move to a shared substrate like Redis or an edge-enforced control plane.

And on payouts, I intentionally built the surrounding controls first: feature flags, PIN verification, credential encryption, ledger states, approval workflows. The actual transfer integration remains a stub because I would rather delay the irreversible side effect than fake production-grade financial execution.

That sequencing was deliberate.

Pattern Recognition as a Product Capability

The user specifically asked for pattern recognition, and that is actually one of the most interesting things about this platform.

Tutors Hub does not use that phrase as marketing. It uses pattern recognition operationally.

Pattern 1: Repeated Demand Signatures

Duplicate lead detection is really the first layer of behavioral patterning. The platform asks: have we seen this parent identity signal recently, and if yes, is this a new request or a duplicate demand artifact?

That saves staff time and preserves funnel truth.

Pattern 2: Executive Response Failure

The scheduler watches for leads that were assigned but not acted on quickly enough. That is pattern detection around operator latency. A lead going stale is not just inactivity; it is an early indicator that routing failed.

Pattern 3: Tutor Reliability Drift

Demo verification creates a feedback loop that can detect a tutor's reliability trend. If a tutor repeatedly self-reports completion and executive verification disproves it, the system increments false-report counts and can restrict that tutor after threshold crossing.

That is not merely a moderation rule. It is behavioral pattern recognition feeding back into marketplace quality control.

Pattern 4: Match Quality as Structured Explanation

The shortlist engine captures why a tutor ranks well. This is subtle but important. I am not only predicting fit; I am preserving the explanation graph for operator review. Good systems do not just classify. They leave behind interpretable reasons that humans can audit.

Pattern 5: Feature Rollout as Operational Sensing

I also used feature flags as a pattern recognition mechanism for product maturity. Invitations, gated contact release, demo scheduling, payouts, and escalation can each be enabled in controlled phases. That lets me observe where real operational friction exists before committing every user and staff member to a permanent workflow.

This may sound like release engineering, but it is also epistemology. I am using staged rollout to learn where the business model is stable and where it is still lying to me.

The Hardest Technical Hassles I Faced So Far

This is the part that mattered most to me personally because I am self-taught, and self-taught developers often learn architecture by getting cut by reality.

1. The Frontend/Backend/Auth Triangle

Mixing Next.js middleware, server-side proxying, Supabase identity, and Spring resource-server authorization is not conceptually hard in isolation. The difficulty comes from all of them disagreeing about where session truth lives.

I had to make the browser, the proxy, the identity provider, and the API agree on:

  • cookie transport
  • role visibility
  • redirect behavior
  • auth failure loops
  • cross-origin session persistence

This is the kind of hassle that never shows up in a UI mockup but can consume entire days of engineering time.

2. Concurrency in Human Workflows

A lot of systems are concurrent in theory but single-user in practice. This one is not. Multiple executives can touch the same lead. Admins can intervene midstream. Background jobs can reassign work while humans still believe they own it.

That forced me to combine:

  • soft locks for editing windows
  • optimistic locking on mutable entities
  • pessimistic locking on executive selection
  • scheduled recovery for abandoned assignments

The insight here is that concurrency is not only a database problem. It is a product truth problem. Two staff members acting on stale assumptions can create data that is technically valid and operationally disastrous.

3. Designing for Manual Operations Without Making the Software Feel Manual

This may have been the hardest product challenge overall.

The business still depends on consultant judgment, approvals, calls, negotiation, and document review. If I expose all of that raw complexity to users, the product feels clumsy. If I hide too much of it, the system becomes dishonest and operators lose control.

The only workable answer was to let the UI feel simple while the backend preserves operational nuance.

That is harder than "full automation" in many ways because it requires architectural humility.

4. Building Trust Gradually Instead of All at Once

I learned that trust should be decomposed.

A tutor being authenticated does not mean they should see guardian contacts.

A staff member being active does not mean they should receive the next lead if their queue is already overloaded.

A verified user does not mean their future behavior will remain reliable.

A payout action being admin-only does not mean the underlying credentials are safely stored.

That pattern changed how I think about system design. Mature systems do not ask "is the user logged in?" They ask "what level of trust has been earned for this exact action?"

What This Project Says About Me as an Engineer

This project is probably the clearest expression of how I think now as a self-taught developer growing into systems design.

I do not get excited by complexity for its own sake. I get interested when a messy business process can be translated into something explicit, auditable, and safer than the manual version it replaces.

What I am proud of here is not just that I built forms, dashboards, and APIs. It is that I kept discovering the real problem hiding behind the visible one.

The visible problem was "help people find tutors."

The real problems were:

  • how to reduce duplicate operational noise
  • how to route leads fairly under human latency
  • how to expose contact data only after trust conditions are met
  • how to rank imperfect supply without pretending certainty
  • how to record enough workflow state that the business can debug itself
  • how to secure credentials and documents in a system that will inevitably handle sensitive information

That shift in thinking is what made this feel less like building a website and more like designing a business operating system.

Where I Would Take It Next

If I continue this system, the next senior-level moves are obvious:

  • introduce a distributed rate-limiting layer
  • add proper CSRF protection or tighten the session transport model
  • move time-based workflow orchestration toward a more explicit job/event system
  • improve match quality with feedback loops from successful placements, not only static tutor attributes
  • add tamper-evident audit logging around admin and payout operations
  • formalize escalation logic as a policy engine instead of scattered business rules

That is the path from "strong product-engineering foundation" to "operational platform I would trust at scale."

Final Reflection

Tutors Hub taught me that senior engineering is often less about writing harder code and more about refusing to lie about the nature of the system.

This was never just a tutor marketplace.

It was a workflow coordination problem, a trust management problem, a reliability problem, a privacy problem, and a pattern recognition problem hiding inside an education service business.

Once I accepted that, the architecture became much more honest.

And honest architecture is usually what survives.