Marktplatz · Kleinanzeigen

Admister — sieben Marktplätze, die eine Engine teilen

Kleinanzeigen, Autos, Immobilien, ein Warenmarkt mit Geschäften, digitale Schlüssel, Gutscheine und ein Preisaggregator. Sieben Dienste leben auf ihren eigenen Subdomains, und jeder von ihnen ist die gleiche Anwendung: Der Dienst wird aus dem Hostnamen ausgewählt, und der Katalog, Konten, Geld, Moderation und Suche werden geteilt. Wir haben das Modell entworfen, die Engine geschrieben und alle sieben ausgeliefert.

Kategorie
Marktplatz · Kleinanzeigen
Geschätzte Kosten
ab 32.000 €
Dienstleistungen
sieben, ein Codebasis
Sprachen
EN, UK · vier Anzeige-Währungen
Stapel
PHP 8.3, MariaDB, kein Framework
admister.org
Admister
Projekt-Homepage — admister.org

01Projektübersicht

Admister ist ein Multi-Vertical-Marktplatz. Unter einem Dach betreibt es Kleinanzeigen, ein Auto- und Teile-Board, Immobilien mit Verkauf und Vermietung, einen Warenmarkt mit Verkäufergeschäften, digitale Waren, einen Gutscheinservice und einen Preisaggregator. Jeder von ihnen hat seine eigene Subdomain, seine eigene Startseite und seine eigene Vorstellung davon, was ein Artikel ist.

Von außen sehen sie aus wie sieben separate Produkte. Von innen sind sie eine Anwendung: Der Dienst wird durch den Hostnamen der Anfrage bestimmt, und alles darunter — Konten, Geld, Moderation, Suche, Medien, Benachrichtigungen — wird geteilt. Das Projekt war von Anfang bis Ende unser: das Modell, der Motor, die Schnittstelle und der Start jedes Vertikals.

02Kontext und das Problem

Der übliche Weg, mehrere Marktplätze zu starten, besteht darin, mehrere Websites zu starten. Es funktioniert bis zur zweiten: Jetzt gibt es zwei Benutzertabellen, zwei Moderationswarteschlangen, zwei Zahlungsintegrationen und zwei Stellen, an denen derselbe Fehler zweimal behoben werden muss — und unterschiedlich behoben werden muss, weil der Code bis dahin abgedriftet ist.

Das gegenteilige Extrem ist ebenso schlecht. Eine Seite mit einer Kategorie namens «Autos» kann nicht nach Kilometerstand, Motor und Nummernschild fragen, wie es ein Auto-Board muss, und eine Immobiliensuche, die nicht nach Etage und Zimmern filtern kann, ist keine Immobiliensuche. Die Aufgabe bestand darin, einen Motor zu behalten, ohne vorzugeben, dass sieben sehr unterschiedliche Kataloge derselbe Katalog sind.

03Projektziele

  • ein Codebasis und ein Konto für alle Vertikalen, mit dem Dienst, der durch den Hostnamen gewählt wird;
  • per-Vertical-Attribute und Filter: Dutzende von Feldern für Autos und Immobilien, keines davon in die anderen Dienste gezogen;
  • Geld, dem der Eigentümer vertrauen kann — ein Guthaben, Krypto-Rechnungen, idempotente Gutschrift und Abgleich von feststeckenden Zahlungen;
  • Gründe für Käufer, zurückzukommen: gespeicherte Suchen mit Push, Verkäuferabzeichen, Auktionen und Gruppenkäufe;
  • zwei Sprachen und vier Anzeige-Währungen, ohne Inhalte zu duplizieren;
  • eine mobile Anwendung, die mit einem Backend-für-Frontend kommuniziert, anstatt die Seite zu scrapen.

04Was wir gemacht haben

Wir haben das Dienstverzeichnis in die Boot-Sequenz integriert. Eine Anfrage trifft ein, der Hostname wird mit dem Verzeichnis abgeglichen, und ab diesem Moment weiß die Anwendung, welches vertikale Segment sie ist: welche Module geladen werden sollen, welches Attributset verwendet werden soll, welche Startseite und welches Menü gerendert werden sollen. Die nackte Domain ist das Kleinanzeigenbrett, daher ist die kürzeste Adresse auch die allgemeinste.

Alles, was nicht vertikalspezifisch ist, wurde einmal geschrieben: Konten und Sitzungen, das Guthaben und Zahlungen, Medienaufnahme, Moderation, gespeicherte Suchen, Benachrichtigungen, die Sitemap und die API. Das Hinzufügen des siebten Dienstes bestand hauptsächlich darin, seine Attribute und seine Startseite zu schreiben — nicht einen weiteren Marktplatz.

