Architecture Decision Record · 0051

Niederlassungs-Footer für Vacancy-Detailseiten

Status: accepted Follow-up zu ADR-0014 customer-lens-reviewer: GO-WITH-CONCERN (2026-06-10)
TL;DR — Unterhalb der bestehenden Vacancy-Detail-Sections (zuletzt Benefits/Gehaltsspanne) erscheinen fünf weitere Sections aus Figma-Frame 62975-23008, die pro Niederlassung statt pro Node gelten. Niederlassung = der required, Single-Cardinality-Taxonomie-Term field_office (Vokabular vacancy_office) — eine saubere 1:1-Beziehung Node → Office-Term. Alle redaktionellen Sections (1, 2, 3, 5) werden als schlichte Felder am vacancy_office-Term abgebildet — ohne neue Module. Section 2 („Arbeiten in …“) ist ein Repeater aus bis zu sechs festen Bild-Text-Card-Feld-Sets (nur befüllte werden gerendert); Section 3 (Adresse + Ansprechpartner:in) und die Sections 1 (Imagevideo) und 5 (kununu-Award + Karriere-Broschüre) sind flache Per-Office-Felder. Das Term-Edit-Formular wird mit field_group (bereits aktiv) in die Section-Gruppen gegliedert. Section 4 („Diese Positionen könnten Dich auch interessieren“) ist dynamisch — ein contextual Views-Block über andere offene Vacancies derselben Recruiting-Kategorie (keine Felder, kein Modul). Gerendert wird nicht über ein Canvas-Content-Template (Canvas ist node-only, verifiziert), sondern durch Erweiterung des bestehenden handkomponierten node--vacancy--full.html.twig — mit explizit gesetztem Cache-Tag für den Office-Term. Das ist der in ADR-0014 ausdrücklich aufgeschobene Out-of-Scope-Block. Bewusst verworfen wurden paragraphs (für Section 2) und block_content (als globale Quelle für 1/5): die Vermeidung zweier neuer Modul-Abhängigkeiten wiegt schwerer als die Repeater-Ergonomie bzw. ein zentraler Global-Default.

Kontext

ADR-0014 fixierte die Topologie der Vacancy-Detailseite (node.vacancy.full) und nannte „zusätzliche Sections“ explizit als Out-of-Scope für den MVP. Dieser aufgeschobene Block kommt jetzt in-Scope: Figma-Frame 62975-23008 zeigt unterhalb der bestehenden Sections (zuletzt Benefits / Gehaltsspanne) fünf weitere Sections, die laut Anforderung pro Niederlassung gepflegt werden — alle Stellenausschreibungen einer Niederlassung sollen denselben Footer-Inhalt ausgeben.

Verifizierte Ausgangslage im Repo:

Section-Inventar & Natur-Klassifikation (Figma-Scan 2026-06-10)

Der entscheidende Befund des Scans: die fünf Sections haben drei verschiedene Naturen. Eine naive „5 identische Feld-Gruppen am Term“-Lösung wäre falsch (Section 4 ist gar kein Redaktionsinhalt) und redundant (Sections 1/5 müssten pro Office dupliziert werden).

#Section (Figma)InhaltNatur
1Über SOLCOMHeading + Imagevideo🌐 global, überschreibbar
2Arbeiten in <Ort>Heading + 6 Bild-Text-Cards (Repeater)📍 pro Niederlassung, redaktionell
3Niederlassung + Ansprechpartner:inAdresse + Google-Maps-Link · Recruiter (Name/Rolle/Tel./Foto)📍 pro Niederlassung, redaktionell
4Diese Positionen könnten Dich auch interessieren4 Job-Cards anderer offener Stellen⚙️ dynamisch
5SOLCOM stellt sich vorkununu-Award + Karriere-Broschüre (PDF)🌐 global, überschreibbar

Entscheidung

