Platform3 · Canonical architecture

Ed Fi 1edtech Architecture

This page is rendered directly from the committed architecture decisions. Each stable section anchor is part of the public traceability contract.

Source-bound decision record

20 committed decisions · ed_fi/1edtech

#overview

edfi served audiences

activeOwner: platform3-loopDate: 2026-06-04

Decision

district and SIS integrators building attendance, discipline, transcript, program, demographic, staff, calendar, and related administrative workflows against the complete Ed-Fi UDM on the TimeBack 1EdTech surface

Alternatives and tradeoffs

  • teacher or parent plain-language report builder
  • Alpha attendance/program/transcript report generator
  • standalone Ed-Fi ODS operator
  • one-time CSV-only migration script

Rationale and consequences

the Ed-Fi 1EdTech surface is the standards-shaped administrative-record base that later Alpha views and skill packs build on

#eitd-000-source-contract

edfi source contract

activeOwner: platform3-loopDate: 2026-06-04

Decision

Use Ed-Fi Data Standard for Suite 3 v6.1.0 as the source contract. The generated data dictionary passes through Ed-Fi UDM entities, references, descriptors, field meanings, and domain guidance by linking to Ed-Fi's Data Handbook, v6 model reference, UML diagrams, default descriptors, and API guidelines. Platform3 documents only the behavior Ed-Fi does not decide for this hosted multi-tenant platform.

Alternatives and tradeoffs

  • Copy every Ed-Fi field definition into platform3 docs
  • Use only sample API payloads or prior ODS tables

Rationale and consequences

The simplest durable architecture is to let Ed-Fi own Ed-Fi semantics and let platform3 own only the missing platform behavior: identity boundary, local IDs, deletion retention, descriptors, draft state, and HTTP/API contracts.

#eitd-001-complete-model-scope

edfi complete model scope

activeOwner: platform3-loopDate: 2026-06-04

Decision

Implement the complete Ed-Fi UDM model on the 1EdTech surface: every domain, entity, association, reference, and descriptor published for Data Standard v6.1. No admission list and no Alpha-demand filter ship here. Unused Ed-Fi tables remain available and harmless. Cutting unused fields, renaming CEDS/Ed-Fi terms, and adding Alpha-only status fields happens only in the later Alpha surface.

Alternatives and tradeoffs

  • Implement only the domains Alpha already needs
  • Implement only high-volume SIS domains

Rationale and consequences

Complete base coverage is simpler than a defended subset because it makes the 1EdTech surface a stable source and pushes audience-specific cuts to the surface designed to make them.

#eitd-002-gap-a1-roster-reference-boundary

edfi roster reference boundary

activeOwner: platform3-loopDate: 2026-06-04

Decision

Every Ed-Fi-module row that touches a roster person, organization, class, course, section, or enrollment anchor must reference platform3 OneRoster sourcedIds through foreign keys. The Ed-Fi module may store Ed-Fi administrative attributes and Ed-Fi external identification codes, but it may not mint a local roster fact. A write that references an unknown OneRoster identity fails with a typed edfi:platform3_roster_required Problem.

Alternatives and tradeoffs

  • Let Ed-Fi create its own student, staff, school, and section records
  • Copy OneRoster identity fields into Ed-Fi tables

Rationale and consequences

A foreign-key boundary is simpler than reconciliation logic. It turns identity alignment into a write-time invariant instead of a recurring report-cleanup problem.

#eitd-003-gap-a2-platform-minted-local-id

edfi platform minted local id

activeOwner: platform3-loopDate: 2026-06-04

Decision

Every Ed-Fi persisted row receives a platform-minted local identifier, edfi_local_id, that is never a OneRoster sourcedId and never an Ed-Fi natural key. OneRoster sourcedIds remain explicit FK fields. Ed-Fi natural keys and external identification codes remain pass-through Ed-Fi fields. The API exposes all three only where the caller needs them and labels them by provenance.