05Sieben Dienstleistungen, sieben Kataloge

  • Kleinanzeigen — das allgemeine Board: alles von einem Fahrrad bis zu einem Sofa, kostenlos zu posten.
  • Autos — Fahrzeuge, Nummernschilder und Teile, mit einem speziellen Attributsatz von mehreren Dutzend Feldern und Filtern, die darauf basieren.
  • Immobilien — Verkauf und Vermietung, eigener Attributsatz, Karten und Suche nach Stadtteilen.
  • Markt — Waren mit Verkäufergeschäften: ein Schaufenster pro Verkäufer, Lager, Bestellungen.
  • Digital — Schlüssel, Geschenkkarten und eSIM: Waren, die geliefert, nicht versendet werden.
  • Angebote — Gutscheine und Promo-Codes von Partnergeschäften.
  • Preis — ein Preisaggregator: dasselbe Produkt in verschiedenen Geschäften, verglichen, mit gezählten ausgehenden Klicks.

Sie teilen das Konto, das Guthaben, die Moderationswarteschlange und den Suchindex — und teilen nichts in ihren Attributen, was der Punkt ist.

06Geld: ein Saldo, Krypto-Rechnungen, Abgleich

Bezahlte Funktionen — Werbung, Shop-Pläne, digitale Waren — werden aus einem internen Guthaben bezahlt, und das Guthaben wird mit Krypto über Payora, unser eigenes Gateway. Der Rechnungsinhalt wird in genau einer Funktion erstellt, da derselbe Inhalt vom Abgleich-Cron unter demselben Idempotenzschlüssel wiedergegeben wird: ein einzelnes Feld, das zwischen den beiden unterschiedlich ist, würde als Konflikt zurückkommen und die Zahlung hängen lassen.

Zwei Details, die wir bei jedem Gateway wiederholen würden. Die Rechnung trägt die eigene Locale des Käufers, die aus seiner Kontoreihe entnommen wird, nicht aus der Webanfrage — der Cron hat keine Anfrage und würde sonst allen Englisch senden. Und sie trägt eine Rücksendeadresse, die zurück ins Konto auf der nackten Domain führt, sodass ein Käufer, der im Automobilbereich begonnen hat, eingeloggt zurückkehrt, nicht zu einer Seite, die ihn auffordert, sich erneut anzumelden.

07Vertrauen: Moderation, Abzeichen, Streitigkeiten

Ein Marktplatz wird nach seinem schlechtesten Angebot beurteilt, daher ist Moderation hier kein Nebenschauplatz. Jede Entscheidung — genehmigen, ablehnen, entfernen — geht durch einen Bearbeiter, und «ablehnen» ist absichtlich nicht dasselbe wie «entfernen»: das erste ist eine Antwort an den Autor mit einem Grund, das zweite entfernt etwas, das bereits live war. Zwei Verben, zwei verschiedene Buchstaben, zwei verschiedene Zustände im Protokoll.

Verkäufer und Geschäfte tragen Abzeichen, die verdient, nicht gekauft werden: wie lange das Konto existiert, wie viele Geschäfte abgeschlossen wurden, ob die Telefonnummer und die E-Mail bestätigt sind. Der Käufer sieht dasselbe Abzeichen überall, wo der Verkäufer erscheint, da es an einem Ort berechnet wird.

09Eine mobile App mit eigenem Backend

Die Anwendung scrapt die Website nicht. Sie kommuniziert mit einem eigenen Backend-für-Frontend: einer kleinen Menge von Endpunkten, die in den Formen antworten, die die Bildschirme benötigen, mit Tokens, die separat von Web-Sitzungen ausgegeben werden. Zwei Pfade sind ohne Token offen — die Referenzdaten, die die App benötigt, bevor sich jemand anmeldet, und einen Gesundheitsendpunkt.

Dieser Gesundheitsendpunkt ist absichtlich eine separate Adresse. Das Register, das die Flotte überwacht, hat früher den Referenzendpunkt abgefragt, und dieser stellt jedes Wörterbuch und jeden Satz zusammen – eine Lebenszeichenprüfung sollte nicht mehr kosten als die Arbeit. Es antwortet auch mit der Vertragsversion, sodass ein für einen älteren Vertrag gebauter Client bemerkt wird, bevor es seine Benutzer bemerken.

10Sieben Fronten der SEO

