Sitecore Experience Commerce™ 9: Architected for Extensibility and Upgradability
Sitecore Experience Commerce™ 9, released in January 2018, represents a significant step in Sitecore’s ambition to bring their core competency as an infinitely-customizable platform to the online commerce market. Although Sitecore has had a commerce platform for a number of years, this is the first release to completely retire the legacy Commerce Server elements, which were built on ancient COM+ technology, in favor of a new Commerce Engine built on top of the recent .NET Core stack using a microservices architecture. A key design goal of this platform is to support extensibility by customers while preserving upgradability (avoiding the “too customized to upgrade” trap). This article examines how this is accomplished, what the development experience is like with this platform, and what new features Sitecore XC 9 offers over its predecessors.
A key design decision made by the Sitecore Commerce development team was to follow a “microservices” architecture, in which each element of functionality is contained in a separate plug-in. So, for example, functionality involving Inventory is in a separate package from catalog functionality. As the Commerce Technology Lead Kerry Havas has pointed out, this gives you “opt-in complexity”. If you are building a storefront for a vendor of digital goods (e.g. Netflix, Spotify), and you have no concept of inventory in your solution, you don’t need to take on a dependency on the Inventory plugin.
Having less code as part of your system makes extending it simpler and easier to evaluate the impact of upgrades. Similarly, it allows the Sitecore Commerce team to be more targeted in their releases. If Sitecore Commerce releases new features in the Inventory plugin, and this is not part of your solution, then you can ignore it. If it is part of your solution, you can more precisely evaluate the impact of the change, since you can see which of your plugins have a dependency on the modified module. There is a good discussion of the benefits of this approach in Sitecore’s Helix guide:
"By reducing the number of dependencies, and also making them apparent and obvious, the developers will know exactly what effects any change will have on the wider solution, greatly reducing the effort spent on stability and testing. Also, by isolating features, with defined interfaces and clear dependencies, the internal workings of the individual modules become less of an impediment, as developers can focus exclusively on the business feature they are addressing, thus greatly increasing flexibility of the solution and productivity of the team."
At the heart of this architecture is the Commerce Core library, which provides foundational elements of the system, but no actual commerce functionality. Instead, the Core provides the basic types used by the system: Commerce Entity, Component, List, and Policy, as well as abstractions for basic actions like persisting an entity (saving it to a database), or journaling changes. By defining these actions as abstractions, the high-level commerce code has no knowledge of the details of how data is persisted, so there are no embedded SQL queries as you sometimes find in Sitecore.Kernel code. This protects Sitecore’s code should the need arise to store the data using new technology (as xDB moved from SQL to MongoDB and then back to SQL), and it protects third party plugin developers too. You can only see the basic “persist” and “retrieve” calls, so you do not have the ability to make use of this knowledge in code, so they should be impervious to technology changes that come with a major Sitecore Commerce upgrade.
A remarkable feature of the system is that all elements, products, catalogs, orders, and customers are stored as subtypes of Commerce Entity in the database serialized to JSON. This means that none of the features of the system are visible in the database schema--there are no tables for billing address, adjustment, payments, or promotions—instead all this information is stored inside, for example, a single order, recorded in the database in a single text field as a JSON document. This is a verbose storage mechanism, but one that allows new functionality to be added to the system with no database changes. This is particularly useful for a system designed to support plugins, as each plugin’s data changes can be accommodated without database changes. It’s worth noting in the screen shot below that each dollar amount is accompanied by the .NET object type “Sitecore.Commerce.Core.Money”. This shows the verbosity of this approach, but also the flexibility: in the unlikely event that the Money type was extended or replaced, this change would be visible in the database, and it would be clear which transactions were recorded with the old vs the new type. And more complex objects can be stored and reconstructed, even if they were unknown to the original system.
I mentioned that every entity in the system is based on Commerce Entity. This base type ensures that each entity has some common features: an ID, a creation and update date, a version number (so changes can be journaled), and, most significantly, a list of “Components” and “Policies.” The Components list allows objects to be stored inside each element. For example, the Order entity stores payments here. And because this list is not restricted by type, plugins can use this to extend objects by adding new components to them. To borrow an example from the Developer Guide, a Loyalty Points plugin could store a Loyalty Points object in the Component field of a product, and during purchase, this object could be copied to the Components field of the Order. In this way, new functionality is added to two separate elements of the system, with no schema changes to either.
The Policies list is similar but has the behavior that only one policy of a given type can exist, so that when you write code to request a policy of a given type, you will get, at most, one object back. In fact, logic to ensure a single policy of a given C# type is baked into the SetPolicy method of the PolicyContainer object that is included in every entity, ensuring this uniqueness. So, a policy can be used to store configuration data and facts about any element of the system, such as the number of cart lines allowed, the connection string to access SQL server or a third-party API, or what price card has been applied to a product. Policies appear to be the fundamental mechanism for varying system behavior. As the Developer Guide states: “A named, versionable and variable set of data that can be used as facts within behaviors to influence behavioral outcomes. This provides an auditable mechanism for viewing, simulating, and changing core decision criteria that might be used by business processes or other policy-driven behavior.”
The Commerce Core, discussed above, contains no actual commerce functionality. All Commerce behavior is implemented through a series of isolated plugins, such as Sitecore.Commerce.Plugin.Payments or Sitecore.Commerce.Plugins.Catalog. The commerce team has pointed out that this gives a high degree of confidence in the extensibility architecture, as they have used it to build out the basic system. If you want to get a look at the core elements of a plugin, you can add the Plugin project type to Visual Studio using the Visual Studio Extension File (VSIX) that ships with the Commerce SDK. This allows you to create a Plugin project, which contains all the possible elements of a plugin:
The SampleController provides a starting point REST API, which invokes the SampleCommand, which launches the SamplePipeline, creating and returning a SampleEntity. So, all the elements are in place to begin constructing new functionality.
Postman Samples and Console App
Because all interaction with the commerce engine takes place through REST API calls, the SDK ships with a collection of sample API calls for loading into Postman, the REST API development tool. This collection serves to both document interaction with the service layer, and to speed plugin in development. For example, if a plugin takes effect when products are added to a cart, you can exercise the new code by calling the AddToCart samples:
The samples contain both the required headers and body text to successfully interact with the API:
This significantly speeds up development over having to manually add items to an online cart. In addition, the SDK ships with a Console application that runs integration tests to exercise the entire system, documents all API calls from initial catalog load to adding products to carts to completing orders and requesting refunds. This serves both to document the system, and verify it is working correctly both before and after developer modifications.
Console App during execution
Merge Cart tests
Views and UI
One last element of built-in extensibility worth discussing is how the architecture manages merchandizer (i.e. non-shopper) user interactions. These all occur in a new interface called BixFx, written in Angular 4, but meant to mimic the look and feel of a SPEAK launchpad application. However, no Angular4 or front-end development skills are involved in building this functionality. Instead, plugins create Views, which define exactly what fields are displayed and what actions are permitted in a pure JSON/REST interface. This is meant to isolate plugin developers from the impact of changes in front-end technology, and this approach proved its value when the Commerce UIs were migrated from SPEAK (in Sitecore Commerce 8.2.1) to Angular4 (in Sitecore XC 9), with no changes to the plugins themselves. To make this clearer, here are the “Add a Catalog” actions as transmitted by the Catalog View, and as displayed by the Catalog UI:
You can change the behavior of the bottom screen purely by changing the JSON REST calls that create it. This establishes a standard pardigm for buiding and extending UIs.
A fundamental evolution in the product is the use of the Sitecore Experience Accelerator (SXA) to house a standard set of commerce renderings, and to generate both a reference storefront and a sample catalog. In addition to the standard benefit of a reference storefront, to provide implementers with a starting point for custom solutions, this also gives plugin developers a place to build renderings to interact with the new back-end elements they are constructing. For example, a builder of a Loyalty Points plugin might create new renderings for displaying Loyalty Points on products, carts, and order summaries.
The SXA integration also provides the standard benefits of SXA, including presentation themes (such as a black and white “Site under construction” look shown below), Creative Exchange, and Drag and Drop page composition. See my earlier series on SXA for more on these features.
The out of the box sample site, in the "Storefront Mock Up" theme.
A New Catalog
Finally, it is worth touching on the new Catalog system, an area of the application that was rewritten from the earlier Commerce Server implementation that was part of 8.2.1 and earlier versions. The new catalog system uses a product (called “Sellable Item” to highlight that this could be a digital good or an entitlement such as a warranty) that is based on the Schema.org Product schema standard, facilitating interactions with external systems such as Google Product search. A noteworthy feature of the new catalog system is the way it combines elements such as inventory, pricing, and promotions, in a loose and flexible way. For example, inventory for individual products can be combined into inventory sets and used to manage inventory in different locations. Inventory sets can be attached to a catalog, as can price books and promotion sets. This allows, for example, adjusting all prices or triggering a holiday promotion in one catalog-wide operation. One thing that struck me as I was listening to this being explained was how many scenarios the multiple inventory sets could be used for: different online shops, online vs in store inventory, etc. The Catalog introductory video even states that this could be used to model “cross docking”, the practice of moving inventory from one truck to another without going to a warehouse. Flexibility and loose coupling are hallmarks of the system.
I hope this article gives a good flavor for the concepts and point of view that underlines Sitecore Experience Commerce™ 9--that a system should be easily extensible and architected in such a way that those extensions do not compromise future upgradability. If you wish to dig deeper into these concepts, I highly recommend the Master Sitecore YouTube series, which discusses the features and concepts of the system in depth, and was a key resource for this article.
If you’ve worked with Sitecore Experience Commerce™ 9, we’d love to hear your thoughts on your challenges and experiences. Please add to the discussion via the comments below or Tweet Us, @Velir!