Alternatives and tradeoffs

  • Use the OneRoster sourcedId as the Ed-Fi row id
  • Use Ed-Fi natural keys as local row ids

Rationale and consequences

A separate local id is the smallest way to keep platform identity, roster identity, and Ed-Fi source identity distinct without teaching consumers precedence rules.

#eitd-004-gap-a3-soft-delete

edfi soft delete retention

activeOwner: platform3-loopDate: 2026-06-04

Decision

Ed-Fi deletes and corrections are represented as soft-deleted records with deletion metadata. Public deletes mark rows deleted, preserve original values, write audit entries, and keep rows available through includeDeleted=true or audit/detail routes authorized for compliance. No public operation hard-deletes Ed-Fi records.

Alternatives and tradeoffs

  • Hard-delete rows on DELETE or missing import row
  • Keep only audit text after deletion

Rationale and consequences

Soft deletion is the simplest correction model that satisfies audit needs and mirrors the NWEAMap retention rule already approved upstream.

#eitd-005-gap-a4-descriptors-as-governed-tags

edfi descriptors governed tags

activeOwner: platform3-loopDate: 2026-06-04

Decision

Every Ed-Fi descriptor is represented in the platform governed tag registry as a code-list definition with namespace, codeValue, shortDescription, description, effective dates, and standard_status. Descriptor use on Ed-Fi objects references the governed code entry. Ed-Fi standard descriptor values are seeded from Ed-Fi's default descriptor source; district-local values are accepted only through governed registry writes, never as free text metadata.

Alternatives and tradeoffs

  • Store descriptor strings directly on each Ed-Fi row
  • Create a bespoke descriptor table family separate from tags

Rationale and consequences

Descriptors are governed code lists. The governed tag registry already exists to hold open-but-controlled values, so using it prevents a second code-list subsystem.

#eitd-006-gap-a5-draft-precanonical-state

edfi draft precanonical state

activeOwner: platform3-loopDate: 2026-06-04

Decision

An Ed-Fi record entry can exist as a draft before it becomes canonical. Draft records never carry a platform3 acknowledgement id, never count as canonical, never appear in ordinary canonical list endpoints, and never satisfy downstream references. A record becomes canonical only after platform3 validation succeeds and the platform returns ack_id plus ETag.

Alternatives and tradeoffs

  • No drafts; reject until every field validates
  • Store invalid records in canonical tables with a status flag

Rationale and consequences

Separating draft from canonical state removes ambiguity at the read boundary while avoiding app-local patch tables for partially entered administrative records.

#eitd-101-axis-write-granularity

edfi api axis write granularity

activeOwner: platform3-loopDate: 2026-06-04

Decision

SHIP both per-resource writes for UDM resources/descriptors/drafts and bulk import/export jobs for Ed-Fi bulk/XML or equivalent complete-model exchange. The two paths share validation, idempotency, roster-reference checks, descriptor governance, soft-delete behavior, and canonical acknowledgement. For teacher attendance marking specifically, the canonical between-bells path is append-only POST to StudentSchoolAttendanceEvent or StudentSectionAttendanceEvent; detail PUT/PATCH/DELETE are correction paths after an acknowledged row exists.

Alternatives and tradeoffs

  • bulk-only
  • per-resource only

Rationale and consequences

This removes app-local staging and migration logic while avoiding two semantic persistence models: both write paths end in the same canonical records. Attendance marks are events, so treating normal teacher marks as append-only avoids forcing a classroom app through overwrite semantics during the narrow between-bells window.

#eitd-102-axis-read-shape

edfi api axis read shape

activeOwner: platform3-loopDate: 2026-06-04

Decision

SHIP list plus detail plus narrow reference sub-collections. Every UDM collection and descriptor has list and detail reads. Narrow sub-collections are allowed only where the UDM reference already defines the parent-child relationship, such as a student's attendance events, a school's bell schedules, or a staff member's assignments. Unrestricted graph expansion is deferred.

