Stoodi
A verified-student social network combining real-time messaging, matching, groups and events. I designed and built the backend domain architecture, identity verification, trust-and-safety pipeline and the CI-enforced boundaries that keep it maintainable as it grows.
Source code is private. The case study focuses on domain boundaries, identity trust and the CI-enforced architecture contract rather than user-specific data.
- Product model
- Verified-student social network
- Audience
- University students
- Timeline
- 2025
- My role
- Backend architect & platform engineer
- Context
- Private commercial product
- Delivery scope
- Domain architecture · realtime · trust & safety · CI/CD
01
Product surfaces
Product surfaces
Distinct product surfaces made school, association and campus responsibilities visible before getting into the backend architecture.
Student verification flow
Enrollment claim, registry matching and reminder path before an account gains full access.
Discovery & real-time messaging
Matching and direct/group conversations delivered over authenticated WebSocket connections.
Trust & safety operations
Report review, sanctions and appeals operating on the same verified identity as the social graph.
02
Problem and constraints
Problem and constraints
A student social product cannot rely on self-reported enrollment, and cannot let 11 growing feature areas — identity, discovery, messaging, groups, events, moderation and billing — collapse into a tangle of direct imports. Trust and architecture had to be enforced, not assumed.
Unverifiable identity claims
A self-reported school claim gives no real trust signal for a product built around verified students.
Real-time messaging needs its own auth boundary
A long-lived HTTP credential is not a safe fit for a persistent socket connection.
Domain growth pressure
Eleven feature areas sharing one codebase tend to erode isolation under delivery pressure unless boundaries are enforced mechanically.
Safety cannot be purely reactive
Some risks, like underage users, need automatic detection and containment rather than waiting for a manual report.
03
My exact ownership
My exact ownership
I designed
- The full domain architecture: 11 bounded contexts, each behind services, selectors and adapters, composed through explicit ports rather than direct imports.
- The trust model linking claimed enrollment, registry verification and moderation into one coherent identity lifecycle.
- The deterministic matching and ranking approach behind discovery, and the ticket-based real-time authentication model.
I implemented
- All 11 Django domains, their APIs, background tasks, and the ADR-driven adapter pattern wired at the composition root.
- The verification pipeline against the ESR open dataset, the moderation and sanctions pipeline, and automated underage detection.
- The 12-stage CI pipeline: linting, strict typing, import-linter, security scanning, coverage gates, migration checks and OpenAPI breaking-change detection.
I collaborated on
- Product behavior on the Flutter client consuming these APIs, and how real-time and verification states surface to users.
- Trust-and-safety policy decisions translated into the moderation domain’s rules.
03
Objectives
Objectives
Trust
Tie product access to enrollment verified against an external, authoritative registry.
Boundaries
Keep 11 domains independently testable and enforce isolation as a CI-gated contract.
Realtime
Deliver messaging over persistent sockets without exposing long-lived credentials to the connection.
Safety
Combine manual moderation with automated detection paths for the highest-risk cases.
04
System architecture
System architecture
Flutter · iOS · Android
DRF · Channels/WebSockets · ticket auth
11 bounded domains · services · ports
PostgreSQL/PostGIS · Redis · Celery · ESR · RevenueCat
The Flutter client, realtime and request APIs, the 11-domain core and external services stay separated so no layer needs to understand another’s internals.
Technology stack
Application
Django 5 · Django REST Framework · drf-spectacular · ASGI/Daphne
Realtime & async
Channels · channels-redis · Celery · Redis
Data & storage
PostgreSQL · PostGIS · S3-compatible storage
Integrations & operations
RevenueCat · Sentry · Prometheus · structlog · ESR open dataset
05
Core domain model
Core domain model
The data model keeps verified identity, the social graph and the safety layer distinct. A school is validated against an external registry before it can anchor a verification, connections and conversations depend on that verified identity, and moderation acts on the same identity without folding into product tables.
School (registry-verified)
Synced from the ESR open dataset and matched by normalized name similarity.
Account & verification
Local identity plus an independent enrollment-verification record and state.
Profile & discovery readiness
Media, coarse location and visibility gates before a profile enters matching.
Connection
Friend requests and blocks scoped to verified accounts.
Conversation & message
Direct and group messaging delivered over authenticated realtime sockets.
Community & event
Groups with roles, and events with participation tied back to membership.
Moderation case
Reports, evidence, sanctions and appeals attach to the verified identity, not to a generic user row.
06
State model
State model
Verification and trust are one lifecycle: an enrollment claim only becomes access after external evidence, and later safety events act on that same verified identity rather than a separate account concept.
Enrollment claimed
The user submits a school and identity claim.
Registry matched
The claim is normalized and matched against the ESR school catalog.
Verified
The account gains full access once enrollment is confirmed.
Trust maintained
Reports and sanctions reference this same verified identity going forward.
Alternate transitions
Verification mismatch
A registry mismatch is routed to review instead of silent accept or reject.
Underage detected
The account is auto-suspended and sessions revoked pending review, independent of manual moderation.
Verification and trust form one lifecycle: access follows external evidence, and safety events act on that same verified identity rather than a parallel concept.
07
Engineering challenges
Engineering challenges
Verifying real students without becoming a registry authority
Self-reported enrollment gives no defensible trust signal, but the platform cannot own an authoritative student registry itself.
Sync and normalize the French Ministry of Higher Education open dataset, match claims by name similarity, and route mismatches to a review and reminder path instead of an automatic accept or reject.
Verification depends on external data freshness and occasional manual review, but access is grounded in defensible evidence instead of trust in the user alone.
If enrollment is only self-reported, the verified-student premise collapses and bad actors can pose as students freely.
Domain boundaries that survive growth
Eleven domains sharing one codebase are one convenient import away from becoming entangled as features accumulate.
Enforce import-linter contracts at CI, document every legitimate cross-domain exception as an ADR-backed adapter registered at a single composition root, and move cross-domain facts through a transactional outbox.
Every cross-domain interaction costs more ceremony than a direct call, but domains stay independently testable and replaceable.
Without enforcement, delivery pressure erodes boundaries import by import until domains can no longer change independently.
Real-time messaging without a long-lived credential on the socket
Exposing a standard access token directly to a persistent WebSocket connection extends its exposure window well beyond a normal request.
Exchange a short-lived realtime ticket for the socket handshake, and track device-level sessions independently from HTTP authentication.
The client needs an extra round trip to obtain a ticket before connecting, in exchange for a bounded credential on the socket.
A leaked long-lived token would otherwise grant sustained access to a live conversation stream instead of a short, revocable window.
08
Failure modes & mitigations
Failure modes & mitigations
Registry mismatch during verification
A normalized-name mismatch is routed to a review and reminder path instead of being silently accepted or rejected.
Underage user detected
The account is automatically suspended and its sessions revoked pending review, independent of the manual moderation queue.
Cross-domain event not yet delivered
The transactional outbox persists the fact durably and supports replay instead of losing it on a downstream failure.
Invalid or expired realtime ticket
The socket connection is rejected explicitly, forcing a fresh ticket exchange instead of degrading silently.
09
Technical decisions
Technical decisions
Ports, not imports, across domains
Cross-domain reads go through adapters registered at a composition root, with import-linter enforcing the boundary as a CI-gated contract rather than a convention.
A transactional outbox for cross-domain facts
Domain events are persisted durably and replayed rather than fired and forgotten, so a downstream domain never silently misses a fact.
Trust built on external evidence
Enrollment is checked against the ESR school registry instead of accepting self-reported claims, with mismatches routed to a review path instead of being silently accepted or rejected.
10
Security & operations
Security & operations
CI-enforced architecture boundaries
import-linter contracts and ADR-documented exceptions gate every merge, not just code review discipline.
Fail-fast production configuration
Startup validation refuses debug mode, wildcard hosts, insecure cookies and placeholder secrets instead of accepting them silently.
Key rotation and encrypted secrets
JWT signing keys rotate with multiple accepted verification keys, and OTP secrets are Fernet-encrypted at rest.
Verified billing webhooks
RevenueCat webhooks are validated by HMAC signature before any entitlement is granted.
- 85% line and 80% branch coverage gates enforced in CI.
- A 12-stage pipeline covering security scanning, migration freshness and OpenAPI breaking-change detection.
- Structured logs, Sentry and Prometheus metrics for operational visibility.
11
Delivered system
Delivered system
Implementation outcomes — no unverified commercial metrics.
A defensible trust boundary
Access is grounded in registry-checked enrollment rather than a self-reported claim.
Isolation enforced, not assumed
11 domains stay independently testable because CI, not convention, enforces their boundaries.
An auditable safety pipeline
Reports, sanctions and appeals operate on the same verified identity with both manual and automatic paths.
Realtime without losing session control
Messaging runs over persistent sockets while credentials stay short-lived and revocable.
What I learned
“Architecture boundaries only hold if CI enforces them mechanically — conventions alone erode under delivery pressure. And trust in a social product has to be built on external evidence, not self-reported claims.”