ort berlin, detz utc+02:00seit 2010modus freelance · lead-developersatz on requestkontakt dima@makaruk.dev
← zurück zu den projekten
#b2b #e-commerce #orocommerce #symfony #pim

B2B-E-Commerce-Plattform — OroCommerce Enterprise 6.1

Full-Stack-Entwicklung auf einer B2B-Ersatzteilplattform — 20+ eigene Symfony-Bundles als Erweiterung von OroCommerce EE, ein Drei-Schichten-Sichtbarkeitsmodell pro Kunde anstelle des Oro-Standard-Resolvers, eine SAP-Middleware-Integration und ein Vue-3-Maschinen-Viewer über interaktiven Anlagen-Skizzen.

Kunde
B2B-Ersatzteilplattform eines deutschen Maschinenbauers
Rolle
Lead Developer
Zeitraum
2024 – heute
PHP 8.4Symfony 6.4OroCommerce Enterprise Edition 6.1Doctrine ORMPostgreSQL 16Elasticsearch 8RedisMongoDBRabbitMQVue 3webpack 5DockerJenkinsPHPUnitBehat

Kontext

Der Kunde stellt langlebige Industrieanlagen her, die weltweit verkauft und gewartet werden. Jede Einheit ist ein Investitionsgut mit einer Lebensdauer von oft mehreren Jahrzehnten und trägt Tausende von Teilenummern, Verbrauchsmaterialien und Konfigurationsoptionen, die der Betreiber nachschlagen, bepreisen und bestellen können muss — ohne den Umweg über einen Vertriebsmitarbeiter.

Die Aufgabe war eine B2B-Plattform, die diesen Katalog tragen kann: Hunderttausende SKUs, Multi-Organization-Zugriff (ein Kunde mit mehreren Tochtergesellschaften, jede mit eigenen Rollen und Berechtigungen), Preislisten je nach Kunde / Region / Währung, ein RFQ-Flow für Artikel außerhalb des Standardkatalogs sowie ein Konfigurator, mit dem ein Betreiber angeben kann, für welche Einheit er Teile bestellt, woraufhin der Katalog auf kompatible Artikel filtert.

Die Plattform basiert auf OroCommerce Enterprise Edition 6.1 — Oros B2B-orientierter E-Commerce-Applikation auf Symfony 6.4 / PHP 8.4. Ich kam als Lead Developer hinzu und verantworte Architektur und Umsetzung der Oro-Erweiterungen, die die Standardplattform nicht von Haus aus abdeckt.

Ansatz

OroCommerce EE ist unter den E-Commerce-Plattformen ungewöhnlich, weil es explizit um B2B-Anforderungen herum gebaut ist: native Multi-Organization, kundenspezifische Preise und Sichtbarkeit, RFQ-/Angebots-Flows und ein auf Symfony-Bundles basierendes Erweiterungsmodell. Das machte es zu einer starken Basis — der Auftrag erforderte dennoch Anpassungen über die meisten Subsysteme hinweg.

Die Arbeit wurde als Portfolio von 20+ eigenen Symfony-Bundles strukturiert, von denen jedes einen zusammenhängenden Verhaltensbereich besitzt:

BereichBundles
Katalog & SucheCatalog API, WebCatalog, Equipment-Konfigurator
CommercePricing, Order, Checkout, Customer
VertriebsprozessRFQ
IdentitätMulti-Organization, eigene Auth-Schicht
FulfillmentWarehouse, Inventory, Shipping
DokumenteDocument
AnalyticsMatomo-Integration

Jedes Bundle dockt an Oros Erweiterungspunkte an — Symfony-Events, Doctrine-Listener, Twig- und YAML-Layout-Updates — statt die Plattform zu forken. So bleibt der Weg für Upstream-Updates von Oro offen, ohne Drei-Wege-Merge-Albträume.

Umsetzungs-Highlights

JSON:API durch Erweiterung der ApiBundle-Processors von Oro. Die Plattform brauchte Headless-Integrationen — Partnersysteme, die Produktdaten ziehen, Order-Events pushen, Bestände synchronisieren. Statt eine parallele API-Oberfläche zu bauen, habe ich die Processor-Pipeline von Oro\Bundle\ApiBundle erweitert, sodass dieselben Domänenmodelle Storefront und JSON:API-Consumer bedienen — mit feldgenauer Zugriffskontrolle über Oros bestehende ACL-Schicht. Ein neuer Endpunkt wird zur Registrierung eines Processors, nicht zum Schreiben von Controllern.

Asynchrone Workflows auf RabbitMQ über die Oro MessageQueue. Drei Pipelines laufen asynchron über RabbitMQ:

  • Katalog-Import — Lieferanten-Feeds landen als Dateien, werden geparst, normalisiert und über idempotente Batches angewendet, sodass ein erneuter Lauf sicher ist. Dieselbe Pipeline verarbeitet neben den Teiledaten auch technische Maschinen-Skizzen und Maschine-zu-Teil-Zuordnungen, gestützt auf eigene Entitäten.
  • Order-Processing — sobald ein Checkout abgeschlossen ist, laufen nachgelagerte Effekte (ERP-Push, Fulfillment-Benachrichtigung, Audit-Log) als separate Nachrichten, damit ein langsames ERP die Checkout-Antwort nicht blockiert.
  • Inventory-Sync — inkrementelle Bestandsänderungen aus dem Warehouse-System strömen kontinuierlich ein und werden mit optimistischem Locking auf der Produkt-Entität angewendet.

Alle drei laufen über die MessageQueue-Komponenten von Oro statt über ein paralleles Symfony-Messenger-Setup, was die Observability im selben Admin-Tooling hält, das Oro ohnehin bereitstellt.