Alternatives and tradeoffs

  • list-only
  • list plus detail only

Rationale and consequences

UDM-bounded sub-collections remove repeated client-side joins without inventing arbitrary projections.

#eitd-103-axis-query-model

edfi api axis query model

activeOwner: platform3-loopDate: 2026-06-04

Decision

SHIP documented filters, stable sort keys, Ed-Fi offset+limit paging, platform opaque cursor paging, and modifiedSince for canonical collections and append-like operational collections. Offset mode accepts limit plus a non-negative offset and returns page.offset, page.nextOffset when another offset page exists, links.next, totalCount, page.totalCount, and the HTTP Total-Count header. Cursor mode accepts limit plus cursor and returns page.nextCursor when another cursor page exists. A request that mixes cursor with offset is invalid and returns edfi:invalid_query_parameter; unsupported query parameters are also typed 400 Problems rather than being ignored.

Alternatives and tradeoffs

  • none
  • filter and sort only
  • opaque cursor as the only paging mode

Rationale and consequences

Two named paging modes are simpler than pretending Ed-Fi offset does not exist or forcing every high-volume client through offset. The contract is explicit: offset+limit for Ed-Fi compatibility, cursor for platform-owned high-volume replay, and mixed cursor+offset rejected at the boundary.

#eitd-104-axis-concurrency-model

edfi api axis concurrency model

activeOwner: platform3-loopDate: 2026-06-04

Decision

SHIP per-resource optimistic concurrency. Canonical detail reads return ETag. PUT, PATCH, DELETE, draft promotion, and descriptor updates that can overwrite customer-visible work require If-Match. Missing required validators return 428; stale validators return 412 or the documented conflict Problem. Bulk jobs use job idempotency and per-row conflict reporting instead of row-level If-Match headers. The attendance teacher-mark contract publishes a stable client cap of 8 concurrent POSTs for a 32-student daily plus section roster batch and confirms each row with ack_id plus ETag. Deployed Postgres/Vercel storage must preserve serverless-safe pool sizing, keep Supabase transaction-pooler URLs in transaction mode instead of rewriting them to session mode, and serialize schema startup so the published cap is production-verifiable.

Alternatives and tradeoffs

  • none
  • bulk-only concurrency

Rationale and consequences

ETag plus If-Match is the smallest interoperable protection against lost updates and does not duplicate idempotency. Publishing the attendance cap removes guesswork for SIS clients while keeping the server-owned acknowledgement and idempotency semantics as the source of truth. The transaction-pooler, pool-size, and migration startup guardrails make that cap an operational contract rather than a local-only benchmark.

#eitd-105-axis-idempotency-model

edfi api axis idempotency model

activeOwner: platform3-loopDate: 2026-06-04

Decision

SHIP Idempotency-Key on retryable writes, persisted through platform.idempotency_key with tenant, module, surface, method, route template, operation id, request hash, response replay metadata, and canonical record pointers. Bulk imports also compute a package/source hash for import evidence, but that hash does not replace Idempotency-Key on command retry.

Alternatives and tradeoffs

  • none
  • natural unique constraints only

Rationale and consequences

One retry ledger shared with platform prevents every Ed-Fi handler from inventing duplicate detection.

#eitd-106-axis-auth-shape

edfi api axis auth shape

activeOwner: platform3-loopDate: 2026-06-04

Decision

SHIP platform HS256 Bearer JWTs with tenant_id or tenantId, roles, scopes, and OneRoster relationship-scoped claims where visibility must narrow by student, staff, school, or class. The Ed-Fi module does not mint independent Ed-Fi identities. Public demo minting is restricted to tenantId=demo; real-tenant tokens are operator-minted.

Alternatives and tradeoffs

  • role-only JWT
  • separate Ed-Fi auth system

Rationale and consequences

The platform token already carries tenant and operation authority; adding relationship-scoped visibility keeps authorization tied to the single roster source.