Sieben Dienste auf einer Domain bedeuten sieben Sitemaps, sieben Sätze strukturierter Daten und einen sehr einfachen Weg, Duplikate zu erzeugen. Jede Vertikale veröffentlicht ihre eigene Sitemap aus dem Cache, die Listing-Seiten deklarieren ihren eigenen Typ strukturierter Daten, und Suchergebnisse-Seiten behaupten niemals, kanonisch für ein Listing zu sein.

Die Inhaltsseite ist ebenfalls automatisiert: Ein redaktioneller Stream kommt von Astrina, der Plattform, die wir genau dafür gebaut haben, und in dem Moment, in dem ein Artikel landet, wird der Sitemap-Cache für seine Vertikale gelöscht – andernfalls würde die neue Seite auf den nächsten Neuaufbau warten und niemand wüsste, warum sie fehlt.

11Ein ausgehender Kanal

Alles, was die Anwendung nach außen sendet – API-Aufrufe, Webhooks, Partner-Feeds, Bilder, die sie abruft, E-Mails – geht durch ein einzelnes Modul, das an einen SOCKS5-Egress-Proxy gebunden ist, wobei DNS ebenfalls über den Proxy aufgelöst wird. Das Modul ist fail-closed: Wenn der Proxy nicht verfügbar ist, wird die Anfrage blockiert, anstatt stillschweigend von der eigenen Adresse des Servers gesendet zu werden.

Das ist keine Dekoration. Ein Marktplatz zieht den ganzen Tag Partner-Feeds und entfernte Bilder; jede dieser Anfragen würde sonst die Ursprungsadresse der Seite an einen Dritten weitergeben. E-Mails gehen denselben Weg, durch YourTrend, sodass Briefe signiert sind und der Hostname des Servers niemals in einem Header erscheint.

12Technologie-Stack

  • PHP 8.3 ohne ein Framework, ein dünner PDO-Wrapper und überall vorbereitete Anweisungen;
  • MariaDB, das Schema wird als nummerierte Migrationsdateien und nicht als das Gedächtnis von jemandem aufbewahrt;
  • Medien werden außerhalb des Web-Stammverzeichnisses gespeichert und von einem Controller bereitgestellt, sodass ein Upload niemals einen ausführbaren Pfad hat;
  • ein Build-Schritt, der Assets hasht und nichts inline stellt, was zwischengespeichert werden sollte, mit einem Kill-Schalter für den Fall, dass es sich schlecht verhält;
  • SOCKS5-Ausgang für jeden ausgehenden Anruf, Mail über YourTrend SMTP;
  • Payora für Krypto-Rechnungen, Astrina für Anmeldungen und redaktionelle Inhalte, Adgora für Hauswerbung.

13Das Ergebnis

Sieben Live-Dienste auf einer Codebasis, ein Konto und ein Guthaben für alle, mit Attributen und Filtern, die sich so sehr unterscheiden wie die Kataloge. Ein neues vertikales Segment ist eine Reihe von Attributen und eine Startseite — kein neues Projekt, keine zweite Benutzertabelle, keine zweite Zahlungsintegration.

Was der Eigentümer täglich erhält, ist ein Ort, an dem er nachsehen kann. Moderation, Geld, Verkäufer und Inhalte werden über das gemeinsame Kontrollpanel der gesamten Gruppe verwaltet, und der Marktplatz stellt sie über eine signierte Admin-API bereit, anstatt über eine eigene zweite Admin-Seite.

14Erkenntnisse

Die Lektion, die wir in den nächsten Marktplatz mitnehmen würden: Teile die Maschinen, nicht die Bedeutung. Konten, Geld, Moderation und Suche sind dasselbe Problem in jedem Bereich und müssen einmal geschrieben werden. Attribute, Filter und die Form eines Listings sind das Produkt selbst, und sie in eine universelle Tabelle zu zwingen, ist der Weg, wie Marktplätze für alle gleichzeitig unbrauchbar werden.

Die zweite Lektion handelt von Geld. Der Ort, an dem eine Zahlung beschrieben wird, muss eine einzige Funktion sein, denn etwas anderes wird sie wiederholen — ein Cron, ein Retry, eine Abstimmung — und zwei Beschreibungen einer Zahlung sind der billigste Weg, um leise Geld zu verlieren.

Wenn du einen Marktplatz, ein Kleinanzeigen-Board oder eine Multi-Vertical-Plattform auf einem Motor aufbaust, ist dies die Art von Arbeit, die wir von Anfang bis Ende machen.