Storefront Development for Performance and Stability

When developing your storefront, consider storefront development best practices.

  • Don't post-process product or content search results. Search results can be large sets. Instead, all search criteria must go into the query for efficiency execution. Don't post-process with custom code.
  • Don't iterate over variations of a base product on a search result page (or any page where multiple base products appear). This approach can significantly increase the number of touched business objects. Instead, use native Salesforce B2C Commerce features to deal with variation product availability (pipelet Search, input parameter OrderableProductsOnly), to determine available variation values (dw.catalog.ProductSearchHit.getRepresentedVariationValues), or to determine price ranges (dw.catalog.ProductSearchHit.minPrice or Product.priceMode.minPrice).
  • Do Break search results into pages before processing or displaying in the storefront (pipelet Paging, class PagingModel). Instead, limit the maximum page size, for example, a maximum of 120 products per page, especially if the "View All" functionality is provided.
  • Don't trigger live calls to external systems on frequently visited pages (homepage, category, or search result pages and product pages). Where live calls are needed, specify a low timeout value (for example, 1 second). A B2C Commerce application server thread waiting for a response from an external system can't serve other requests. Many threads waiting for responses can make the entire cluster unresponsive, for any pages.
  • Don't execute any long running operations in a storefront pipeline (for example, import or export). Instead, use "jobs" for all long running tasks. The web tier closes browser connections after 5 minutes. The pipeline could still be running at this time.
  • Avoid concurrent changes to the same object. Storefront pipelines should only read shared data (for example, catalogs and prices) and read or write customer-specific data (for example, customer profiles, shopping carts or orders). This practice avoids making concurrent changes. The inventory framework is designed to support concurrent change (for example, two customers buying the same product at the same time or a customer buying a product while the inventory import is running). The storefront pipeline marks the order with EXPORT_STATUS_READY as the last step in order creation. Then order processing jobs can start modifying the order object. Concurrent requests for the same session are serialized at the application server. Concurrent Script API pipeline or controller requests can lead to Optimistic Locking exceptions. Any number of requests (AJAX or a WS) in the same session from a controller are treated as separate requests to the platform. If requests are concurrent, then optimistic locks prevents two requests from modifying same resource and the second request errors out.
  • Limit transaction size. The system is designed to deal with transactions with up to 1,000 modified business objects. A storefront pipeline shouldn't even come close to this number.
  • Make sure that the most visited pages are cacheable and well performing. These controller and pages are usually category page or search result pages (Search-Show), product detail pages (Product-Show), home pages (Default-Start, Home-Show), Cart Page (Cart-Show) and Checkout. For more information, see Optimize General Site Pages.
  • Limit expensive (> 10 ms) custom server logic on OnSession and OnRequest controllers. (Needed only one time at the start of a code review).
  • Use Index Friendly APIs
    • Replace database intensive or inefficient APIs with appropriate index-friendly APIs. Check code for database intensive APIs in most-visited pages:
      • Category.getProducts()
      • Category.getOnlineProducts()
      • Category.getProductAssignments()
      • Category.getOnlineCategoryAssignments()
      • ProductMgr.queryAllSiteProducts()
      • Product.getPriceModel()
      • Product.getVariants()
      • Product.getVariationModel()
    • Use API Methods that are index-friendly as appropriate to replace custom logic:
      • ProductSearchModel.search()
      • ProductSearchModel.orderableProductsOnly(true)
      • ProductSearchModel.getRefinements()
      • ProductSearchRefinements.getNextLevelRefinementValues()
      • ProductSearchModel.getProductSearchHits()
      • ProductSearchHit.getMinPrice()
      • ProductSearchHit.getMaxPrice()
      • ProductSearchHit.getRepresentedProductIDs()
      • ProductSearchHit.getRepresentedVariationValues(attribute)
  • Ensure all direct 3rd-party HTTP calls are migrated to Web Service Framework
  • Ensure no Enforced quota violations are reported in STAGING and PRODUCTION, and that Quota Dashboard alerts have been subscribed by all site admins.
  • Ensure there isn’t unnecessary creation of custom Session objects, productlist objects, or cookies.
  • Ensure a WishList isn’t created for every anonymous user (e.g.created at the end of every product item add to cart calls)
  • Ensure Custom Object volume is kept in check with purge jobs
  • OCAPI:
    • Ensure Shop API GET requests are limited to smaller blocks of data. Instead of 200 products payload, retrieve 100 or 50.
    • Ensure there’s no OCAPI request of persistent objects within a hook customization such as ProductMgr.getProduct() or product.getVariations()
    • For more information, see OCAPI best practices.
  • SFRA:
    • Ensure SFRA templates don't include multi-part, embedded, or nested forms. We don't recommend them as a best practice.
    • Ensure that controllers don’t call each other, because controller functionality should be self-contained to avoid circular dependencies.
    • Ensure no calling pipelets from within a controller. It's allowed while there are still pipelets that don't have equivalent B2C Commerce script methods, but won’t be supported in future.