Skip to content

Software Design Document

Neemias

1. Architecture Summary

Neemias shall be implemented as a modular progressive web app with a backend-primary data layer (Cloudflare Workers + D1) and a local SQLite WASM (OPFS) fallback for offline resilience. The design favors browser delivery, fast startup, and a small contributor-friendly architecture.

2. Design Principles

PrincipleMeaning
Modular by defaultFeatures should be separable into clear boundaries such as attendance, reporting, student management, and synchronization.
Offline-resilientThe UI operates against the backend; falls back to local SQLite WASM (OPFS) when the network is unavailable. Since v0.53.0, an OnlineProxy adapter delegates reads/writes through the Worker API when backend is reachable, with hot-swap on offline transition.
Fast by defaultThe initial app shell, search flow, and repeated screens should benefit from aggressive caching.
Accessible by defaultUI components must satisfy WCAG AA behavior and expose clear feedback.
Auditable by defaultChanges must leave a history trail that survives sync conflicts.
Open-source friendlyThe architecture should avoid unnecessary vendor lock-in and document unresolved decisions explicitly.

3. High-Level Components

ComponentResponsibility
App shellHosts the UI, routing, localization, and global state coordination.
Local data layerStores students, attendance actions, and event history for offline use via SQLite WASM (OPFS). OnlineProxy adapter (v0.53.0+) delegates to Worker API when backend is reachable. EntityRepository pattern (v0.54.0+) abstracts D1 access on the backend.
Sync engineQueues local mutations and marks sync state locally in Phase 1; no remote API calls yet.
Conflict resolverApplies server rules when multiple changes overlap.
Auth/session layerValidates identity, manages session TTL, and gates protected actions.
Media pipelineLoads and caches student photos for immediate display during search.
Reporting moduleReads attendance history and exposes report views.
User management moduleAllows admin-only user actions with immutable user event history.

4. Data Model Outline

EntityKey FieldsNotes
Studentid, displayName, photoRef, status, createdAt, updatedAtRemoval should be represented as a historical event, not silent loss.
AttendanceEventid, studentId, actionType, actorId, actorRole, timestamp, syncStateUsed for audit and conflict handling.
StudentEventid, studentId, eventType, actorId, actorRole, justification, timestamp, syncStateUsed for add, edit, and delete history.
SessionuserId, role, issuedAt, expiresAt, stateSupports offline-aware authentication windows.
UseruserId, username, displayName, passwordHash, roles[], primaryRole, statusStatus is ACTIVE or DEACTIVATED; deactivation is soft only. Roles stored in user_roles join table (v0.56.0+).
UserEventeventId, userId, eventType, actorId, actorRole, timestamp, changePayload, syncStateAppend-only user lifecycle and credential events without password hash exposure.
LocaleResourcelocaleCode, key, translatedTextSupports Brazilian Portuguese by default and future translations.

5. Local Storage Strategy

The client should use Dexie over IndexedDB for application state and queued actions. The store must be treated as the operational source of truth while offline.

Design requirements:

  • Data must remain available without internet.
  • Mutations must be written locally before sync.
  • All domain writes must be atomic transactions that include parent entity changes plus immutable event insertion.
  • The storage layer must support concurrent writes from multiple user sessions as far as browser constraints allow.
  • Student photos should be cached locally so search results can render immediately.
  • Sensitive data should be encrypted at rest using web-standard crypto primitives where feasible.

6. Caching Strategy

Resource TypeStrategyReason
App shell assetsCache firstFast startup and offline availability.
Static localization filesCache first with versioningPredictable UI text loading.
Student photosStale-while-revalidate or cache-first depending on freshness needsImmediate identification is more important than always fetching the latest image.
Sync metadataNetwork-aware with local fallbackMust not block offline operation.

A service worker shall manage the cache lifecycle, update notifications, and offline request handling.

7. Offline and Sync Design

  1. User actions are committed locally first.
  2. Each action is recorded as an auditable event with actor, role, timestamp, and sync state.
  3. When connectivity returns, the sync engine submits queued events to the backend.
  4. The backend evaluates events in order and applies conflict rules.
  5. If an admin action conflicts with a lower-privilege action, the admin action wins and both events remain in history.
  6. Failed sync items remain queued and visible until resolved.

8. Authentication and Session Design

The application should support short-lived sessions, with a recommended baseline of 24 hours unless product policy changes.

Design behavior:

  • Protected actions require a valid session.
  • If the session expires while the user is offline, the user can continue viewing local state only if allowed by policy, but they cannot submit protected actions until they re-authenticate.
  • Unsynced actions must not be discarded solely because a session expired.
  • The session layer should be built so the exact identity provider can be swapped later without changing the app shell.

9. Security Design

ControlDesign Choice
Least privilegeRole checks must be enforced in both UI and backend.
Data minimizationOnly required student and attendance data should be stored locally.
EncryptionLocal sensitive data should be encrypted using browser-available cryptography.
AuditabilityEvery create, update, delete, and attendance mutation should produce an event.
Deletion justificationStudent deletion must require a justification field and retain the historical record.
Protected syncSync endpoints should reject unauthorized or expired actions.

10. Accessibility and UX Design

The interface should be designed for non-technical users who need immediate confirmation.