Alle redaktionellen Sections (1, 2, 3, 5) werden als schlichte Felder am vacancy_office-Term abgebildet — ohne neue Module —, mit field_group gegliedert; Section 4 bleibt ein dynamischer Views-Block; gerendert wird durch Erweiterung des bestehenden Twig-Templates, nicht über Canvas. Konkret:

  1. Section 2 („Arbeiten in …“) → bis zu sechs feste Card-Feld-Sets am Term. Je Card ein Feld-Paar (Bild als Media-Referenz + Text), field_office_card_1_*field_office_card_6_*. Alle optional; das Twig rendert nur die Cards, die Inhalt tragen (5 befüllt → 5 angezeigt). Bewusst keine Paragraphs — die feste Card-Zahl macht starre Feld-Sets ausreichend und spart das Modul (siehe Rejected Alternatives).
  2. Section 3 (Niederlassung + Ansprechpartner:in) → flache Felder am Term. Adresse (Firma/Straße/PLZ/Ort), Google-Maps-URL als link, Recruiter (Name/Rolle/Telefon/Foto-Media). Die Adresse ist redaktionell, nicht aus Personio (verifiziert: Feed liefert nur den Office-Namen).
  3. Sections 1 (Imagevideo) + 5 (kununu-Award + Karriere-Broschüre) → flache Felder am Term. Section 1: ein Video-Feld (Media bzw. URL). Section 5: Award-Asset + Broschüre (File/Media) + Label. Kein block_content, kein globaler Default, keine Override-Logik — jede Niederlassung pflegt diese Inhalte selbst (bewusst gewählter Trade-off: keine Module gegen Pflege-Redundanz, falls die Inhalte firmenweit identisch sind).
  4. Section 4 („Diese Positionen …“) → bestehendes Views-Display wiederverwendet. Das bereits existierende Display vacancies_listing : block_related erfüllt Section 4 exakt: contextual arg field_recruiting_category_target_id (default_argument_type: taxonomy_tid, node: true) zieht die Kategorie aus der aktuellen Node, ein zweiter arg nid (not: true, default_argument_type: node) schließt die aktuelle Stelle aus; Filter status=1/type=vacancy, block_hide_empty, Rows im card-View-Mode. Kein neuer View, kein Redaktions-Feld, kein Modul. Einziger Tune an Figma-Frame: items_per_page 3→4 und Heading-Copy auf Du-Ansprache („Diese Positionen könnten Dich auch interessieren“) — display-spezifisch, das Display ist nirgends als Layout-Block platziert. Eingebettet via views_embed_view('vacancies_listing', 'block_related') (Default-Args lösen aus dem Route-Kontext auf).
  5. Formular-Gliederung via field_group. Das Term-Edit-Formular von vacancy_office wird mit field_group (bereits aktiv) in benannte, einklappbare Gruppen je Section zerlegt — das hält das feldreiche Formular (Sections 1+2+3+5) übersichtlich.
  6. Rendering: Erweiterung von node--vacancy--full.html.twig. Die fünf Sections werden im bestehenden handkomponierten Twig ergänzt; der Views-Block (Section 4) sitzt an seiner festen Position zwischen den Term-Sections. Alle benötigten SDCs existieren bereits (verifiziert 2026-06-10): Section 1 → video; Section 2 → card/card-frame/card-feature; Section 3 → bubble-card-frame (Recruiter) + heading/text/button (Adresse + Maps-Link); Section 4 → card via Views; Section 5 → badge + download-brochure-card. Kein Greenfield-SDC-Bau. Inline-Render-Mechanik: twig_tweak ist nicht aktiv — der Views-Block wird über Core-views_embed_view() eingebettet.
  7. Cache-Korrektheit explizit. Weil per-Hand auf Term-Felder zugegriffen wird (statt Entity-Render mit automatischem Bubbling), wird das Cache-Tag des Office-Terms in byte_theme_preprocess_node() explizit als Cacheable-Dependency gesetzt. Sonst invalidiert das Bearbeiten von „Stuttgart“ die betroffenen Vacancy-Seiten nicht — was der ganze Zweck von „geteilt pro Niederlassung“ ist. (Ein zweiter Block-Cache-Tag entfällt, da es keinen Globals-Block mehr gibt.)
  8. Keine neuen Module. paragraphs und block_content werden bewusst nicht eingeführt; media, views und field_group (alle aktiv) genügen.

Rejected Alternatives

Canvas-Content-Template direkt auf dem vacancy_office-Term

Technisch unmöglich. Wäre die eleganteste Variante (Term visuell im Canvas-Builder editieren, kein neuer Node, kein Linking). Aber Canvas-Content-Templates sind hart node-only (ApiConfigControllers.php:163, expliziter @todo in ContentTemplateHooks.php:90); ein canvas.content_template.taxonomy_term.vacancy_office.full lässt sich nicht erstellen. Erst relevant, wenn der Upstream-@todo aufgelöst wird.

Dedizierter „Niederlassung“-Node mit Canvas-Full, referenziert vom Term

Rejected wegen Editor-Friktion. Ein neuer Node-Typ mit Canvas-Full-Template (bewährtes Node-Pattern) plus Entity-Reference vom Office-Term gäbe maximalen visuellen Komfort und freie Komposition. Kostet aber: der Redakteur müsste zwei Entities pro Office pflegen und synchron halten (Node anlegen und am Term verknüpfen), mehr bewegliche Teile, Drift-Risiko (ADR-0039), und einen Canvas-Node in ein Twig-Template einzubetten ist Mehraufwand. Personio spiegelt zudem den Term, nicht einen Node — der Term ist der natürliche 1:1-Anker.

Paragraphs-Feld für Section 2 (6 Bild-Text-Cards)