Kundenspezifische Preise. B2B-Preisfindung ist selten „ein Preis pro SKU". Kunden sehen ausgehandelte Preise, regionale Anpassungen und Mengenstaffeln. Ich habe Oros Pricing-Engine über Doctrine-Listener und Symfony-Events erweitert, sodass die Preislisten-Auflösung innerhalb von Oros Caching- und Indexing-Schicht bleibt.

Produktsichtbarkeit pro Kunde / pro Katalog — ein eigenes Drei-Schichten-Modell. Die Sichtbarkeit war der schwierigste Teil des Auftrags: welcher Kunde welches Produkt sehen darf, je Katalog skaliert, im Produktivumfang. Der Standard-Customer-Group-Resolver von Oro konnte das nicht abbilden, daher habe ich ihn durch ein Drei-Schichten-Modell ersetzt — Katalog-Metadaten → abgeleitete Basis-Katalog-Berechtigungen → aufgelöste Produkt-/Scope-Tabelle — durchgesetzt sowohl auf Elasticsearch- als auch auf ORM-Query-Ebene. Ein Kunde sieht nie ein Teil, für das er nicht berechtigt ist — ob er es über die Suche erreicht oder direkt navigiert — und die Auflösung bleibt in Oros Indexing-Schicht, statt nachträglich in PHP zu filtern.

Hierarchisches Anlagen-Domänenmodell. Der Katalog wird nicht als flache SKU-Liste navigiert, sondern über eine Machine- / Assembly- / Part- / CustomerMachine-Hierarchie. Ein Betreiber startet von der konkreten Einheit, die er besitzt (CustomerMachine), steigt in deren Baugruppen ab und gelangt zu kompatiblen Teilen — der Katalog filtert sich also auf das, was zu seiner Anlage passt, statt die volle Liste von ~140.000 Produkten zu zeigen. Eigene Doctrine-Entitäten tragen die Hierarchie, befüllt durch die Import-Pipeline.

SAP-Middleware-Integration. Bestände und Order-History liegen in der SAP-Landschaft des Kunden und werden über eine Middleware-Schicht erreicht. Die Integration betreibt eine Live-Bestandsabfrage mit lokalem Fallback — ein langsamer oder nicht verfügbarer SAP-Aufruf degradiert auf zwischengespeicherte Bestände, statt die Seite zu blockieren — dazu einen Order-History-Merge, der SAP-Auftragsdaten mit shopseitigen Bestellungen abgleicht, und asynchrone PDF-Auftragsbestätigungen auf dedizierten MessageQueue-Topics.

Multi-Organization mit eigener Authentifizierungsschicht. Ein einzelner Unternehmenskunde kann ein Dutzend Tochtergesellschaften haben, jede mit eigenen Nutzern, eigenem Katalog-Scope und eigenen Freigaberegeln. Die eigene Auth-Schicht erweitert Oros Organization-/Business-Unit-Modell und übersetzt die externe Identität des Kunden bei jeder Anfrage in die richtige Oro-Rolle und -Organisation, ohne eine Synchronisation zu persistieren.

Storefront-Maschinen-Viewer (Vue 3). Eine in die Storefront eingebettete Vue-3-App bildet Produkte auf interaktive Maschinen-Skizzen ab. Die Seitenleiste listet Teile nach Materialnummer und Bezeichnung; die Auswahl eines Eintrags hebt die zugehörige Komponente in der Skizze hervor — und macht aus „welches Teil brauche ich?" eine visuelle statt einer Teilenummern-Suche.

Suche und Speicherung.

  • Elasticsearch 8 für Produktsuche und facettierte Katalog-Navigation.
  • PostgreSQL 16 als primärer OLTP-Speicher.
  • Redis für Cache und Session-Storage.
  • MongoDB für die Dokumentenablage (technische Zeichnungen, Datenblätter, Handbücher).

CI/CD und Qualität. PHPUnit und Behat laufen in Jenkins bei jedem Push; PHPCS, PHP-CS-Fixer und PHPMD werden als Gate-Checks erzwungen. Dockerisierte lokale Umgebungen spiegeln das Staging-Deployment, sodass das Onboarding eines neuen Entwicklers Stunden statt Tage dauert.

Ergebnis

Ausgewählte Produktivzahlen:

  • Katalog-Umfang — ~140.000 Produkte
  • Konfigurator-Reichweite — 2.000+ Kataloge und 800+ registrierte Einheiten
  • Aktive Organisationen — ~1.000 Kundenorganisationen
  • Build & Deploy — Jenkins-Pipeline mit PHPUnit + Behat als Gate für jedes Release

Architekturentscheidungen, die sich ausgezahlt haben:

  • Der Bundle-pro-Bereich-Schnitt hielt die Feature-Arbeit parallelisierbar; mehrere Bundles können im selben Release ausgeliefert werden, ohne sich gegenseitig zu koppeln.
  • Oros ApiBundle zu erweitern, statt eine parallele API-Oberfläche zu bauen, sparte eine komplette Autorisierungsschicht.
  • Asynchrone Workflows auf Oros MessageQueue laufen zu lassen bedeutete, dass Ops und Entwickler dieselben Dashboards nutzen statt zweier Stacks.

Stack

Backend — PHP 8.4, Symfony 6.4, OroCommerce EE 6.1, Doctrine ORM. Speicher & Suche — PostgreSQL 16, Elasticsearch 8, Redis, MongoDB. Async — RabbitMQ über die Oro MessageQueue. Frontend — Vue 3, webpack 5. Build & QA — Docker, Jenkins, PHPUnit, Behat, PHPCS, PHP-CS-Fixer, PHPMD. API — JSON:API auf Basis von Oro\Bundle\ApiBundle.