Behavioral requirements:

  • Button states, confirmations, and errors must be visually clear.
  • Success and failure should be communicated with both visual and auditory feedback where supported.
  • Search and attendance actions should minimize required taps or clicks.
  • The layout must remain usable on mobile and desktop browsers.
  • The component system must expose semantics compatible with WCAG AA.

11. Localization Design

Brazilian Portuguese is the default locale. All user-facing text should be externalized into locale resources so additional languages can be added without redesigning screens.

Implementation rules:

  • No text should be hard-coded into feature logic.
  • Locale selection should be separated from business rules.
  • New languages should be added through resource files rather than duplicated components.

12. Module Boundaries

ModuleContents
AttendanceMark present/absent and track attendance state.
StudentsSearch, add, edit, delete, and photo handling.
ReportsRead-only reporting views.
SyncQueueing, upload, retry, and conflict resolution.
AuthLogin state, session expiry, and protected route handling.
UsersAdmin-only user management, guard rules, and user lifecycle audit.
Shared UIButtons, dialogs, feedback, and accessibility primitives.
OnboardingPublic registration form (React + Tailwind, no MUI). Post-MVP deployed to separate subdomain (onboard.neemias.app). See ADR-0025.
LocalizationLanguage resources and formatting.

13. Open Architectural Decisions

These decisions remain open so contributors can implement them without being boxed into a premature vendor choice:

AreaOpen Constraint
Hosting providerMust support cloud deployment and HTTPS, but no provider is mandated.
Identity providerMust satisfy offline-aware session behavior, but the exact provider remains open.
Encryption libraryMust rely on web-standard crypto APIs or equivalent browser-compatible primitives.
Local data engineMust support offline operation as a fallback when the backend is unreachable.
Reporting engineMust remain modular so reports can evolve independently.

14. Implementation Notes

The first implementation should prioritize:

  • App shell and routing.
  • Local storage and event history.
  • Student search with immediate photo rendering.
  • Attendance capture with offline queueing.
  • Sync and conflict resolution.
  • Role enforcement and session gating.
  • Accessibility and localization primitives.

15. Deployment Architecture — Subdomains

15.1 Application subdomains

SubdomainPurposePlatformSource directory
app.neemias.appMain SPA (dashboard, students, attendance, reports)Cloudflare Pagesapp/ (vite.config.ts)
api.neemias.appBackend API (Workers + D1)Cloudflare Workersworkers/
onboard.neemias.appPublic onboarding form (post-MVP)Cloudflare Pagesapp/ (vite.onboarding.config.ts)
docs.neemias.appDocumentation portalCloudflare Pagesdocs/ (VitePress)

15.2 Onboarding subdomain (onboard.neemias.app)

Current state (MVP): The onboarding form (/onboarding/:hash) is served by the main SPA via React Router. This is intentional — minimizes complexity during initial development.

Post-MVP (SDD-147, Q2): The onboarding form moves to a separate subdomain with its own Vite build and Cloudflare Pages project.

Architecture:

onboard.neemias.app  ──▶  Cloudflare Pages (@neemias-app)

        └── fetch("/api/v1/onboarding/:hash/*")  ──▶  api.neemias.app (Worker)

The onboarding subdomain does not introduce new backend logic — it consumes existing public API routes at api.neemias.app. Authentication is OTP-based (no session required), so no CORS or auth middleware changes are needed.

Build strategy: Two Vite configs in the same app/ directory — main SPA uses vite.config.ts, onboarding uses vite.onboarding.config.ts. Both share the same package.json and node_modules. Vite tree-shakes each build independently, so the onboarding bundle does not include MUI, recharts, or other dashboard dependencies.

Rationale: Documented in ADR-0025. The two-config approach avoids the overhead of a separate pnpm workspace package at current project scale while preserving a clear upgrade path to full separation if needed.

16. PWA Caching and Update Strategy

The app uses vite-plugin-pwa with Workbox for service worker generation. The caching strategy balances instant load times with safe deploys:

CacheStrategyRationale
App shell (HTML, JS, CSS)CacheFirst with versioned nameInstant loads; each deploy creates a new cache namespace so old caches become unreachable
Fonts (external)StaleWhileRevalidate with versioned nameFonts rarely change; background update ensures freshness over time
Precache manifestprecacheAndRouteCritical assets are preloaded on SW install

Version tag: Date.now().toString(36) at build time is injected via Vite define as __APP_VERSION__. This value suffixes every runtime cache name (e.g., app-shell-mpxaxob6). On each deploy the suffix changes, making old caches orphaned. cleanupOutdatedCaches() removes orphaned precache entries.

Update flow: The SW uses registerType: "autoUpdate" — when a new version is detected, it installs in the background, calls skipWaiting(), claims clients, and the page reloads via controllerchange. Users can also manually trigger an update check from Settings → About → "Verificar atualização", which calls registration.update() and reloads if a waiting worker is found.

IndexedDB (Dexie): Not affected by SW cache purging. Schema migrations are handled by Dexie's version().stores() chain. The sync queue (syncQueue table) retries pending entries with exponential backoff; permanently failed entries (FAILED state) are surfaced in Settings and can be cleared manually.

Distribuído sob licença MIT.