loc berlin, detz utc+02:00since 2010mode freelance · lead-developerrate on requestcontact dima@makaruk.dev
← back to work
#b2b #e-commerce #orocommerce #symfony #pim

B2B E-Commerce Platform — OroCommerce Enterprise 6.1

Full-stack development on an enterprise B2B parts platform — 20+ custom Symfony bundles extending OroCommerce EE, a three-layer per-customer visibility model replacing Oro's stock resolver, SAP Middleware integration, and a Vue 3 machine viewer over interactive equipment sketches.

Client
A German industrial-equipment manufacturer's B2B parts platform
Role
Lead Developer
Dates
2024 – present
PHP 8.4Symfony 6.4OroCommerce Enterprise Edition 6.1Doctrine ORMPostgreSQL 16Elasticsearch 8RedisMongoDBRabbitMQVue 3webpack 5DockerJenkinsPHPUnitBehat

Context

The client manufactures long-lived industrial equipment sold and serviced worldwide. Each unit is a capital good with a service life often measured in decades, carrying thousands of part numbers, consumables, and configuration options that the operator needs to be able to look up, price, and order without going through a salesperson.

The brief was a B2B platform that could carry that catalog: hundreds of thousands of SKUs, multi-organization access (one customer with multiple subsidiaries, each with its own roles and permissions), price lists that vary by customer / region / currency, an RFQ flow for items not on the standard catalog, and a configurator so an operator could specify which unit they were ordering parts for and have the catalog filter to compatible items.

The platform is built on OroCommerce Enterprise Edition 6.1 — Oro's flagship B2B-first e-commerce application on Symfony 6.4 / PHP 8.4. I joined as Lead Developer, owning the architecture and implementation of the Oro extension work that the stock platform doesn't cover out of the box.

Approach

OroCommerce EE is unusual among e-commerce platforms in being built explicitly around B2B requirements: native multi-organization, customer-specific pricing and visibility, RFQ / quote flows, and a Symfony-bundle-based extension model. That made it a strong base — but the brief still required customization across most of its subsystems.

The work was structured as a portfolio of 20+ custom Symfony bundles, each owning a coherent slice of behavior:

DomainBundles
Catalog & searchCatalog API, WebCatalog, Equipment Configurator
CommercePricing, Order, Checkout, Customer
Sales processRFQ
IdentityMulti-Organization, custom auth layer
FulfillmentWarehouse, Inventory, Shipping
DocumentsDocument
AnalyticsMatomo integration

Each bundle plugs into Oro's extension points — Symfony events, Doctrine listeners, Twig and YAML layout updates — rather than forking the platform. That keeps the door open to upstream Oro updates without three-way merge nightmares.

Implementation highlights

JSON:API by extending Oro's ApiBundle processors. The platform needed headless integrations — partner systems pulling product data, pushing order events, syncing inventory. Rather than build a parallel API surface, I extended Oro\Bundle\ApiBundle's processor pipeline so the same domain models served the storefront and the JSON:API consumers, with field-level access controlled by Oro's existing ACL layer. New endpoints become a matter of registering a processor — not writing controllers.

Async workflows on RabbitMQ via Oro MessageQueue. Three pipelines run asynchronously over RabbitMQ:

  • Catalog import — supplier feeds land as files, get parsed, normalized, and applied via idempotent batches so a re-run is safe. The same pipeline ingests machine technical sketches and machine-to-part mappings alongside parts data, backed by custom entities.
  • Order processing — once a checkout completes, downstream side effects (ERP push, fulfilment notification, audit log) run as separate messages so a slow ERP doesn't block the checkout response.
  • Inventory sync — incremental stock updates from the warehouse system stream in continuously and are applied with optimistic locking on the product entity.

All three run on Oro's MessageQueue components rather than a parallel Symfony Messenger setup, which keeps observability inside the same admin tooling Oro already provides.

Customer-specific pricing. B2B pricing is rarely "one price per SKU." Customers see negotiated prices, regional adjustments, and quantity breaks. I extended Oro's pricing engine via Doctrine listeners and Symfony events so the price-list resolution path stayed inside Oro's caching and indexing layer.

Per-customer / per-catalog product visibility — a custom three-layer model. Visibility was the hardest part of the brief: which customer may see which product, scoped per catalog, at production scale. Oro's stock customer-group resolver couldn't express it, so I replaced it with a three-layer model — catalog metadata → derived base-catalog permissions → a resolved product/scope table — enforced at both the Elasticsearch and the ORM query level. A customer never sees a part they're not entitled to whether they reach it via search or by navigating directly, and the resolution stays inside Oro's indexing layer rather than filtering in PHP after the fact.

Hierarchical equipment domain model. The catalog is navigated not as a flat SKU list but through a Machine / Assembly / Part / CustomerMachine hierarchy. An operator starts from the specific unit they own (CustomerMachine), drills into its assemblies, and reaches compatible parts — so the catalog filters itself to what fits their equipment instead of presenting the full ~140K-product list. Custom Doctrine entities back the hierarchy, populated by the import pipeline.

SAP Middleware integration. Inventory and order history live in the customer's SAP landscape, reached through a middleware layer. The integration runs a live inventory chain with a local fallback — a slow or unavailable SAP call degrades to cached stock rather than blocking the page — plus an order-history merge reconciling SAP order records with shop-side orders, and asynchronous PDF order confirmations delivered on dedicated MessageQueue topics.

Multi-organization with a custom authentication layer. A single corporate customer might have a dozen subsidiaries, each with its own users, its own catalog scope, and its own approval rules. The custom auth layer extends Oro's organization / business-unit model and translates the customer's external identity into the right Oro role and organization on every request, without persisting a sync.

Storefront machine viewer (Vue 3). A Vue 3 app embedded in the storefront maps products onto interactive machine sketches. The sidebar lists parts by material number and title; selecting an entry highlights the corresponding component on the sketch — turning "which part do I need?" from a part-number lookup into a visual one.

Search and storage.

  • Elasticsearch 8 for product search and faceted catalog navigation.
  • PostgreSQL 16 as the primary OLTP store.
  • Redis for cache and session storage.
  • MongoDB for document storage (technical drawings, datasheets, manuals).

CI/CD and quality. PHPUnit and Behat run in Jenkins on every push; PHPCS, PHP-CS-Fixer, and PHPMD are enforced as gate checks. Dockerized local environments mirror the staging deployment so onboarding a new developer takes hours, not days.

Outcome

Selected scale numbers from production:

  • Catalog scale — ~140,000 products
  • Configurator scope — 2,000+ catalogs and 800+ registered units
  • Active organizations — ~1,000 customer organizations
  • Build & deploy — Jenkins pipeline with PHPUnit + Behat gating every release

Architectural choices that paid off:

  • The bundle-per-domain split kept feature work parallelizable; multiple bundles can ship in the same release without cross-coupling.
  • Extending Oro's ApiBundle rather than building a parallel API surface saved an entire authorization layer.
  • Running async workflows on Oro's MessageQueue meant ops and developers used the same dashboards rather than two stacks.

Stack

Backend — PHP 8.4, Symfony 6.4, OroCommerce EE 6.1, Doctrine ORM. Storage & search — PostgreSQL 16, Elasticsearch 8, Redis, MongoDB. Async — RabbitMQ via Oro MessageQueue. Frontend — Vue 3, webpack 5. Build & QA — Docker, Jenkins, PHPUnit, Behat, PHPCS, PHP-CS-Fixer, PHPMD. API — JSON:API on top of Oro\Bundle\ApiBundle.

need an orocommerce / b2b e-commerce developer for something similar? i take on one new engagement at a time.