[{"data":1,"prerenderedAt":396},["ShallowReactive",2],{"case-b2b-spare-parts-platform-orocommerce":3},{"id":4,"title":5,"body":6,"client":366,"client_anonymized":367,"dates":368,"description":358,"draft":369,"extension":370,"hero_image":367,"meta":371,"navigation":372,"path":373,"role":374,"seo":375,"slug":367,"stack":376,"stem":387,"summary":388,"tags":389,"__hash__":395},"caseStudies\u002Fde\u002Fcase-studies\u002Fb2b-spare-parts-platform-orocommerce.md","B2B-E-Commerce-Plattform — OroCommerce Enterprise 6.1",{"type":7,"value":8,"toc":357},"minimark",[9,14,18,21,29,33,36,43,119,122,126,137,143,173,176,182,196,206,224,230,240,245,271,277,281,284,310,313,324,328],[10,11,13],"h2",{"id":12},"kontext","Kontext",[15,16,17],"p",{},"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.",[15,19,20],{},"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 \u002F Region \u002F 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.",[15,22,23,24,28],{},"Die Plattform basiert auf ",[25,26,27],"strong",{},"OroCommerce Enterprise Edition 6.1"," — Oros B2B-orientierter E-Commerce-Applikation auf Symfony 6.4 \u002F 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.",[10,30,32],{"id":31},"ansatz","Ansatz",[15,34,35],{},"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-\u002FAngebots-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.",[15,37,38,39,42],{},"Die Arbeit wurde als Portfolio von ",[25,40,41],{},"20+ eigenen Symfony-Bundles"," strukturiert, von denen jedes einen zusammenhängenden Verhaltensbereich besitzt:",[44,45,46,59],"table",{},[47,48,49],"thead",{},[50,51,52,56],"tr",{},[53,54,55],"th",{},"Bereich",[53,57,58],{},"Bundles",[60,61,62,71,79,87,95,103,111],"tbody",{},[50,63,64,68],{},[65,66,67],"td",{},"Katalog & Suche",[65,69,70],{},"Catalog API, WebCatalog, Equipment-Konfigurator",[50,72,73,76],{},[65,74,75],{},"Commerce",[65,77,78],{},"Pricing, Order, Checkout, Customer",[50,80,81,84],{},[65,82,83],{},"Vertriebsprozess",[65,85,86],{},"RFQ",[50,88,89,92],{},[65,90,91],{},"Identität",[65,93,94],{},"Multi-Organization, eigene Auth-Schicht",[50,96,97,100],{},[65,98,99],{},"Fulfillment",[65,101,102],{},"Warehouse, Inventory, Shipping",[50,104,105,108],{},[65,106,107],{},"Dokumente",[65,109,110],{},"Document",[50,112,113,116],{},[65,114,115],{},"Analytics",[65,117,118],{},"Matomo-Integration",[15,120,121],{},"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.",[10,123,125],{"id":124},"umsetzungs-highlights","Umsetzungs-Highlights",[15,127,128,131,132,136],{},[25,129,130],{},"JSON:API durch Erweiterung der ApiBundle-Processors von Oro.","\nDie 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 ",[133,134,135],"code",{},"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.",[15,138,139,142],{},[25,140,141],{},"Asynchrone Workflows auf RabbitMQ über die Oro MessageQueue.","\nDrei Pipelines laufen asynchron über RabbitMQ:",[144,145,146,161,167],"ul",{},[147,148,149,152,153,156,157,160],"li",{},[25,150,151],{},"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 ",[25,154,155],{},"technische Maschinen-Skizzen"," und ",[25,158,159],{},"Maschine-zu-Teil-Zuordnungen",", gestützt auf eigene Entitäten.",[147,162,163,166],{},[25,164,165],{},"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.",[147,168,169,172],{},[25,170,171],{},"Inventory-Sync"," — inkrementelle Bestandsänderungen aus dem Warehouse-System strömen kontinuierlich ein und werden mit optimistischem Locking auf der Produkt-Entität angewendet.",[15,174,175],{},"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.",[15,177,178,181],{},[25,179,180],{},"Kundenspezifische Preise.","\nB2B-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.",[15,183,184,187,188,191,192,195],{},[25,185,186],{},"Produktsichtbarkeit pro Kunde \u002F pro Katalog — ein eigenes Drei-Schichten-Modell.","\nDie 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 ",[25,189,190],{},"Drei-Schichten-Modell"," ersetzt — Katalog-Metadaten → abgeleitete Basis-Katalog-Berechtigungen → aufgelöste Produkt-\u002FScope-Tabelle — durchgesetzt sowohl auf ",[25,193,194],{},"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.",[15,197,198,201,202,205],{},[25,199,200],{},"Hierarchisches Anlagen-Domänenmodell.","\nDer Katalog wird nicht als flache SKU-Liste navigiert, sondern über eine ",[25,203,204],{},"Machine- \u002F Assembly- \u002F Part- \u002F 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.",[15,207,208,211,212,215,216,219,220,223],{},[25,209,210],{},"SAP-Middleware-Integration.","\nBestände und Order-History liegen in der SAP-Landschaft des Kunden und werden über eine Middleware-Schicht erreicht. Die Integration betreibt eine ",[25,213,214],{},"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 ",[25,217,218],{},"Order-History-Merge",", der SAP-Auftragsdaten mit shopseitigen Bestellungen abgleicht, und ",[25,221,222],{},"asynchrone PDF-Auftragsbestätigungen"," auf dedizierten MessageQueue-Topics.",[15,225,226,229],{},[25,227,228],{},"Multi-Organization mit eigener Authentifizierungsschicht.","\nEin einzelner Unternehmenskunde kann ein Dutzend Tochtergesellschaften haben, jede mit eigenen Nutzern, eigenem Katalog-Scope und eigenen Freigaberegeln. Die eigene Auth-Schicht erweitert Oros Organization-\u002FBusiness-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.",[15,231,232,235,236,239],{},[25,233,234],{},"Storefront-Maschinen-Viewer (Vue 3).","\nEine in die Storefront eingebettete Vue-3-App bildet Produkte auf ",[25,237,238],{},"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.",[15,241,242],{},[25,243,244],{},"Suche und Speicherung.",[144,246,247,253,259,265],{},[147,248,249,252],{},[25,250,251],{},"Elasticsearch 8"," für Produktsuche und facettierte Katalog-Navigation.",[147,254,255,258],{},[25,256,257],{},"PostgreSQL 16"," als primärer OLTP-Speicher.",[147,260,261,264],{},[25,262,263],{},"Redis"," für Cache und Session-Storage.",[147,266,267,270],{},[25,268,269],{},"MongoDB"," für die Dokumentenablage (technische Zeichnungen, Datenblätter, Handbücher).",[15,272,273,276],{},[25,274,275],{},"CI\u002FCD und Qualität.","\nPHPUnit 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.",[10,278,280],{"id":279},"ergebnis","Ergebnis",[15,282,283],{},"Ausgewählte Produktivzahlen:",[144,285,286,292,298,304],{},[147,287,288,291],{},[25,289,290],{},"Katalog-Umfang"," — ~140.000 Produkte",[147,293,294,297],{},[25,295,296],{},"Konfigurator-Reichweite"," — 2.000+ Kataloge und 800+ registrierte Einheiten",[147,299,300,303],{},[25,301,302],{},"Aktive Organisationen"," — ~1.000 Kundenorganisationen",[147,305,306,309],{},[25,307,308],{},"Build & Deploy"," — Jenkins-Pipeline mit PHPUnit + Behat als Gate für jedes Release",[15,311,312],{},"Architekturentscheidungen, die sich ausgezahlt haben:",[144,314,315,318,321],{},[147,316,317],{},"Der Bundle-pro-Bereich-Schnitt hielt die Feature-Arbeit parallelisierbar; mehrere Bundles können im selben Release ausgeliefert werden, ohne sich gegenseitig zu koppeln.",[147,319,320],{},"Oros ApiBundle zu erweitern, statt eine parallele API-Oberfläche zu bauen, sparte eine komplette Autorisierungsschicht.",[147,322,323],{},"Asynchrone Workflows auf Oros MessageQueue laufen zu lassen bedeutete, dass Ops und Entwickler dieselben Dashboards nutzen statt zweier Stacks.",[10,325,327],{"id":326},"stack","Stack",[15,329,330,333,334,337,338,341,342,345,346,349,350,353,354,356],{},[25,331,332],{},"Backend"," — PHP 8.4, Symfony 6.4, OroCommerce EE 6.1, Doctrine ORM.\n",[25,335,336],{},"Speicher & Suche"," — PostgreSQL 16, Elasticsearch 8, Redis, MongoDB.\n",[25,339,340],{},"Async"," — RabbitMQ über die Oro MessageQueue.\n",[25,343,344],{},"Frontend"," — Vue 3, webpack 5.\n",[25,347,348],{},"Build & QA"," — Docker, Jenkins, PHPUnit, Behat, PHPCS, PHP-CS-Fixer, PHPMD.\n",[25,351,352],{},"API"," — JSON:API auf Basis von ",[133,355,135],{},".",{"title":358,"searchDepth":359,"depth":359,"links":360},"",2,[361,362,363,364,365],{"id":12,"depth":359,"text":13},{"id":31,"depth":359,"text":32},{"id":124,"depth":359,"text":125},{"id":279,"depth":359,"text":280},{"id":326,"depth":359,"text":327},"B2B-Ersatzteilplattform eines deutschen Maschinenbauers",null,"2024 – heute",false,"md",{},true,"\u002Fde\u002Fcase-studies\u002Fb2b-spare-parts-platform-orocommerce","Lead Developer",{"title":5,"description":358},[377,378,27,379,257,251,263,269,380,381,382,383,384,385,386],"PHP 8.4","Symfony 6.4","Doctrine ORM","RabbitMQ","Vue 3","webpack 5","Docker","Jenkins","PHPUnit","Behat","de\u002Fcase-studies\u002Fb2b-spare-parts-platform-orocommerce","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.",[390,391,392,393,394],"b2b","e-commerce","orocommerce","symfony","pim","8SUt53gyCqPUVjaAy8QXgoVc4YAH88FXJX6DPUNZwpE",1780993805389]