Einleitung: Mehr Komfort für Benutzer
Mit dem heutigen Entwicklungsschritt des URL-Shortener-Projekts wurde ein entscheidender Meilenstein erreicht, der die Benutzererfahrung grundlegend verbessert. Bis zu diesem Punkt war die Arbeit mit Kurz-URLs zwar funktional, aber in vielerlei Hinsicht noch linear: Jede Ziel-URL war genau einem Alias zugeordnet. Das bedeutete, dass Benutzer für unterschiedliche Kontexte oder Kampagnen jeweils eine neue Kurz-URL erstellen mussten, selbst wenn das Ziel identisch war. Dieser Ansatz war zwar einfach, aber nicht flexibel genug, um den realen Anwendungsanforderungen gerecht zu werden.
Den Quelltext zu diesem Entwicklungsstand findest Du auf Github unter https://github.com/svenruppert/url-shortener/tree/feature/advent-2025-day-06

Im Alltag stoßen Benutzer schnell an Grenzen, wenn sie dieselbe Zieladresse für verschiedene Zwecke abbilden möchten – etwa um Zugriffe aus unterschiedlichen Quellen zu analysieren oder interne und externe Verwendungen voneinander zu trennen. Das Arbeiten mit nur einem Alias pro Ziel-URL zwang sie zu Workarounds und führte zu unnötiger Redundanz in den gespeicherten Mappings. Gerade in professionellen Umgebungen, in denen Tracking, Nachvollziehbarkeit und Wiederverwendbarkeit eine Rolle spielen, war dieser Zustand unzufriedenstellend.
Mit der heutigen Entwicklung wurde diese Einschränkung aufgehoben. Das System erlaubt nun, mehrere Aliasse auf eine gemeinsame Ziel-URL zu verweisen. Damit rückt der Benutzerkomfort in den Mittelpunkt: Bestehende Kurz-URLs können nun um neue Aliasse erweitert werden, ohne dass dafür ein vollständig neuer Datensatz angelegt werden muss. Der Prozess wird durch eine intuitive Benutzeroberfläche begleitet, die sich nahtlos in den Detaildialog der Anwendung integriert.


Diese Erweiterung ist mehr als nur eine funktionale Verbesserung. Sie verändert die Art und Weise, wie Benutzer mit dem System interagieren. Während zuvor jeder Alias eine separate Entität darstellte, wird nun das Konzept einer zentralen Ziel-URL mit variablen Bezeichnern eingeführt. Der Benutzer denkt nicht mehr in einzelne Verknüpfungen, sondern in einen flexiblen Alias-Raum, der einer Zieladresse zugeordnet ist. Das reduziert die kognitive Belastung und beschleunigt typische Arbeitsabläufe spürbar.

Auf technischer Ebene wurde diese Entwicklung durch die Einführung neuer UI-Komponenten und Event-Mechanismen unterstützt, die eine dynamische Synchronisation zwischen Detail-Dialog und Übersicht ermöglichen. Doch die wahre Bedeutung liegt im Bedienerlebnis selbst: Der Shortener wird damit zu einem Werkzeug, das sich an reale Nutzungsmuster anpasst – nicht umgekehrt.
Vom Einzel-Alias zur Multi-Alias-Verwaltung
Die Einführung der Multi-Alias-Verwaltung markiert einen klaren Wendepunkt in der Entwicklung des URL-Shorteners. Während bisher jede Kurz-URL untrennbar an einen einzelnen Alias gebunden war, öffnet sich das System nun einem flexibleren und zugleich natürlicheren Modell der Zuordnung. Aus technischer Sicht bedeutet dies eine strukturelle Erweiterung des Datenmodells, aus Benutzersicht jedoch vor allem eines: Freiheit im Umgang mit der eigenen Kurzlink-Landschaft.