Erwogen, bewusst verworfen. Ein office_card-Paragraph-Typ (Bild + Text, multi-value) gäbe die ergonomischste Repeater-UX: natives Hinzufügen/Sortieren, garantierte Bild-Text-Kopplung, beliebige Anzahl. Aber: die Card-Zahl ist im Design fix (max. 6), und paragraphs ist heute nicht installiert. Bei fester Obergrenze leisten sechs optionale Feld-Paare am Term dasselbe Ergebnis (nur befüllte Cards rendern) ohne eine neue Modul-Abhängigkeit. Der einzige Verlust — freies Hinzufügen über 6 hinaus — wird nicht gebraucht. Entscheidung des Users: Felder statt Modul.

Zwei parallele Multi-Value-Felder (Bild-Liste + Text-Liste) für Section 2

Rejected wegen fragiler Editor-UX. Modulfrei wäre auch ein field_office_card_images (Media, multi-value) + field_office_card_texts (Text, multi-value), im Twig nach Delta gepaart. Der Redakteur pflegt dann aber zwei getrennte Listen, deren Positionen manuell ausgerichtet bleiben müssen — verrutscht eine, verrutscht die Bild-Text-Zuordnung. Feste Feld-Paare pro Card vermeiden diese Fehlerquelle bei gleicher Modul-Freiheit.

Globale Quelle (block_content oder geschützter Default-Term) für Sections 1+5

Erwogen, bewusst verworfen zugunsten reiner Term-Felder. Ein block_content-Typ „Vacancy Footer Globals“ (eine Instanz, firmenweit) oder ein geschützter „Global“-Default-Term gäbe ein zentrales „einmal pflegen“ für Imagevideo, Award und Broschüre — mit optionalem Per-Office-Override. block_content bringt aber einen eigenen Revisions-/Permission-Stack mit (für eine 2–3-Feld-Section schwer), und ein Default-Term ist nur eine ungeschützte fachliche Konvention mit inverser Pflege-Logik. Beide Varianten kosten mehr Mechanik (Modul bzw. Schutz-Hook, Fallback-Coalesce, Override-Flags) als reine Per-Office-Felder. Entscheidung des Users: keine Module, jede Niederlassung pflegt 1+5 selbst. Der Preis (Pflege-Redundanz, falls firmenweit identisch) ist in den Consequences vermerkt.

Monolithischer Term-Render {{ node.field_office.entity|view('vacancy_footer') }}

Rejected, weil Section 4 dazwischenliegt. Ein einzelner Term-View-Mode-Render würde sauber cachen, kann aber den dynamischen Views-Block (Section 4) nicht aufnehmen, der zwischen Term-Sections 3 und 5 sitzt. Man müsste in zwei Teil-Renders um den Block herum splitten — unübersichtlicher als das bestehende handkomponierte Section-Muster konsequent fortzuführen.

Consequences

Positive

Negative / Concerns

C-1 — Pflege-Redundanz für Sections 1+5 (bewusst gewählt)

Ohne globale Quelle pflegt jede Niederlassung Imagevideo, kununu-Award und Karriere-Broschüre selbst. Sind diese firmenweit identisch, ist das Doppelarbeit, und ein Broschüren-Wechsel muss in jeder Niederlassung nachgezogen werden. Bewusst akzeptierter Trade-off (User-Entscheidung: keine Module). Rückfallpfad, falls die Redundanz stört: nachträglich eine globale Quelle (block_content oder Config-Singleton) + Per-Office-Override ergänzen — additiv, ohne das Feld-Modell zu brechen.

C-2 — Feldreiches Term-Formular

Sections 1+2+3+5 als flache Felder ergeben grob 20+ Felder am vacancy_office-Term (allein Section 2: bis zu 6 Card-Paare). Mitigation: zwingende field_group-Gliederung in einklappbare Section-Gruppen; klare Feld-Labels.

C-3 — Manuelles Cache-Tag-Bubbling

Per-Hand-Feldzugriff im Twig bubbelt Cache-Tags nicht automatisch. Mitigation: Office-Term-Cache-Tag zwingend in byte_theme_preprocess_node(); ein Render-/Kernel-Test prüft die Invalidierung gegen. (Nur eine Quelle, da kein Globals-Block.)

C-4 — Starre Card-Obergrenze (Section 2)

Feste Feld-Sets deckeln Section 2 auf 6 Cards. Braucht eine Niederlassung mehr, ist ein Schema-Eingriff (weitere Feld-Sets) nötig — nicht bloß ein Klick wie bei Paragraphs. Akzeptiert, weil das Design die Zahl fixiert; bei realem Bedarf an freier Anzahl ist der dokumentierte Pfad der Wechsel auf Paragraphs (Rejected Alternative).

Neutral / Folgeeffekte