docs: add architect decisions addendum after WS-1

This commit is contained in:
2026-04-24 13:49:05 +02:00
parent 4f47e054b9
commit ad941cc944

View File

@@ -0,0 +1,258 @@
---
title: ARCH Consolidation — Architect Decisions Addendum (2026-04-24)
description: Beslissingen op de 6 architect-vragen uit het WS-1 rapport
status: binding
owner: architect
created: 2026-04-24
supersedes-sections-of: /dev-docs/ARCH-CONSOLIDATION-2026-04.md §5 (scope inschattingen)
---
# ARCH Consolidation — Architect Decisions Addendum (2026-04-24)
## Context
Op 2026-04-24 leverde WS-1 (opsporings-pas) een rapport met 45 bevindingen en 6 architect-vragen. Dit addendum legt de beantwoording van die vragen vast én corrigeert waar nodig de werkstroom-scope. Dit document is **bindend** voor WS-2 t/m WS-8.
Bronnen:
- `/dev-docs/ARCH-CONSOLIDATION-2026-04.md` — sprint charter (§1 principes, §3 vastgelegde besluiten, §5 werkstromen)
- `/dev-docs/ARCH-CONSOLIDATION-WS1-REPORT.md` — opsporings-rapport (45 bevindingen)
Het charter blijft ongewijzigd. §5 inschattingen worden in dit addendum herzien. §3 vastgestelde besluiten worden bevestigd (niet gewijzigd) — de architect-precisering op besluit 5 die tijdens review was overwogen is verworpen: besluit 5 blijft exact zoals geformuleerd.
## Samenvatting beslissingen
| Q | Onderwerp | Besluit |
|---|-----------|---------|
| Q1 | ULID voor 11 non-ULID tabellen | **Alle 11 migreren naar ULID.** Charter §3 besluit 5 blijft exact zoals geformuleerd; geen exceptions. |
| Q2 | OrganisationScope strategie | **FK-chain scope extension.** Denormalisatie alleen op tabellen die rapportage-hot zijn. Charter §3 besluit 3 bevestigd als enige uitzondering op basis van meetbaar rapportage-gebruik. |
| Q3 | WS-5 scope | **Vier committed splits op `form_fields` + drie library-mirror splits.** `role_restrictions` blijft JSON. |
| Q4 | Sanctum `personal_access_tokens` morph | **Ongewijzigd laten.** Framework-polymorfie valt buiten de domain morph-map. Documenteren in WS-8. |
| Q5 | SCHEMA.md rewrite-shape | **Aparte `/dev-docs/ARCH-PLANNED-MODULES.md`** voor tabellen zonder migratie. SCHEMA.md wordt strict "waarheid van wat bestaat in de database". |
| Q6 | `subject_type` allow-list | **Consolideren onder WS-2 purpose registry.** Compile-time test dwingt morph-map-alignment met purpose-registry af. |
---
## Q1 — ULID voor 11 non-ULID tabellen
**Besluit:** Alle 11 tabellen uit Categorie A van het WS-1 rapport worden naar ULID gemigreerd. Charter §3 besluit 5 ("ULID consistent overal, ook pivots, ook logging-tabellen") blijft ongewijzigd. Geen exceptions.
**Tabellen:**
| ID | Tabel | Categorie |
|----|-------|-----------|
| A-01 | `organisation_user` | Pure pivot, geen model |
| A-02 | `event_user_roles` | Pure pivot, geen model |
| A-03 | `crowd_list_persons` | Pure pivot, geen model |
| A-04 | `event_person_activations` | Pure pivot, geen model |
| A-05 | `user_organisation_tags` | Pivot met model |
| A-06 | `person_section_preferences` | Pivot met model |
| A-07 | `mfa_backup_codes` | Ephemeral data |
| A-08 | `mfa_email_codes` | Ephemeral data |
| A-09 | `form_submission_section_statuses` | Pivot met model |
| A-10 | `form_values` | EAV hot path |
| A-11 | `form_value_options` | EAV hot path |
**Motivatie:**
- **Pre-launch window:** geen data-migratie, geen downtime, geen backfill-pijn. Dit is het enige moment waarop consistentie gratis is. Een half jaar later met productie-data kost hetzelfde werk weekenden met downtime.
- **Performance-argumenten zijn theoretisch op Crewli's schaal:** EAV join-performance tussen int en ULID keys is verwaarloosbaar bij de volumes waar Crewli zich op richt (enkele miljoenen `form_values` per festival-jaar, ruim binnen MySQL + goede indexen op ULID).
- **Consistentie-winst is concreet en blijvend:** elke uitzondering introduceert cognitieve belasting ("waarom is deze tabel anders?") die besluit 5 juist wil voorkomen.
- **Valstrik 2 (gold-plating) niet van toepassing:** dit is geen elegantie-verbetering, dit is uitvoering van een reeds genomen besluit.
**Scope-impact:** WS-4 migreert 11 PK's in plaats van 0 of 2.
**Migratie-notes voor WS-4:**
- Voor tabellen met een model (`UserOrganisationTag`, `PersonSectionPreference`, `MfaBackupCode`, `MfaEmailCode`, `FormSubmissionSectionStatus`, `FormValue`, `FormValueOption`): `HasUlids` trait toevoegen.
- Voor pure pivots zonder model (`organisation_user`, `event_user_roles`, `crowd_list_persons`, `event_person_activations`): `$table->ulid('id')->primary()` in de migratie, geen model nodig.
- Inline migratie-commentaar "int AI for join performance" wordt vervangen door de ULID-migratie zelf; geen extra rationale-documentatie in de migratie.
---
## Q2 — OrganisationScope strategie
**Besluit:** `OrganisationScope` wordt uitgebreid met een declaratieve FK-chain strategy. Denormalisatie van `organisation_id` blijft beperkt tot tabellen die **meetbaar rapportage-hot** zijn.
**Concreet:**
- `form_submissions` behoudt denormalized `organisation_id` (per charter §3 besluit 3 — bevestigd als bewuste exception voor rapportage-queries: dashboards, CSV-exports, aggregerende counts over duizenden rijen).
- Andere form-builder child tables krijgen **geen eigen `organisation_id` kolom**; zij gebruiken FK-chain via hun parent:
- `form_schema_sections` → via `form_schemas.organisation_id`
- `form_fields` → via `form_schemas.organisation_id`
- `form_values` → via `form_submissions.organisation_id` (denormalized parent)
- `form_value_options` → via `form_submissions.organisation_id`
- `form_submission_section_statuses` → via `form_submissions.organisation_id`
- `form_submission_delegations` → via `form_submissions.organisation_id`
- `form_schema_webhooks` → via `form_schemas.organisation_id`
- `form_webhook_deliveries` → via `form_submissions.organisation_id`
**Shape van de scope-extensie (concept — implementatie-details in WS-4):**
```php
class FormField extends Model
{
use HasUlids;
protected static function tenantScopeStrategy(): array
{
return ['via' => FormSchema::class, 'fk' => 'form_schema_id'];
}
}
```
`OrganisationScope` leest de strategy en bouwt de JOIN automatisch op.
**Motivatie:**
- **Normalisatie-zuiverheid:** `organisation_id` heeft één bron van waarheid (`form_schemas`, `events`, enz.). Denormalisatie introduceert synchronisatie-risico en dubbele boekhouding.
- **Single responsibility:** `OrganisationScope` blijft *de* multi-tenant guard. Geen gedeelde verantwoordelijkheid tussen observer + scope over 8 tabellen.
- **Toekomstbestendig:** hetzelfde patroon werkt straks automatisch voor accreditation, briefings en andere modules met sub-tabellen. Eén scope-klasse uitbreiding, geen migraties per module.
- **Denormalisatie-uitzondering gerechtvaardigd:** `form_submissions` is rapportage-hot. De denormalisatie is een meetbare performance-optimalisatie voor aggregerende queries, geen "veilig-voor-het-geval-dat" patroon.
**Rapportage-hotness criterium:** een tabel krijgt alleen denormalized `organisation_id` als er regelmatig aggregerende queries rechtstreeks op draaien (counts, group-by, exports over duizenden rijen). Alle andere tabellen gebruiken FK-chain via hun parent.
**Scope-impact:** WS-4 krijgt één scope-klasse uitbreiding + scope-registratie op 9 form-builder child models + 5 event-data models (uit WS-1 rapport D-04: `ShiftAssignment`, `ShiftWaitlist`, `VolunteerAvailability`, `PersonSectionPreference`, `PersonIdentityMatch`). Geen 8 extra kolom-migraties.
---
## Q3 — WS-5 scope
**Besluit:** WS-5 splitst de vier committed kolommen op `form_fields` (per charter §3 besluit 6) én de drie library-mirrors op `form_field_library`. `role_restrictions` blijft JSON.
**Te splitsen:**
| Bron kolom | Doel tabel | Sub-WS |
|------------|-----------|--------|
| `form_fields.binding` | `form_field_bindings` | WS-5a |
| `form_fields.validation_rules` | `form_field_validation_rules` | WS-5b |
| `form_fields.conditional_logic` | `form_field_conditional_logic` | WS-5c |
| `form_fields.options` | `form_field_options` | WS-5d |
| `form_field_library.default_binding` | `form_field_bindings` (met owner discriminator) | WS-5a |
| `form_field_library.validation_rules` | `form_field_validation_rules` (met owner discriminator) | WS-5b |
| `form_field_library.options` | `form_field_options` (met owner discriminator) | WS-5d |
**Blijft JSON:**
- `form_fields.role_restrictions` — kleine set Spatie rol-strings. Geen FK-partner mogelijk (rollen zijn strings in Spatie-permission, geen aparte tabel-PK's). Niet queryable in practice. Relationele splitsing levert geen architectuur-winst.
- `form_fields.translations`, `form_field_library.translations` — flat key-value bags per locale, nooit queried. Splitsen is speculatief.
**Motivatie:**
- **Consistent relationeel form-builder domein:** library en fields delen dezelfde onderliggende tabellen voor options, bindings en validation rules. Twee stijlen in hetzelfde domein is inconsistent.
- **Library-entries krijgen een discriminator** (exacte shape in WS-5a — ofwel `owner_type` enum (`field|library`), ofwel een paar nullable FK's (`form_field_id` OR `form_field_library_id`)). Discriminator-keuze wordt in WS-5a besloten op basis van query-patterns.
- **`FormFieldService::insertFromLibrary` kopieert rijen** tussen library-entries en field-entries in plaats van JSON te hydrateren. Natuurlijk werk binnen WS-5; geen aparte PR.
**Scope-impact:** WS-5 wordt ~7-8 dagen in plaats van 4-6. Drie extra sub-werkstromen voor de library-mirrors passen binnen de bestaande WS-5a/b/d PR-structuur (library valt onder dezelfde PR als de corresponderende field-split).
---
## Q4 — Sanctum `personal_access_tokens`
**Besluit:** De Sanctum-default voor `tokenable_type / tokenable_id` (FQCN in DB, geen morph-map entry) blijft ongewijzigd.
**Motivatie:**
- Framework-polymorfie valt buiten de domain morph-map conventie. Een alias toevoegen introduceert onderhoudsschuld bij elke Sanctum-upgrade.
- Domain polymorfie (`form_schemas.owner_type`, `form_submissions.subject_type`) blijft strict in morph-map geregistreerd; dit besluit raakt daar niet aan.
**Documentatie-actie (WS-8):** in de ARCH-documentatie wordt een expliciete regel opgenomen:
> De `enforceMorphMap`-conventie geldt voor domain polymorfe relaties. Framework-relaties (Sanctum `tokenable_type`, Spatie `activitylog.subject_type`, Spatie `activitylog.causer_type`) volgen hun framework-defaults en zijn expliciet uitgezonderd van de morph-map conventie.
---
## Q5 — SCHEMA.md rewrite-shape
**Besluit:** WS-8 extraheert alle tabellen-zonder-migratie uit SCHEMA.md naar een apart document `/dev-docs/ARCH-PLANNED-MODULES.md`. SCHEMA.md wordt strict "waarheid van wat bestaat in de database".
**Te verplaatsen tabellen** (uit WS-1 rapport bevinding D-01):
- §3.5.4 Volunteer Profile & History: `volunteer_festival_history`, `post_festival_evaluations`, `festival_retrospectives`
- §3.5.6 Accreditation Engine: `accreditation_categories`, `accreditation_items`, `event_accreditation_items`, `accreditation_assignments`, `access_zones`, `access_zone_days`, `person_access_zones`
- §3.5.7 Artists & Advancing: `performances`, `stages`, `stage_days`, `advance_submissions`, `artist_contacts`, `artist_riders`, `itinerary_items`
- §3.5.8 Communication & Briefings: `briefing_templates`, `briefings`, `briefing_sends`, `communication_campaigns`, `messages`, `message_replies`, `broadcast_messages`, `broadcast_message_targets`
- §3.5.9 Check-In & Operational: `check_ins`, `show_day_absence_alerts`, `scanners`, `inventory_items`, `event_info_blocks`, `event_info_block_crowd_types`, `production_requests`, `material_requests`
**Onderhoud-pattern:**
- Bij elke PR die een planned-module tabel aanmaakt verhuist de betreffende sectie van `ARCH-PLANNED-MODULES.md` naar `SCHEMA.md`. Dit wordt onderdeel van de PR-template checklist.
- `ARCH-PLANNED-MODULES.md` behoudt de index/soft-delete/FK-intent zoals vastgelegd in de originele SCHEMA.md rijen, zodat de planning-informatie niet verloren gaat.
- SCHEMA.md Rule 4 (required indexes) verwijst alleen nog naar bestaande tabellen na de rewrite.
**Motivatie:**
- **Enterprise documentatie-discipline:** onboarding developers zien direct wat er in de DB staat, zonder ruis van planning.
- **SCHEMA.md Rule 4 consistent:** required indexes kunnen niet meer verwijzen naar fantoom-tabellen.
- **Planned-modules eigen evolutie:** kan sneller bijgewerkt worden zonder SCHEMA-ruis.
**Scope-impact:** WS-8 wordt ~4-5 dagen in plaats van 2-3.
---
## Q6 — `subject_type` allow-list
**Besluit:** De allow-list uit `config/form_subjects.php` wordt opgeheven en geconsolideerd onder de purpose-registry in WS-2. `PurposeDefinition` heeft per charter §3 besluit 4 al een `subject_type` veld; dat wordt de enige bron van waarheid.
**Concreet (uitvoering in WS-2):**
- `config/form_subjects.php` verdwijnt na WS-2.
- `StoreFormSubmissionRequest` leidt toegestane `subject_type` waarden af uit `PurposeRegistry::allSubjectTypes()`.
- `AppServiceProvider::boot` bouwt de morph-map deels vanuit de purpose-registry (per purpose een subject-type entry) + framework-entries (activity-log subjects/causers, Sanctum uitgezonderd).
- **Compile-time guard:** een unit-test faalt als een `subject_type` in `PurposeRegistry` niet in de morph-map staat, of vice versa. Dit vervangt de huidige "developer discipline" afspraak.
**Motivatie:**
- **Eén bron van waarheid** voor subject-semantiek. Geen twee-bestanden-sync met impliciete koppeling.
- **Verstevigt charter §3 besluit 4:** `PurposeDefinition` wordt écht de volledige purpose-specificatie.
**Scope-impact:** WS-2 krijgt ~halve dag extra werk. De migratie zelf is klein; het compile-time consistency testje is het echte werk.
---
## Herziene werkstroom-scope en inschatting
Charter §5 inschattingen zijn op basis van dit addendum herzien:
| WS | Onderwerp | Charter inschatting | Herzien |
|----|-----------|---------------------|---------|
| WS-1 | Opsporings-pas | 1 dag | 1 dag (afgerond 2026-04-24) |
| WS-2 | Purpose registry | 2-3 dagen | 2-3 dagen + Q6 consolidatie (~halve dag) |
| WS-3 | Één SPA consolidatie | 3-5 dagen | 3-5 dagen (ongewijzigd) |
| WS-4 | ULID + denormalized submission columns + scope | 2-3 dagen | **5-6 dagen** (Q1 elf ULID migraties + Q2 scope extension + scope-registratie op 14 models + D-05 Person SoftDeletes verify) |
| WS-5 | JSON-kolom-opsplitsing | 4-6 dagen | **7-8 dagen** (Q3 library-mirrors toegevoegd aan WS-5a/b/d) |
| WS-6 | FormBindingApplicator | 4-5 dagen | 4-5 dagen (ongewijzigd) |
| WS-7 | Observability foundation | 2-3 dagen | 2-3 dagen + D-06 activity_log indexes |
| WS-8 | Documentatie-consolidatie | 2-3 dagen | **4-5 dagen** (Q4 framework-exception + Q5 planned-modules extractie + PK-decisions doc) |
**Totale herziene inschatting:** 28-38 dagen werk (charter had 22-32 dagen). Toename van ~6 dagen, volledig verklaard door de strict-enterprise invulling van Q1, Q2 en Q3.
**Werkstroom-volgorde ongewijzigd:** WS-1 → WS-2 → WS-3 (parallel mogelijk met WS-4/5) → WS-4 → WS-5 → WS-6 → WS-7 → WS-8.
---
## Openstaande bevindingen zonder architect-beslissing
WS-1 rapport Categorie D bevindingen die geen architect-beslissing vereisten en in de relevante werkstromen worden meegenomen:
| ID | Bevinding | Werkstroom |
|----|-----------|-----------|
| D-03 | Form-builder child models registreren OrganisationScope niet | WS-4 (als onderdeel van Q2 uitvoering) |
| D-04 (event-data subset) | ShiftAssignment / ShiftWaitlist / VolunteerAvailability / PersonSectionPreference / PersonIdentityMatch zonder scope | WS-4 |
| D-04 (user/admin subset) | MFA / TrustedDevice / UserProfile / EmailLog / OrganisationEmailSettings etc. | backlog, per-model ticket |
| D-05 | Person SoftDeletes verificatie | WS-4 |
| D-06 | activity_log `(subject_type, subject_id)` en `(causer_type, causer_id)` indexes | WS-7 |
| D-07 | Morph map forward-looking entries documentatie-link | WS-8 |
---
## Verwijzingen
- `/dev-docs/ARCH-CONSOLIDATION-2026-04.md` — sprint charter (§1 principes, §3 vastgestelde besluiten blijven ongewijzigd; §5 inschattingen herzien in dit addendum).
- `/dev-docs/ARCH-CONSOLIDATION-WS1-REPORT.md` — bron van alle 45 bevindingen waarnaar dit addendum verwijst.
---
## Sign-off
- **Architect review:** akkoord per Claude Chat sessie 2026-04-24, iteratief verscherpt over drie rondes (initial → strict-enterprise op Q1/Q3 → FK-chain correctie op Q2).
- **Product owner:** akkoord per Bert Hausmans 2026-04-24.
Volgende stap: prompt opstellen voor WS-2 (Purpose registry) met Q6-consolidatie als integraal onderdeel van de werkstroom.