Im Zentrum dieser Neuerung steht die Komponente MultiAliasEditorStrict, die speziell dafür entwickelt wurde, den Benutzer intuitiv durch die Verwaltung mehrerer Aliasse zu führen. Der Editor ermöglicht es, zusätzliche Aliasse hinzuzufügen, bestehende zu ändern oder fehlerhafte Einträge zu entfernen, ohne dass dafür modale Wechsel oder neue Dialoge geöffnet werden müssen. Stattdessen bleibt der Benutzer in einem konsistenten Kontext – der Detailansicht einer Ziel-URL – und kann dort direkt agieren. Diese Entscheidung wurde bewusst getroffen, um Reibungsverluste in der Interaktion zu vermeiden und den Fluss zwischen Betrachtung und Bearbeitung zu wahren.
Die Implementierung von MultiAliasEditorStrict folgt einem klaren Prinzip: strenge Validierung bei gleichzeitig maximaler Benutzerfreundlichkeit. Jeder eingegebene Alias wird unmittelbar geprüft – auf leere Werte, doppelte Einträge und die syntaktische Korrektheit. Fehler werden direkt im Eingabefeld visualisiert, sodass der Benutzer unmittelbar Feedback erhält. Diese direkte Rückmeldung schafft Vertrauen in die Eingabe und verhindert, dass unvollständige oder fehlerhafte Daten den Persistenzlayer erreichen.
Eine Besonderheit dieser Komponente besteht in ihrem Zusammenspiel mit der Event-Infrastruktur der Anwendung. Sobald ein neuer Alias gespeichert oder ein bestehender Alias verändert wird, löst der Editor ein Ereignis aus, das über die gesamte UI propagiert. Damit werden auch andere Ansichten – etwa die Übersichtsliste – automatisch aktualisiert, ohne dass ein erneutes Laden erforderlich ist. Diese Ereignissteuerung sorgt für eine reaktive Benutzeroberfläche, die stets den aktuellen Zustand widerspiegelt.
Aus technischer Perspektive ist die Multi-Alias-Verwaltung ein Beispiel dafür, wie sich konsequent umgesetzte Benutzerorientierung und saubere Architektur gegenseitig verstärken können. Das System bleibt modular, die Komponenten sind klar getrennt, und dennoch entsteht für den Benutzer der Eindruck einer durchgängigen, organischen Oberfläche. Was zuvor ein sequenzieller Prozess war – Anlegen, Prüfen, Korrigieren – wird zu einem interaktiven Dialog mit dem System, das in Echtzeit auf die Aktionen des Benutzers reagiert.
Damit bildet MultiAliasEditorStrict nicht nur das Herzstück der neuen Alias-Logik, sondern auch einen Vorboten zukünftiger Entwicklungen. Seine Architektur legt die Grundlage für weitere Interaktionen wie Massenbearbeitungen, Alias-Gruppierungen oder zeitlich gesteuerte Alias-Aktivierungen. In diesem Sinne steht die Komponente exemplarisch für die Entwicklung des Projekts: von einem funktionalen Werkzeug hin zu einer reifen, benutzerzentrierten Anwendung.
quelltexte und Erläuterungen
MultiAliasEditorStrict – Kern der Multi-Alias-Verwaltung (Auszug)
package com.svenruppert.urlshortener.ui.vaadin.components;
//SNIPP
public class MultiAliasEditorStrict extends VerticalLayout {
private static final String RX = "^[A-Za-z0-9_-]{3,64}$";
private final Grid<Row> grid = new Grid<>(Row.class, false);
private final TextArea bulk = new TextArea("Aliases (comma/space/newline)");
private final Button insertBtn = new Button("Take over");
private final Button validateBtn = new Button("Validate all");
private final String baseUrl;
private final Function<String, Boolean> isAliasFree; // Server-Check (true = frei)
public MultiAliasEditorStrict(String baseUrl, Function<String, Boolean> isAliasFree) {
this.baseUrl = baseUrl;
this.isAliasFree = isAliasFree;
build();
}
// ==== Public API for the parent view ====
public void validateAll() {
var items = new ArrayList<>(grid.getListDataView().getItems().toList());
items.forEach(this::validateRow);
grid.getDataProvider().refreshAll();
}
public List<String> getValidAliases() {
return grid
.getListDataView()
.getItems()
.filter(r -> r.getStatus() == Status.VALID)
.map(Row::getAlias)
.collect(Collectors.toList());
}
public void markSaved(String alias) { setStatus(alias, Status.SAVED, "saved"); }
public void markError(String alias, String message) {
setStatus(alias, Status.ERROR, (message == null ? "error" : message));
}
public long countOpen() {
return grid.getListDataView().getItems()
.filter(r -> r.getStatus() != Status.SAVED).count();
}
public void clearAllRows() { grid.setItems(new ArrayList<>()); }
private void setStatus(String alias, Status s, String msg) {
grid.getListDataView().getItems().forEach(r -> {
if (Objects.equals(r.getAlias(), alias)) {
r.setStatus(s);
r.setMsg(msg);
}
});
grid.getDataProvider().refreshAll();
}
}
Erläuterung. Die Komponente kapselt die gesamte Alias-Verwaltung in einer eigenständigen UI-Einheit. Der Konstruktor injiziert die baseUrl sowie eine Funktion zur serverseitigen Verfügbarkeitsprüfung, wodurch der Editor testbar bleibt und von konkreten Services entkoppelt wird. Die öffentliche API liefert der aufrufenden View präzise Kontrollpunkte: Alle Einträge validieren, gültige Aliasse extrahieren, den Status einzelner Zeilen nach erfolgreichem Speichern auf „SAVED“ setzen, Fehlerzustände markieren und offene Arbeiten zählen. Die Methode setStatus aktualisiert den Zeilenstatus und triggert ein UI-Refresh des Grid-DataProvider, sodass Nutzer die Rückmeldung unmittelbar sehen.
UI‑Event für konsistentes Refreshing
package com.svenruppert.urlshortener.ui.vaadin.events;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEvent;
public class MappingCreatedOrChanged extends ComponentEvent<Component> {
public MappingCreatedOrChanged(Component source) { super(source, false); }
}
Erläuterung. Dieses minimalistische Event ist das Bindeglied zwischen Detail-Dialog, Create-View und Übersicht. Nach erfolgreichem Speichern neuer Aliasse feuert die UI dieses Ereignis; die OverviewView registriert einen Listener und lädt ihren DataProvider neu. Für den Benutzer bedeutet das: Änderungen werden ohne manuelles Reload sofort sichtbar.
Serverseitiger Redirect – konsistente Methodikontrolle (Auszug)
@Override
public void handle(HttpExchange exchange) throws IOException {
if (!RequestMethodUtils.requireGet(exchange)) return;
// ... weiterer Code
}
Erläuterung. Wiederkehrende Prüfungen‑ und Fehlerbehandlung werden in Utility-Methoden ausgelagert. Das reduziert die Streuung, verhindert Copy-&-Paste-Divergenzen und erhöht die Robustheit der Routen. Dieses Prinzip spiegelt sich auch in der UI wider: Validierung, Aufbereitung und Persistierung sind klar getrennt und werden über wohldefinierte Schnittstellen orchestriert.
Hinweis zur Darstellung. In diesem Kapitel sind bewusst nur die für die Benutzerführung entscheidenden Ausschnitte gezeigt. Die vollständigen Dateien sowie der Kontext der privaten Hilfsmethoden (build(), validateRow(…), Parser für Bulk-Eingaben, Row-POJO und Status-Enum) befinden sich im Branch feature/advent-2025-day-06. Auf sie greifen die öffentlichen API-Methoden unmittelbar zu und bilden den validierungsgetriebenen Interaktionsfluss ab.
Intelligentes Refreshing der Übersicht
Die Integration der Multi-Alias-Verwaltung wäre unvollständig, wenn Änderungen an bestehenden Kurzlinks nicht auch unmittelbar in der Übersicht sichtbar wären. Genau hier setzt das Konzept des intelligenten Refreshings an. Der Benutzer soll nach dem Hinzufügen oder Ändern von Aliassen nicht mehr gezwungen sein, die Ansicht manuell zu aktualisieren. Stattdessen reagiert die Anwendung automatisch auf relevante Ereignisse und stellt sicher, dass die dargestellten Daten jederzeit den aktuellen Zustand widerspiegeln.
Technisch umgesetzt wird dieses Verhalten über den globalen EventBus von Vaadin. Sobald im DetailsDialog das Event MappingCreatedOrChanged ausgelöst wird, registriert die OverviewView dieses Signal und löst intern den Aktualisierungsvorgang aus. Der Benutzer erlebt so ein nahtloses Zusammenspiel zwischen Bearbeitung und Übersicht. Die dafür verantwortliche Logik findet sich direkt im Konstruktor der OverviewView:
ComponentUtil.addListener(UI.getCurrent(),
MappingCreatedOrChanged.class,
_ -> {
logger().info("Received MappingCreatedOrChanged -> refreshing overview");
refreshPageInfo();
refresh();
});
Dieser Code zeigt, wie die Übersicht dynamisch auf Änderungen in anderen UI-Komponenten reagiert. Der entscheidende Teil liegt in der Registrierung des Event-Listeners mit ComponentUtil.addListener. Jedes Mal, wenn ein MappingCreatedOrChanged-Event ausgelöst wird – etwa durch den DetailsDialog nach dem Speichern neuer Aliasse –, empfängt die OverviewView dieses Ereignis. Die Methoden refreshPageInfo() und refresh() sorgen dafür, dass die angezeigten Daten neu geladen werden. Dadurch bleiben Statusanzeigen, Zähler und Tabelleninhalte stets aktuell, ohne dass der Benutzer eingreifen muss.
Wesentlich an dieser Implementierung sind ihre Einfachheit und Unabhängigkeit. Die OverviewView kennt die Quelle der Änderung nicht. Sie reagiert lediglich auf das generische Ereignis und holt die Daten frisch vom Server. Dieses lose gekoppelte Design verhindert zirkuläre Abhängigkeiten und macht das System robust gegenüber Erweiterungen. Künftige Komponenten, die ebenfalls Alias-Änderungen auslösen, können dasselbe Event nutzen, ohne die Übersicht anpassen zu müssen.
Bemerkenswert ist zudem, dass das EventBus-System in Vaadin hier eine klare Entkopplung zwischen UI-Elementen ermöglicht. Der DetailsDialog und die OverviewView teilen weder direkte Referenzen noch gegenseitige Kenntnis über ihren jeweiligen Zustand. Sie kommunizieren ausschließlich über Ereignisse. Diese Architektur ist nicht nur elegant, sondern auch skalierbar. Sie ermöglicht, das System in Zukunft um weitere Listener zu erweitern, etwa um Benachrichtigungen, Logging oder Statistikaktualisierungen.
Aus Benutzersicht ergibt sich daraus ein reaktives, unmittelbares Anwendungserlebnis. Änderungen an Aliassen oder Zieladressen werden in der Übersicht ohne spürbare Verzögerung widergespiegelt. Der Benutzer bleibt im Arbeitsfluss und hat jederzeit die Gewissheit, dass das, was er sieht, dem aktuellen Datenstand entspricht. Diese scheinbar kleine Anpassung transformiert die Bedienung des URL-Shorteners in ein modernes, responsives Interaktionsmodell, das Transparenz und Effizienz vereint.
Verbesserungen in der Create-View
Mit diesem Entwicklungsschritt erhielt auch die Create-View eine umfassende Überarbeitung. Ziel war es, den Prozess der Erstellung neuer Kurz-URLs stärker an die geänderte Benutzerlogik anzupassen und gleichzeitig die Arbeitsabläufe zu vereinheitlichen. Während der Detail-Dialog primär für die Pflege bestehender Mappings zuständig ist, fungiert die Create-View als Einstiegspunkt für neue Einträge – nun jedoch mit denselben Komfortfunktionen wie die Bearbeitungsansicht.

