Developer guide
Architecture and deployment boundary
Understand the request lifecycle, application layers, storage boundaries, and deployment shape before extending ExamNova.
MaintainerIntegratorReviewer
Responsibility
ExamNova is a Laravel 12 self-hosted assessment application. Controllers coordinate HTTP requests; requests validate input; services own domain decisions; models persist assessment state; policies and middleware enforce access; jobs handle retryable side effects; private files remain behind authorized routes.
Components and data flow
- Public webroot: front controller and compiled public assets only.
- Application layer: routes, middleware, form requests, controllers, services, models, notifications, and jobs.
- Persistence: MySQL/MariaDB records scoped by institute plus private filesystem objects.
- Operational services: queue worker, scheduler, mail provider, optional HTTP integrations, and update hub.
Rules that must remain true
- The project root, environment configuration, logs, private storage, and source code are not public web assets.
- Controllers remain thin enough that rules can be tested without a browser.
- Domain services receive or derive explicit institute and actor context.
- Retryable jobs are idempotent and preserve tenant context.
Failure modes
- Serving the project root exposes sensitive files.
- Rules embedded only in Blade or JavaScript can be bypassed.
- A queued job without institute context can process the wrong records.
- Public storage paths can expose evidence or result documents.
Safe extension guidance
- Put reusable business decisions in a focused service.
- Validate at the request boundary and authorize before loading or mutating sensitive records.
- Use private storage plus authorized streaming/download responses.
- Document new scheduler, queue, filesystem, or provider requirements.
Required tests
- Application boots with production-like configuration and debug disabled.
- Protected files cannot be requested directly.
- Jobs retain institute context and are idempotent on retry.
- Public routes expose only intended content.
Release gate: Verify routes, cached views/configuration, queues, scheduler, private downloads, and the packaged public-webroot layout.