Commerce Engine is the centerpiece in Sitecore Commerce, a highly extensible E-commerce product built for seamless integration with Sitecore CMS. The 8.2.1 release replaces a lot of old functionality through a newer code-base built on .NET Core, exposed via a collection of OData services.
You will still need some elements of the legacy Commerce solution for maintaining Catalogs etc, but they too will eventually be replaced by Commerce Engine API services.
Customizing and extending Commerce Engine is fairly easy and can be done at several layers, but needs some thought to be put in to it. This is just bits and pieces of information I gathered recently by talking to the experts as well as a fair amount of trial and error. It is not meant to be a definitive guide on writing code on top of Commerce Engine, but covers some key constructs that are useful. More comprehensive documentation can be found here.
At a glance
Here is a rough top-to-bottom overview of a typical Commerce Engine API endpoint and its underlying design. It’s built using a three main concepts, Commands, Pipelines and Pipeline Blocks.
Any piece of Commerce Engine functionality can be invoked via an OData service endpoint. If you are not familiar with OData, it is a RESTful API built in accordance to the OData protocol — a widely accepted standards for Web APIs. ASP.NET has been supporting OData for quite some time now, and a lot of new API-based work coming out of Sitecore are likely to be supporting OData, as seen with Commerce Engine and xConnect.
Commerce Engine comes with a plethora of in-built APIs, which you can execute independently using a tool like Postman. The SDK package that comes with Sitecore Commerce has a great Postman Collection with references to a lot of these APIs, along with an exportable Postman environment file that has the required HTTP header values, including common ones such as Environment, Shopname and Currency, as well as request-specific ones; for example - Product/Variant ID for the AddCartLine() API call. Calling Commerce Engine APIs without the necessary HTTP headers will most likely end up with HTTP 400 (Bad Request) or similar responses, so it is better to download the SDK and import the Postman Collection and the sample Postman Environment to begin with.
Here are some examples of important API endpoints that are available in Commerce Engine.
Api/Carts([Cart ID]) — Retrieves the Cart Entity for a given ID
Api/AddCartLine() — Lets you add an existing product from your catalog to a Cart
Api/Customers([Customer ID]) — Retrieves the Customer entity for a given ID
Api/CreateOrder() — Convert an existing Cart entity to an Order
The Postman collection that comes with the SDK is probably the best source of reference for all of the available APIs, and what’s good about it being a Postman collection is that you can actually execute a sample request to get a real response — once you have Commerce Engine running.
You’re not limited to the existing functionality in anyway, and you can add your own API endpoints that trigger brand-new Pipelines, or add or remove fuctionality from existing Pipelines via Pipeline Blocks (more on Pipelines and Blocks later).
One way to code such extensions and alterations is typically through building your own ‘Plugin’ project.
A Commerce Engine Plugin is simply a Visual Studio project that can contain logic to cover a single unit of functionality like Cart, Orders or Payments, and can be deployed independently. A lot of existing functionality has been written in the form of Plugins, and you can find these deployed along with Commerce Engine deployments. To name a few, there’s Sitecore.Commerce.Plugin.Carts.dll, Sitecore.Commerce.Plugin.Orders.dll and Sitecore.Commerce.Plugin.Inventory.dll among many others. These plugins are wired up to associate with each other as Nuget dependencies, and your custom ones can and should be the same. The easiest way to roll out a new plugin is by using the Visual Studio project template that is available with the SDK.
OData API Endpoints
Chances of having to write your own OData API endpoint and configuring it in to Commerce Engine are probably rare. Commerce Engine already has an extensive collection of APIs and associated Pipelines that cover most functionality required for a standard e-commerce solution. However, each implementation has its own quirks, and there are chances that you may need to add functionality in to Commerce Engine but cannot find a clean way of tying it in to an existing Pipeline.
An OData Controller is not very different from a Controller file you would find in any ASP.net project.
From what I’ve seen so far, you do not need to add attribute routing at class level if your Controller class is named CommandsController, and they are automatically routed as /Api/[Commmand].
There are two minor things you will need to do at class level if you do not name your controller CommandsController.
1. Add a routing attribute [Route(“api/xx”)] specifying a Route prefix. This could be simply [Route(“api”)] or [Route(“api/payments”)]
2. Decorate it with an [Microsoft.AspNetCore.OData.EnableQuery] attribute
Next step is to add an actual method, which is the trigger point for calls from outside of Commerce Engine. Its unnecessary to contain any business logic inside these methods, as they are simply instruments to call a Command.
Both these examples simply call a Command, but the HTTP method and naming suggests that Example 1 retrieves state without changing it, whereas Example 2 changes state.
Also it’s important to note how data is being passed in to the Controller. In the first example it’s a simple URI parameter, and in the next example data is being passed in the within the Body of a PUT or POST HTTP message. It’s useful to pass data in the HTTP message body if you are passing a complex value that does not fit in to a simple URI parameter.
A Command object must inherit from Sitecore.Commerce.Core.Commands.CommerceCommand.
A Command is primarily responsible for executing a Pipeline, which is probably the most important programming construct within the Commerce Engine framework. It is similar to Pipelines that are in the Sitecore platform — running a set of pre-configured steps (called Blocks, as opposed to Processors in Sitecore) in a given order.
Most of the code here is wrapped inside a CommandActivity, which is a disposable type that does some housekeeping like checking the Messages property of CommerceContext object for any known error messages, keeping track of a Pipeline’s execution time, and if configured to do so, pushing data in to Performance Counters.
Arguments are in important concept, and are used to pass data in to a Pipeline.
You can declare any number of such types, and they should inherit from Sitecore.Commerce.Core.PipelineArgument class.
We will be looking at Pipelines next, and an Argument object’s job is to encapsulate input data for a Pipeline.
Pipelines by design are meant to be having a single input Type and an output Type. You can encapsulate all of input data in an Argument object.
An Argument allows you to group multiple bits of data together in to Pipeline, which it can dissect and consume in various ways.
Also note SampleEntity ,an Entity type.
Entities are types inheriting Sitecore.Commerce.Core.CommerceEntity.Entity.
These are persisted objects in the Commerce Engine, and is a container of Policy and Component objects — two principles that are key to Commerce Engine functionality.
You can declare both Policy and Component objects in a Plugin project and manipulate them in the pipeline, as well as persist them along with existing (Cart, Order etc.) and custom Entity types.
Pipelines are key to Commerce Engine, and every important piece of functionality is handled by a Pipeline. Declaring a Pipeline is easy.
You start with the interface, which defines the contract for the Pipeline.
A Pipeline has to be inheriting for Generic interface IPipeline<TInput, TOutput, TContext>, which consecutively defines the input type, output type and the context object.
In the example above the input type is an Argument type, and output is an Entity, but this does not necessarily need to be the case. For example, you can just as well declare a Commerce Engine pipeline that has an input of type string, and an output of type int. Like discussed before, Argument types allow for encapsulation of multiple bit of data that the Pipeline would likely require, and it is common for a Pipeline to return an Entity retrieved from persistent storage.
By convention, the actual concrete implementation will not contain any logic. Like in the sample below, all you need is an empty class declaration that implements the interface that we wrote above, and a constructor that allows the internal Dependency Injection mechanism to inject the required configuration and logging objects.
Since there is no business logic declared inside a Pipeline class, the actual work happens inside Pipeline Blocks. A Pipeline Block is a single unit of functionality that accepts a pre-configured input and returns an output.
All Blocks in a Pipeline do not necessarily need to have the same input and output types as its parent Pipeline, except for the input type of the first Block and the output type of the last Block.
The structure for a Pipeline Block is simple.
What’s great about the inbuilt Dependency Injection mechanism is that you can inject other components (Pipelines, Blocks or Services) in to a Block very easily. All you have to is to declare those Interfaces or Types as parameters inside the Constructor of the Pipeline Block.
This would allow you to run the in-built GetCart Pipeline from within your custom Block.
You can do this not just within Pipeline Blocks, but from within Command methods (described above) as well. For example, you can retrieve a Cart before a Pipeline is executed at Command level, and then pass in the cart as an input to the Pipeline.
You can have multiple blocks within a Pipeline, and the two main constraints for a Pipeline block is that:
1) A Pipeline Block must inherit type Sitecore.Framework.Pipelines. PipelineBlock<TInput, TOutput, TContext>
2) If the Pipeline Block is the first in the execution order (described later), it must accept an input type similar to the Pipeline
3) If last in the execution order, it must have the same return type as the output type of the Pipeline
4) Depending on where in the execution order of Blocks a given Block is, a pipeline Block must accept its predecessors’ output type.
A simple illustration of the input/output concept.
The execution order of a Pipeline’s Blocks are configured in a simple class, which by convention is named ConfigureSitecore.cs and implements Sitecore.Framework.Configuration.IConfigureSitecore interface.
There’s one final piece of work that needs to be done if you choose to add new API endpoints to Commerce Engine. Sitecore’s Commerece Connect layer uses a proxy to talk to Commerce Engine, and it needs to be regenerated in order to have a reference to your new endpoint. You can look here for instructions on how to do that. However, in the event that you are recreating the existing proxy, you need to supply required metadata through a Pipeline Block.
Typically, if your API endpoint support POST or PUT method and requires data to be passed in the body of the HTTP request, you would declare it as an OData Action, as opposed to Function. More about OData Actions/Functions here.
Finally, you need to modify your implementation of IConfigureSitecore interface (ConfigureSitecore.cs) so that this Block is injected in to the existing IConfigureServiceApiPipeline.