Die neue Version der Create-View bietet eine deutlich klarere Struktur und nutzt das Split-Layout, um Formularelemente und Vorschauinformationen übersichtlich nebeneinander anzuordnen. Dadurch kann der Benutzer sofort sehen, welche Aliasnamen er anlegt und wie sie zur Ziel-URL in Beziehung stehen. Außerdem wurde der MultiAliasEditorStrict vollständig integriert, sodass auch beim Erstellen einer neuen Kurz-URL mehrere Aliasse direkt erfasst werden können.
package com.svenruppert.urlshortener.ui.vaadin.views;
//SNIPP all imports
import static com.svenruppert.urlshortener.core.DefaultValues.SHORTCODE_BASE_URL;
@Route(value = CreateView.PATH, layout = MainLayout.class)
public class CreateView
extends VerticalLayout
implements HasLogger {
public static final String PATH = "create";
private static final ZoneId ZONE = ZoneId.systemDefault();
private final URLShortenerClient urlShortenerClient = UrlShortenerClientFactory.newInstance();
private final TextField urlField = new TextField("Target URL");
private final DatePicker expiresDate = new DatePicker("Expires (date)");
private final TimePicker expiresTime = new TimePicker("Expires (time)");
private final Checkbox noExpiry = new Checkbox("No expiry");
public CreateView() {
setSpacing(true);
setPadding(true);
setSizeFull();
urlField.setWidthFull();
Button saveAllButton = new Button("Save");
saveAllButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
Button resetButton = new Button("Reset");
resetButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
configureExpiryFields();
FormLayout form = new FormLayout();
form.add(urlField, noExpiry, expiresDate, expiresTime);
form.setResponsiveSteps(
new FormLayout.ResponsiveStep("0", 1),
new FormLayout.ResponsiveStep("900px", 2)
);
form.setColspan(urlField, 2);
HorizontalLayout actions = new HorizontalLayout(saveAllButton, resetButton);
actions.setWidthFull();
actions.setJustifyContentMode(JustifyContentMode.START);
Binder<ShortenRequest> binder = new Binder<>(ShortenRequest.class);
binder.forField(urlField)
.asRequired("URL must not be empty")
.withValidator((String url, ValueContext _) -> {
var res = UrlValidator.validate(url);
return res.valid() ? ValidationResult.ok() : ValidationResult.error(res.message());
})
.bind(ShortenRequest::getUrl, ShortenRequest::setUrl);
var editor = new MultiAliasEditorStrict(
SHORTCODE_BASE_URL,
alias -> {
try {
return urlShortenerClient.resolveShortcode(alias) == null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
);
editor.setSizeFull();
editor.getStyle().set("padding", "var(--lumo-space-m)");
saveAllButton.addClickListener(_ -> {
var validated = binder.validate();
if (validated.hasErrors()) return;
if (!validateExpiryInFuture()) return;
if (urlField.getValue() == null || urlField.getValue().isBlank()) {
Notification.show("Target URL is empty", 2500, Notification.Position.TOP_CENTER);
return;
}
editor.validateAll();
List<String> validAliases = editor.getValidAliases();
if (validAliases.isEmpty()) {
Notification.show("No valid aliases to save", 2000, Notification.Position.TOP_CENTER);
return;
}
Optional<Instant> expiresAt = computeExpiresAt();
int ok = 0;
for (String alias : validAliases) {
try {
logger().info("try to save mapping {} / {} ", urlField.getValue(), alias);
var customMapping = urlShortenerClient.createCustomMapping(alias, urlField.getValue(), expiresAt.orElse(null));
logger().info("created customMapping is {}", customMapping);
if (customMapping != null)
logger().info("saved - {}", customMapping);
else logger().info("save failed for target {} with alias {}", urlField.getValue(), alias);
editor.markSaved(alias);
ok++;
} catch (Exception ex) {
editor.markError(alias, String.valueOf(ex.getMessage()));
logger().info("failed to save url with alias {}", alias);
}
}
Notification.show("Saved: " + ok + " | Open: " + editor.countOpen(), 3500, Notification.Position.TOP_CENTER);
});
resetButton.addClickListener(_ -> {
clearFormAll(binder);
editor.clearAllRows();
});
// — SplitLayout
var leftCol = new VerticalLayout(new H2("Create new short links"), form, actions);
leftCol.setPadding(false);
leftCol.setSpacing(true);
leftCol.setSizeFull();
var rightCol = new VerticalLayout(new H2("Aliases"), editor);
rightCol.setPadding(false);
rightCol.setSpacing(true);
rightCol.setSizeFull();
SplitLayout split = new SplitLayout(leftCol, rightCol);
split.setSizeFull();
split.setSplitterPosition(40);
add(split);
}
//... SNIPP
private void clearFormAll(Binder<ShortenRequest> binder) {
urlField.clear();
noExpiry.clear();
expiresDate.clear();
expiresTime.clear();
binder.setBean(new ShortenRequest());
urlField.setInvalid(false);
}
}
Diese Implementierung verdeutlicht, dass die Create-View nicht länger nur ein einfaches Eingabeformular ist, sondern nun denselben Funktionsumfang wie der Detail-Dialog hat. Der Benutzer kann mehrere Aliasse erfassen, diese direkt validieren und mit einem einzigen Klick speichern. Die Integration des MappingCreatedOrChanged-Events sorgt auch hier dafür, dass die Übersicht automatisch aktualisiert wird, sobald ein neues Mapping erstellt wurde.
Der Einsatz des Split-Layouts unterstützt die visuelle Trennung zwischen Dateneingabe und Aliasverwaltung. Der Benutzer bleibt im Kontext seiner Eingabe und sieht gleichzeitig, wie sich die gewählten Aliasse in Beziehung zur Ziel-URL verhalten. Die Reset-Funktion ermöglicht, Eingaben vollständig zurückzusetzen, ohne die Seite neu laden zu müssen.
Durch diese Überarbeitung wurde die Create-View zu einem integralen Bestandteil der konsistenten Benutzerführung. Sie teilt sich die Validierungs- und Eventlogik mit dem Detail-Dialog und bildet damit eine gemeinsame Grundlage für das gesamte Alias-Management. Der Benutzer profitiert von einem einheitlichen Verhalten in beiden Kontexten, während der Code durch die Wiederverwendung zentraler Komponenten klarer, wartbarer und erweiterbarer bleibt.
Konsistente Validierung und Fehlerrückmeldung
Die wachsende Komplexität der Benutzeroberfläche im URL-Shortener erforderte eine ebenso verlässliche wie transparente Validierungslogik. Mit der Erweiterung um mehrere Aliasse wurde es unabdingbar, dem Benutzer klare Rückmeldungen zu geben, wenn Eingaben unvollständig, doppelt oder syntaktisch fehlerhaft sind. Diese Rückmeldungen mussten direkt im Kontext der jeweiligen Komponente erscheinen, um den Interaktionsfluss nicht zu unterbrechen.
Das Validierungssystem wurde daher konsequent in den bestehenden Binder-Mechanismus von Vaadin integriert. Der zentrale Einstiegspunkt hierfür ist die Create-View, in der das Feld für die Ziel-URL einer strikten Prüfung unterzogen wird. Die Implementierung nutzt einen eigens dafür vorgesehenen UrlValidator, der die syntaktische Gültigkeit und die Schema-Konformität prüft. Der folgende Auszug aus der Create-View zeigt die Bindung und Validierung des URL-Feldes:
Binder<ShortenRequest> binder = new Binder<>(ShortenRequest.class);
binder.forField(urlField)
.asRequired("URL must not be empty")
.withValidator((String url, ValueContext _) -> {
var res = UrlValidator.validate(url);
return res.valid() ? ValidationResult.ok() : ValidationResult.error(res.message());
})
.bind(ShortenRequest::getUrl, ShortenRequest::setUrl);
Dieser Code demonstriert das Zusammenspiel von Pflichtfeldprüfung und semantischer Validierung. Der Validator liefert bei ungültigen Eingaben eine präzise Fehlermeldung, die direkt unterhalb des Eingabefelds eingeblendet wird. Der Benutzer erkennt so sofort, warum eine Eingabe abgelehnt wurde. Auf diese Weise wird die klassische Formularvalidierung von Vaadin um projektspezifische Logik ergänzt.
Ein ähnliches Prinzip findet sich auch im MultiAliasEditorStrict, der jede Alias-Eingabe zeilenweise prüft. Neben der Überprüfung der syntaktischen Korrektheit wird auch geprüft, ob ein Alias bereits vergeben ist. Die visuelle Rückmeldung erfolgt direkt in der Alias-Tabelle, sodass der Benutzer sofort zwischen gültigen, gespeicherten und fehlerhaften Einträgen unterscheiden kann. Ein Auszug aus der zugehörigen Klasse verdeutlicht diese Mechanik:
public void validateAll() {
var items = new ArrayList<>(grid.getListDataView().getItems().toList());
items.forEach(this::validateRow);
grid.getDataProvider().refreshAll();
}
private void validateRow(Row r) {
var a = Objects.requireNonNullElse(r.getAlias(), "");
if (!a.matches(RX)) {
r.setStatus(Status.INVALID_FORMAT);
r.setMsg("3–64: A–Z a–z 0–9 - _");
return;
}
long same = grid.getListDataView().getItems()
.filter(x -> x != r && Objects.equals(a, x.getAlias()))
.count();
if (same > 0) {
r.setStatus(Status.CONFLICT);
r.setMsg("Duplicate in list");
return;
}
if (isAliasFree != null) {
try {
if (!isAliasFree.apply(a)) {
r.setStatus(Status.CONFLICT);
r.setMsg("Alias taken");
return;
}
} catch (Exception ex) {
r.setStatus(Status.ERROR);
r.setMsg("Check failed");
return;
}
}
r.setStatus(Status.VALID);
r.setMsg("");
}
Die Methode validateAll() iteriert über alle Aliaszeilen und ruft für jede eine interne Prüfung auf. Diese Validierung ist streng, aber für den Benutzer transparent: Jede Aliaszeile erhält einen eigenen Statusindikator und eine Textnachricht. Dadurch werden Fehlerzustände nicht abstrakt in einer Liste gesammelt, sondern unmittelbar im visuellen Kontext angezeigt. Diese Form der Inline-Validierung entspricht modernen UI-Prinzipien und minimiert kognitive Unterbrechungen bei der Dateneingabe.
Eine weitere Stärke dieser Architektur liegt in der Vereinheitlichung der Fehlerrückmeldungen. Ob eine ungültige URL, ein leerer Alias oder ein doppelt verwendeter Name – jede Abweichung wird auf dieselbe Weise behandelt und über denselben Mechanismus kommuniziert. Das reduziert Inkonsistenzen und schafft ein verlässliches Feedback-Verhalten, das Benutzer intuitiv nachvollziehen können. Gleichzeitig bleibt der Code leicht erweiterbar: Neue Prüfregeln können ergänzt werden, ohne die bestehende Logik anzutasten.
Im Zusammenspiel von Binder-Validierung und aliasbezogener Statusanzeige entsteht so ein kohärentes System der Rückmeldung. Der Benutzer erfährt unmittelbar, welche Eingaben akzeptiert wurden, welche korrigiert werden müssen und wann ein Datensatz erfolgreich gespeichert wurde. Dadurch wird Validierung nicht mehr als Hürde wahrgenommen, sondern als integraler Bestandteil einer präzisen und vertrauenswürdigen Benutzererfahrung.
Cheers Sven