#eitd-107-axis-eventing-model

edfi api axis eventing model

activeOwner: platform3-loopDate: 2026-06-04

Decision

SHIP poll plus modifiedSince for change discovery. Webhooks, event streams, and push subscriptions are deferred. modifiedSince applies to canonical collections, descriptors, drafts, import/export jobs, and audit-like operational collections where timestamp semantics are documented.

Alternatives and tradeoffs

  • poll-only
  • webhooks now

Rationale and consequences

modifiedSince removes full-poll complexity while avoiding a duplicate delivery primitive.

#eitd-108-axis-error-envelope

edfi api axis error envelope

activeOwner: platform3-loopDate: 2026-06-04

Decision

SHIP typed RFC 7807 Problem Details with stable type URIs, stable edfi:* codes, requestId, traceId, status, title, detail, and optional fieldErrors. Problem details must redact PII, secrets, source file bytes, headers, user agents, and raw request bodies.

Alternatives and tradeoffs

  • no common envelope
  • plain RFC 7807 with free-text type

Rationale and consequences

Typed Problems keep the standard shape and add the stable fields integrators and support need.

#eitd-109-axis-tenant-routing

edfi api axis tenant routing

activeOwner: platform3-loopDate: 2026-06-04

Decision

SHIP tenant routing by the signed JWT tenant claim for ordinary Ed-Fi resource routes. The surface does not require /tenants/{id} prefixes or tenant headers on Ed-Fi-shaped collection paths. Public demo token minting accepts tenantId=demo only. Real tenant tokens are minted out of band by operators or the driver.

Alternatives and tradeoffs

  • tenant in URL
  • tenant in header

Rationale and consequences

JWT tenant routing gives one source of tenant truth on Ed-Fi resource routes while preserving the route shape integrators expect.

#eitd-110-axis-conformance-evidence

edfi api axis conformance evidence

activeOwner: platform3-loopDate: 2026-06-04

Decision

SHIP local executable conformance evidence: UDM coverage manifest, generated dictionary coverage checks, descriptor governance checks, OneRoster FK checks, soft-delete checks, draft/canonical-state checks, HTTP contract tests, and live smoke probes. DEFER official Ed-Fi certification or compatibility claims until external evidence exists.

Alternatives and tradeoffs

  • no conformance evidence
  • claim official certification now

Rationale and consequences

Local evidence is the smallest honest trust mechanism; official labels require official proof.

#eitd-111-axis-privacy-retention

edfi api axis privacy retention

activeOwner: platform3-loopDate: 2026-06-04

Decision

SHIP GDPR-style export, redaction, retention, erasure/tombstone handling, and privileged-read audit for Ed-Fi data. Ordinary deletes are soft deletes under EITD-004. Hard erasure is a controlled privacy operation that redacts or tombstones PII while preserving non-PII audit facts required for compliance.

Alternatives and tradeoffs

  • no privacy behavior beyond auth
  • DELETE per route only

Rationale and consequences

Privacy behavior must be an architecture contract because every downstream artifact will otherwise decide it differently.

#eitd-112-axis-list-endpoints

edfi api axis list endpoints

activeOwner: platform3-loopDate: 2026-06-04

Decision

SHIP one documented list endpoint per generated Ed-Fi UDM collection and descriptor catalog, plus list endpoints for drafts, imports, exports, and conformance/evidence where authorized. Every list endpoint inherits the EITD-103 query contract: limit, offset, cursor, modifiedSince, sort, documented filters, page.offset, page.nextOffset, page.nextCursor, links.next, totalCount, page.totalCount, HTTP Total-Count, and edfi:invalid_query_parameter for unsupported, malformed, or mixed cursor+offset requests. Write-and-remember-only resources are not allowed on this surface.

Alternatives and tradeoffs

  • write-and-remember only
  • lists for common domains only

Rationale and consequences

A complete model needs complete discoverability. One list per collection is easier for agents and humans than hidden support-only IDs.