September 17, 2026 • 13 min read
How I built Domina ENEM: architecture, applied AI, and scaling exam processing
How I designed the architecture for Domina ENEM, combining Next.js, Supabase, deterministic data pipelines, and LLMs to turn years of exam papers into structured data.
How I built Domina ENEM: architecture, applied AI, and scaling exam processing
Domina ENEM started with a relatively straightforward idea: build a platform to help students prepare for the ENEM exam in a more targeted manner, using their own performance results to highlight exactly where to study and practice.
The challenge was that turning this idea into a production product required far more than building a few UI screens and storing questions in a database.
I had to solve problems across software architecture, data processing, educational content classification, and artificial intelligence—while designing a foundation from day one that could scale without degenerating into a disconnected patchwork of features.
In this post, I want to share how I designed the architecture of Domina ENEM, the core technologies used, and, most importantly, how I applied AI to product and software engineering to solve one of the project's biggest hurdles: turning years of raw ENEM exams into structured, application-ready data.
What is Domina ENEM?
Domina ENEM is an exam preparation platform centered around deliberate practice, diagnostic assessment, and continuous performance tracking.
Rather than acting as just a simple question bank, the goal is to progressively understand which topics and competencies a student has mastered and where knowledge gaps remain.
Key features planned and developed around this core concept include:
- ENEM question practice;
- Custom problem sets;
- Mock exams;
- Initial knowledge diagnostic;
- Performance tracking broken down by knowledge area, discipline, topic, and skill;
- Personalized study recommendations;
- Error review workouts;
- Flashcards;
- Study goals and daily quests;
- Gamification and progress milestones.
From the beginning, a critical architectural decision was to avoid modeling an exam question as merely a problem statement with five multiple-choice options.
To generate meaningful learning insights, the system needed to understand what each question was actually evaluating.
Consequently, content was structured along a multi-tiered taxonomy:
Knowledge Area → Discipline → Topic → Subtopic → Skill
This taxonomy became a foundational pillar for application architecture, recommendation algorithms, and performance analytics.
Main Stack
The Domina ENEM web application was built with:
- Next.js
- React
- TypeScript
- Tailwind CSS
- shadcn/ui
- Supabase
- PostgreSQL
- Stripe
- Generative AI / LLMs
- Structured Outputs
- Prompt Engineering
- Data processing and validation pipelines
I also leveraged AI engineering tooling throughout development to accelerate implementation, explore technical approaches, write documentation, and conduct architectural reviews.
However, a paramount goal was avoiding the trap of treating AI merely as a "code generator."
The objective was to integrate models as modular software components with well-defined contracts, strict validations, and deterministic controls.
One of the biggest challenges: turning ENEM exam papers into data
For an exam preparation platform, there is an immediate question: where do the questions come from?
INEP (the Brazilian National Institute for Educational Studies and Research) releases official exam booklets and answer keys, but this data is not delivered in an application-ready format.
An exam booklet is designed for human eyes. An application requires structured data.
For every single question, the system needs to resolve:
Question
├── year
├── number
├── knowledge area
├── discipline
├── statement
├── alternatives
├── correct answer
├── images / assets
├── topic
├── subtopic
├── skill
├── difficulty
└── explanation / resolution
Multiply that across multiple exams over many years.
At this scale, manual curation quickly proved unsustainable.
That was the turning point where AI shifted from being an engineering productivity tool to an integral part of Domina ENEM's data processing pipeline.
Building an exam extraction pipeline with AI
Instead of embedding ingestion logic inside the core web application, I created a dedicated, standalone data processing project.
The goal was to treat exam import as an independent data pipeline responsible for transforming disparate inputs into a canonical schema consumed by Domina ENEM.
The pipeline architecture follows this flow:
Data Sources
↓
Extraction
↓
Normalization
↓
Official Answer Key Validation
↓
Asset Processing
↓
Taxonomy Classification
↓
AI Resolution Generation
↓
Validation
↓
Persistence
This decoupled architecture solved a critical system design concern: Domina ENEM does not need to know whether a question originated from a PDF, a third-party API, or another format. Every record enters the database following the exact same canonical contract.
Normalization before intelligence
One of the most important decisions was never sending raw data directly to an LLM while blindly trusting the output.
Before any AI enrichment occurs, there is a deterministic normalization phase. This stage standardizes varying input shapes into a single strict schema.
Conceptually, this resembles:
interface EnemQuestion {
year: number;
number: number;
statement: string;
alternatives: Alternative[];
correctAnswer: string;
assets: Asset[];
taxonomy: {
area: string;
subject: string;
topic: string;
subtopic?: string;
skill?: string;
};
explanation?: QuestionExplanation;
}
This establishes clean decoupling across:
Data Source → Internal Model → Application
This approach also significantly simplified onboarding future data sources into the system.
AI as a system stage, not as a source of truth
This is perhaps the most valuable lesson learned while building the system.
LLMs excel at interpretation, semantic classification, and structured generation. However, they should never be treated automatically as a source of truth.
For instance, I do not want a model to "guess" the correct answer to an ENEM question when INEP has already published the official answer key.
The proper architecture is:
LLM → Interpretation
INEP → Official Truth
The official answer key remains the validation benchmark. AI is utilized precisely where semantic understanding is needed that cannot be easily solved with static procedural rules alone.
Automatically classifying questions
Once a question is extracted, the next problem emerges: what specific subject matter does this question evaluate?
This data is crucial for Domina ENEM. Without accurate categorization, the platform cannot deliver actionable feedback like:
- "You are struggling with this particular topic."
- or "You need additional practice targeting this specific competency."
I utilized AI to assist with classification.
Crucially, the model is never permitted to invent arbitrary categories. It must map content strictly onto the system's predefined taxonomy.
This turns an open-ended prompt into a constrained pipeline:
Question + Allowed Taxonomy + Classification Rules
↓
LLM
↓
Structured Classification
The output is then deterministically validated. In short: probabilistic generation paired with deterministic validation.
This design pattern repeats across several stages of the architecture.
Generating step-by-step explanations with AI
Writing detailed, step-by-step solutions manually for thousands of exam questions would be prohibitively time-consuming and expensive.
This is an area where LLMs shine. I built a pipeline where the model receives:
- The question statement;
- Answer choices;
- Official correct answer;
- Relevant contextual background;
- Resolved taxonomy;
- Domain-specific pedagogical guidelines.
From this prompt context, the model produces a structured explanation.
However, the generated content is not immediately marked as published. It enters the system as a draft, allowing for review and validation before reaching students.
This establishes an explicit confidence boundary:
AI generated → Draft → Validation → Published
In production AI systems, this distinction is paramount: not all generated data carries the same level of confidence.
Structured Outputs instead of free text
Another major shift in my approach to AI engineering was completely abandoning unstructured free-text responses whenever possible.
If the downstream application expects:
{
"topic": "...",
"difficulty": "...",
"explanation": "..."
}
relying on arbitrary text responses that require fragile post-processing regex or parsing is an anti-pattern.
Wherever possible, I enforce structured outputs governed by strict schemas.
This provides clear advantages:
- Predictable data contracts;
- Elimination of manual text parsing;
- Automated schema validation;
- Robust error handling;
- Seamless integration with TypeScript types;
- Programmatic retry strategies;
- Clear observability into invalid payload shapes.
In practice, the LLM functions predictably like any other component in a traditional software architecture.
Asset validation
Exam questions frequently feature diagrams, graphs, charts, and illustrations. Downloading these files is not enough—the pipeline must ensure every ingested asset is completely valid.
I incorporated strict automated validation checks:
- Magic byte file signatures;
- MIME type verification;
- Image dimensions;
- File size thresholds;
- SHA-256 hash deduplication;
- Relational mapping between question and image assets.
This prevents silent data corruption during ingestion from ever reaching the student-facing UI.
Blocking pipeline
Another core design principle was avoiding the trap of: "import everything now and troubleshoot failures later."
Several pipeline validations are strictly blocking. If a question is missing critical attributes or fails validation checks, the pipeline halts persistence for that record.
The execution model follows:
Extract → Normalize → Validate → Enrich → Validate again → Persist
The earlier an error is caught in the data pipeline, the cheaper and safer it is to remediate.
Idempotency and reprocessing
A fundamental challenge when operating data pipelines is being able to re-run ingestion without duplicating or corrupting existing records.
The pipeline was engineered around strict idempotency and database upserts. This accommodates real-world workflows such as:
Process 2023 Exam → Fix Extraction Rule → Re-run 2023 Pipeline → Upsert Existing Records
Existing questions are updated in place without duplicate records or broken foreign key relationships. This resilience becomes increasingly vital as data volume scales.
Decoupling the application from the data pipeline
One of the architectural decisions I am most satisfied with was keeping the primary web application and the data pipeline in separate services.
Domina ENEM's web frontend and backend focus strictly on product experience:
User → Domina ENEM Web App → PostgreSQL / Supabase
Meanwhile, the pipeline operates with a distinct responsibility:
External Data Sources → Pipeline → Normalized Data → Domina ENEM
This separation minimizes coupling. An unexpected edge case or failure during exam ingestion will never impact or degrade the live web application used by students.
Using AI across the development process
AI was also an integral part of developing the product itself. However, I avoided the naive pattern of:
prompt → generate snippet → copy & paste → done
Instead, I integrated AI models and agents as collaborative engineering partners for tasks including:
- Architectural exploration;
- Feature scaffolding;
- Unit and integration test generation;
- Code refactoring;
- Bug diagnosis;
- Technical documentation;
- Interface contract definitions;
- Trade-off analysis;
- Implementation reviews;
- Data processing and classification.
For this workflow to succeed, the context provided to the agent is just as critical as the model's baseline capabilities.
I curated project documentation, domain rules, and architecture guidelines so that agents operate within clear constraints:
Product requirements + Architecture + Domain rules + Coding conventions + Feature specification
↓
AI Agent
↓
Implementation
This transforms AI into a reliable, consistent engineering multiplier.
How my view of AI Engineering evolved
Building Domina ENEM shifted my perspective on AI engineering.
Early on, it is easy to equate AI engineering with merely calling an API endpoint. Yet the most challenging and interesting engineering problems reside around the model:
- How do you guarantee the response conforms to the expected structure?
- How do you validate semantic output programmatically?
- How do you prevent hallucinations?
- Which information must originate from deterministic sources?
- When should traditional heuristics replace probabilistic AI?
- How do you reprocess thousands of records when prompts or models change?
- How do you version AI-generated artifacts?
- How do you score confidence?
- Where do humans need to remain in the loop?
At this point, AI engineering aligns far more closely with traditional systems engineering than with simple prompt tinkering.
The LLM is just one component. Around it, you still need schemas, contracts, pipelines, validation layers, observability, fallbacks, storage, and business rules.
Key technical challenges
Domina ENEM presented several fascinating engineering challenges:
- Transforming unstructured documents into data: Converting exam booklets designed for humans into structured, relational records required careful extraction, normalization, asset handling, and multi-stage validation.
- Designing an educational taxonomy: Storing questions was insufficient. The knowledge evaluated by each question had to be systematically modeled to enable personalized performance analytics.
- Integrating AI reliably: The core challenge was not connecting to an LLM, but establishing where to trust model output and where to enforce deterministic boundaries.
- Maintaining predictable outputs: Structured outputs, schemas, and runtime assertions were vital to integrating probabilistic results into a typed codebase.
- Architecting for reprocessing: Prompts, models, and taxonomy rules evolve over time. The architecture needed to support seamless data reprocessing without manual intervention.
- Decoupling system concerns: Keeping ingestion, AI enrichment, and user-facing application tiers separated made the entire ecosystem substantially easier to scale and maintain.
Core technologies used
Throughout the project, I worked extensively with:
- Frontend: Next.js, React, TypeScript, Tailwind CSS, and shadcn/ui.
- Backend & Database: Supabase, PostgreSQL, relational schema modeling, authentication, storage, and Row Level Security policies.
- AI Engineering: LLMs, Prompt Engineering, Structured Outputs, semantic classification, synthetic content generation, AI pipelines, and human-in-the-loop workflows.
- Data Engineering: ETL, data normalization, schema validation, asset processing, cryptographic hashing, idempotency, and upserts.
- Software Architecture: Separation of concerns, inter-system contracts, data pipelines, runtime schemas, and domain-driven design principles.
Far beyond a frontend project
As an engineer with a deep background in frontend development, a personal goal with Domina ENEM was deliberately stepping outside my comfort zone.
The project pushed me to consider the complete software lifecycle well beyond React components and user interfaces:
Product Vision → User Experience → Frontend → Backend → Database → Pipelines → AI → External Data
And that is what makes this project so rewarding. Domina ENEM serves as a live engineering lab where I can explore software architecture, full-stack development, product thinking, and Applied AI Engineering on a meaningful, real-world problem.
Next steps
There are still many exciting challenges ahead.
Among them are refining personalized recommendation algorithms from student performance data, improving mastery assessment, optimizing spaced-repetition error review, and exploring new applied AI touchpoints within the study workflow.
Throughout all of it, one core philosophy remains: leverage AI where it genuinely expands product capability, rather than simply because AI is available.
Domina ENEM continues to be an incredible journey in learning how to build software where AI models are not just isolated novelty features, but reliable building blocks within a resilient software architecture.
If you'd like to check out the project:
👉 dominaenem.com