Ich war zwei Monate bei payever. Februar und März 2020 — die zwei Monate unmittelbar bevor die Pandemie alles lahmlegte und jeder seine Pläne umschmeißen musste. Kurzer Einsatz, konkrete Arbeit. Darum geht es hier.
payever baut E-Commerce-Infrastruktur — eine Plattform, mit der Händler ihren Verkauf über mehrere Kanäle aus einem einzigen Produkt heraus verwalten können. Das konkrete Problem, das man mir hingeworfen hat: Backend-Microservices für die Integration mit Mobile.de, Amazon.de und eBay. Drei Marketplaces. Drei völlig verschiedene Datenmodelle. Eine einheitliche Domain-Repräsentation, mit der die Plattform konsistent arbeiten kann.
Wie viele Engineers kennst du, die das Vehicle-Listing-API von Mobile.de aus erster Hand beschreiben können? Eben.
Drei Marketplaces, drei verschiedene Weltbilder
Das Wichtigste bei Marketplace-Integrationen: jeder Marketplace ist nicht einfach ein anderes API. Er ist ein anderes konzeptuelles Modell davon, was Commerce bedeutet.
Amazon denkt in Catalog und Inventory als getrennte Konzepte. Ein Produkt hat eine ASIN — Amazons kanonische ID für ein Ding, das in der Welt existiert. Dein Listing ist nicht das Produkt — es ist dein Angebot, ein Produkt zu verkaufen, das Amazon bereits kennt. Diese Unterscheidung hat tiefgreifende Konsequenzen dafür, wie du Updates modellierst: Wenn du einen Preis änderst, aktualisierst du dein Angebot. Wenn du die Produktbeschreibung änderst, versuchst du möglicherweise, Amazons kanonischen Datensatz zu ändern — und da hat Amazon starke Meinungen. Der Feed-basierte Update-Flow (damals MWS, heute SP-API) ist asynchron und Acknowledgment-basiert. Du submittest einen Feed, bekommst eine Submission-ID, pollst nach Ergebnissen. Error Handling ist kein simpler HTTP-Status-Code; es ist ein strukturiertes Feed-Result-Dokument, das du parsen musst.
eBay funktioniert eher wie ein direktes Auktions-/Listing-Modell. Du besitzt dein Listing vollständig — es gibt keine Catalog-Disambiguierungsschicht. Das klingt einfacher, und in gewisser Weise ist es das; der Tradeoff ist, dass dein Listing eine Insel ist und du für alle Daten allein verantwortlich bist. Die Integrationskomplexität verlagert sich auf den Order-Flow: eBays Order-Modell hat eigene Konzepte für Buyer Messages, Feedback, Dispute Resolution und Return Initiation, die auf das interne Order-Modell der Plattform gemappt werden müssen.
Mobile.de ist das Interessante, das man auf einer Party erzählt, weil fast niemand außerhalb Deutschlands es kennt und fast niemand in Software direkt damit gearbeitet hat. Es ist Deutschlands dominanter Gebrauchtwagenmarkt. Das Inventory-Modell ist stark vertikal — ein Fahrzeuglisting hat Marke, Modell, Baujahr, Ausstattung, Kilometerstand, FIN, eine spezifische Klassifikationstaxonomie für den Zustand und eine Menge durchsuchbarer Attribute (Kraftstoffart, Getriebe, Schadstoffklasse), die auf dem deutschen Markt eine Bedeutung haben, die eine generische E-Commerce-Plattform nicht nativ versteht. Das API ist gut dokumentiert, aber das konzeptuelle Modell ist für jeden fremd, der nur mit horizontalen Retail-Integrationen gearbeitet hat.
Das Problem des einheitlichen Domain-Modells
Die Aufgabe der Plattform ist es, einem Händler eine einzige Oberfläche zum Verwalten all dessen zu geben. Das heißt: die Aufgabe des Backends ist ein einziges internes Domain-Modell, das ein Listing auf allen drei Plattformen faithful repräsentieren und Updates zwischen ihnen hin- und hertransportieren kann.
Hier wird es wirklich hart. Ein “Produkt” im internen Modell muss genug Information tragen, um:
- Als Feed-Eintrag gegen eine bekannte ASIN an Amazon submitted zu werden, oder einen neuen Produkt-Creation-Request auszulösen, wenn keine ASIN existiert
- Auf eBay gelistet zu werden, mit vollständiger Ownership der Listing-Daten, inklusive Kategorie, Item Specifics und Shipping Policy
- Auf Mobile.de gelistet zu werden, mit den korrekten Fahrzeugtaxonomie-Feldern, die weder Amazon noch eBay brauchen
Der naive Ansatz ist ein riesiges Produkt-Schema mit jedem Feld von jedem Marketplace. Das ist falsch. Die Felder sind nicht nur verschieden — sie sind semantisch verschieden. “Condition” auf eBay (New, Used, For parts or not working usw.) ist nicht dasselbe Konzept wie Fahrzeugzustand auf Mobile.de, das seinen eigenen Bewertungsvokabular hat. Ein einheitliches Modell, das diese Unterschiede flachklopft, produziert ein Schema, das lügt.
Der richtige Ansatz ist ein Core-Domain-Modell mit den Feldern, die wirklich universal sind (Titel, Preis, Inventory-Anzahl, Medien), plus Marketplace-spezifische Extensions, die getypt getrennt gespeichert werden. Beim Publish zu einem Marketplace kombinierst du den Core-Record mit der passenden Extension. Wenn du ein Update von einem Marketplace empfängst, zerlegst du es zurück. MongoDBs Document-Modell passt gut dazu, weil die Extension-Felder je nach Marketplace variieren und kein fixes Schema haben, das sich gut in einer Relational Table abbilden würde.
NestJS als die Naht
Die Service-Schicht war NestJS — TypeScript auf Node.js mit Angular-inspirierter Modulorganisation. Für Integration-Arbeit wie diese sind die Modulgrenzen wirklich nützlich: das Amazon-Modul besitzt das Amazon-Wire-Format, das eBay-Modul das eBay-Wire-Format, und das Core-Product-Modul das Domain-Modell. Die Dependency-Richtung ist klar. Nichts im Amazon-Modul hängt davon ab, wie eBay ein Listing repräsentiert.
RabbitMQ erledigte die asynchrone Arbeit: Marketplace-Feeds, die Zeit brauchen, eingehende Order-Notifications von jedem Marketplace, Inventory-Updates, die an alle aktiven Kanäle ausgefächert werden müssen, wenn ein Händler einen Preis ändert. Das Exchange-Routing war straightforward — ein Exchange pro Marketplace für eingehende Events, ein Exchange für interne Produkt-Updates, der an alle aktivierten Channel-Publisher fächert. Die Publisher sind die Module, die wissen, wie ein internes Update in das Marketplace-spezifische Wire-Format übersetzt wird.
Acht Wochen und was die wert sind
Zwei Monate sind nicht viel Zeit, und ich werde nicht so tun, als wäre die Integration vollständig gewesen. Was wir in diesem Zeitfenster geshippt haben, war der Core-Product-Push-Pfad und der eingehende Order-Sync für alle drei Marketplaces. Der Returns-Flow, die Messaging-Integration, die vollständige Fehler-Reconciliation-Loop — das war Roadmap.
Was zwei Monate reichen für: die Domain tief genug zu kennen, um nützliche Meinungen dazu zu haben. Ich kenne Mobile.des Fahrzeugtaxonomie von innen. Ich weiß, wie Amazons asynchroner Feed-Acknowledgment-Flow aussieht, wenn ein Feed die Validierung nicht besteht. Ich weiß, wie eBays Kategorie-spezifische Item-Specifics-Anforderungen darüber entscheiden können, ob ein Listing live geht oder nicht. Das sind keine abstrakten Architekturkonzepte — das sind die konkreten Failure-Modes, die in den Logs passierten, während ich sie las.
Das ist die Art von Integration-Archäologie, für die die meisten Engineers länger als zwei Monate brauchen, um eine Meinung zu entwickeln — weil die meisten nie gleichzeitig mit allen drei Systemen arbeiten. Der komprimierte Zeitplan erzwang Klarheit darüber, was zählte und was zufällige Komplexität war.
Die gleiche Disziplin stecke ich in Fulcrum: dasselbe Denken über typisierte Contracts zwischen Domains, stabile Event-Schemas und composable Adapter für externe Systeme gilt direkt für Agent-Orchestration. Die Probleme reimen sich, auch wenn die Domain anders ist. Und anders als beim payever-Engagement ist der Timeline auf Fulcrum nicht zwei Monate.