Migrate your accelerator-based storefront to Composable Storefront
If you’ve read the “Five reasons to move to a project Spartacus javascript storefront ” and “Getting started with SAP Commerce Cloud Project Spartacus” you may be wanting to migrate to a stateless high-performance architecture and now wonder how to actually prepare for migration. In this article we will discuss an approach suitable for small storefronts, but with a process that can also help larger migrations, where a full re-implementation is recommended.
To Migrate or Not?
Although the recommendation is to use the move to Composable Storefront (a.k.a. Spartacus) as an opportunity to start fresh and rethink your storefront experience, you may have requirements to migrate to Composable Storefront and maintain the same experience. Depending on the number and method of customizing your accelerator-based storefront, you made find trying to migrate your existing experience over to Composable Storefront to be something simple or difficult. If you’re unsure, you can use the steps below to go through an exercise to identify the number of changes without necessarily spending the time to implement. This can give you an idea of how much work may be required to migrate vs. starting with a greenfield approach.
As mentioned in “Getting started with SAP Commerce Cloud Project Spartacus” you can run your Accelerator and Composable Storefronts at the same time to reduce risk, though it is recommended to not do this for a prolonged period. Maintaining two storefronts on two technology stacks can make development and testing quite difficult, not to mention you may be giving your customers an inconsistent user experience depending on which storefront page they are hitting.
Prerequisites
It’s recommended that you start your migration to a Composable Storefront after upgrading to SAP Commerce 1905 or later, as the way Omni Commerce Connect (OCC) application programming interfaces (APIs) are installed has been simplified (they are now available as extensions instead of AddOns).
To get started you will also need the following:
Initial Setup
Review the What’s New in SAP Commerce Cloud, composable storefront and Roadmap for Composable Storefront to ensure you are aware of which features have parity, which may have changed and which may be missing
Upgraded your existing storefront to use at least SAP Commerce 2011
Any existing OCC Addons have been converted into regular extensions (if using SAP Commerce 2005 or later)
You have gone through the steps of “Build and Deploy Your First SAP Commerce Cloud Project”. You may also want to review the content covered in Setting Up SAP Commerce Cloud for use with Composable Storefront, and ensure you have configured and performed the Building the Composable Storefront From Libraries
Front-End Team
The front-end team will build the storefront User Interface composed of layout and Angular Modules. The core skills of the developers will be:
-
- Angular
-
- RxJS
-
- Composable Storefront
-
- HMTL5
Back-End Team
The back-end team will build the OCC APIs needed by the front-end team. The core developer skills needed are:
-
- SAP Commerce
-
- OCC
Anatomy of a Composable Storefront
Before you begin your migration, you should be familiar with how a Composable Storefront works. Start by visiting the Composable Storefront Documentation and taking a look at the Modules and Components directory. It will look like this:
Navigate the Modules and Components and get acquainted with the functionality offered by Composable Storefront. Pay special attention at Modules that provide a default which define the Mapping between standard CMS Components and Composable Storefront Components. In the example below, the BannerComponent provides mappings to SimpleResponsiveBannerComponent, BannerComponent and SimpleBannerComponent.
BannerModule
@NgModule({
imports: [CommonModule, RouterModule, GenericLinkModule, MediaModule],
providers: [
provideDefaultConfig(<CmsConfig>{
cmsComponents: {
SimpleResponsiveBannerComponent: {
component: BannerComponent,
},
BannerComponent: {
component: BannerComponent,
},
SimpleBannerComponent: {
component: BannerComponent,
},
},
}),
],
declarations: [BannerComponent],
entryComponents: [BannerComponent],
exports: [BannerComponent],
})
export class BannerModule {}
To see the which API endpoints are available in your local installation, navigate to the OCC Swagger documentation. You may need to install additional extensions if a given endpoint is missing.
Version | URL |
2005 and above | https://localhost:9002/occ/v2/swagger-ui.html |
1905 and below | https://localhost:9002/rest/v2/swagger-ui.html |
Accelerator Storefront vs Composable Storefront
You might be familiar with the standard Spring-MVC based accelerator on the left side of the picture below, but look carefully to the description of the Composable Storefront on the right to understand the key differences.
Accelerator Storefront
In the traditional storefront, the browser makes requests to the server which retrieve the page structure and executes controllers, facades and services to process and retrieve the information needed to render the view. Most of the state is kept on the server-side, inside the HttpSession, which may be replicated across containers. |
Composable Storefront
In the headless storefront the front-end is loaded on the browser, and the page structure and layout is retrieved from the server (unless it has a static layout). The Composable Storefront Components (see Modules and Components above) are used to build the page on the client side, and they execute OCC Calls to the server (see OCC APIs above) to retrieve data needed for rendering. The initial page might, however, be built initially on the Server (using a technology known as Server Side Rendering – SSR), for performance and SEO purposes. State is kept on the Client side, inside the Browser Local / Session Storage. |
During the migration, you will break down the existing accelerator Controller functionality into individual Composable Storefront Angular Components (also including the view/template logic) and OCC APIs, as indicated by the dotted lines. Be aware in some cases it might be necessary to also modify existing underlying Facades and Services.
Changes to the Content Catalog will also be required, the Composable Storefront documentation gives a good overview of the differences between the accelerator and Composable Storefront sample data.
Finally, you can analyze a sample call for the Product Details Page with the help of Chrome Developer Tools, to see how everything comes together. Use the Network Tab to see the requests generated by Composable Storefront.
Going through the calls above in more detail, using the Swagger API documentation as reference, you can see:
Call | Parameters | Purpose | OCC Controller |
/occ/v2/electronics-spa/cms/pages | ng=en&curr=USD |
Requests the product details page including all WCMS Slots and Components. Using the Component list in the response, Composable Storefront will use the CMSConfig mappings (see BannerModule example above) and will instantiate an Angular Component for each CMS Component and build the page using the structure provided by the slots, on the customer’s browser. |
de.hybris.platform.cmsocc.controllers.PageController |
/sockjs-node/info | info?t=1597322562030 | Only used during development, this socket is used to update the web app when the backend rebuilds | |
/occ/v2/electronics-spa/products/300938 | 300938?fields=name,purchasable,baseOptions(DEFAULT…iantOptions(DEFAULT),variantType&lang=en&curr=USD | Returns details of a single product according to the product code 300938
|
de.hybris.platform.commercewebservices.core.v2.controller.ProductsController |
/occ/v2/electronics-spa/cms/components | components?fields=DEFAULT&productCode=300938&curre…nk%2CBlankVideotapesCategoryLink&lang=en&curr=USD | Requests a list of the provided CMS components | de.hybris.platform.cmsocc.controllers.ComponentController |
/occ/v2/electronics-spa/languages | languages?lang=en&curr=USD | Gets a list of available languages | de.hybris.platform.commercewebservices.core.v2.controller.MiscsController |
/occ/v2/electronics-spa/currencies | currencies?lang=en&curr=USD | Gets a list of available currencies | de.hybris.platform.commercewebservices.core.v2.controller.MiscsController |
/occ/v2/electronics-spa/cms/components | components?fields=DEFAULT&productCode=300938&curre…ink%2CFacebookLink%2CTwitterLink&lang=en&curr=USD | Requests additional CMS components | de.hybris.platform.cmsocc.controllers.ComponentController |
/occ/v2/electronics-spa/users/anonymous/consenttemplates | consenttemplates?lang=en&curr=USD | Fetches the list of consents | de.hybris.platform.commercewebservices.core.v2.controller.ConsentsController |
/occ/v2/electronics-spa/cms/components | components?fields=DEFAULT&productCode=300938&curre…eviewsTabComponent%2CdeliveryTab&lang=en&curr=USD | Requests additional CMS components | de.hybris.platform.cmsocc.controllers.ComponentController |
/occ/v2/electronics-spa/products/300938 | 300938?fields=code,name,summary,price(formattedVal…nfiguratorType,configurable,tags&lang=en&curr=USD | Requests additional fields for product code 300938 | de.hybris.platform.commercewebservices.core.v2.controller.ProductsController |
/occ/v2/electronics-spa/products/300938/references |
references?fields=DEFAULT%2Creferences(target(images(FULL))) &referenceType=SIMILAR&lang=en&curr=USD |
Requests product references for product code 300938 | de.hybris.platform.commercewebservices.core.v2.controller.ProductsController |
/occ/v2/electronics-spa/products/300938 | 300938?fields=classifications&lang=en&curr=USD | Retrieves classifications for the product code 300938 | de.hybris.platform.commercewebservices.core.v2.controller.ProductsController |
/occ/v2/electronics-spa/products/300938/reviews | reviews?lang=en&curr=USD | Retrieves customer reviews for this product | de.hybris.platform.commercewebservices.core.v2.controller.ProductsController |
Using the Augury Chrome Plugin you can see the resulting component hierarchy after the page has been built in the browser.
Starting the Migration
Now that you understand how Composable Storefront works on a technical level, you are ready to proceed with the migration.
Step 1 – Make an Inventory of your CMS Components and Pages
It is important to make an inventory of the pages and components used in your current storefront, like in the table below. For each page, list the controllers and custom CMS Components that are used, and for each component, figure out which data it needs to display or process. Some of the information needed might not be visible at first sight, such as dropdown boxes or pop-up windows.
You can filter requests to /pagescontentslotscomponents in SmartEdit while editing a given page in your existing storefront to retrieve the component details.
Components used in the Accelerator Product Details Page
0: {componentId: "SiteLogoComponent",…}
1: {componentId: "HomepageNavLink",…}
2: {componentId: "OrderComponent",…}
3: {componentId: "MiniCart",…}
4: {componentId: "ElectronicsCategoryNavComponent",…}
5: {componentId: "breadcrumbComponent",…}
6: {componentId: "TabPanelContainer",…}
7: {componentId: "FooterNavigationComponent",…}
8: {componentId: "MyAccountComponent",…}
9: {componentId: "MyCompanyComponent",…}
10: {componentId: "SearchBox",…}
11: {componentId: "VariantSelector",…}
12: {componentId: "AddToCart",…}
13: {componentId: "Similar",…}
14: {componentId: "CookieNotificationComponent",…}
15: {componentId: "AnonymousConsentManagementComponent",…}
16: {componentId: "AssistedServiceComponent",…}
17: {componentId: "ProfileTagScriptComponent",…}
18: {componentId: "PersonalizationScriptComponent",…}
19: {componentId: "BundleCarouselComponent",…}
You may find it helpful to make screenshots of your storefront and annotate the components, like so:
Out-of-the-box Composable Storefront supports almost all responsive B2C CMS components, so you will only need to focus on your custom components, as well as those coming from third party addons or marketplace extensions not covered in the standard library. Group and classify them according to the functional area, using a component inventory like the one below. Be aware that unlike the Accelerator Page approach, everything needs to be a component in Composable Storefront, so you might need to componentize parts of an existing Jakarta Server Page (JSP) layout.
The following queries give you an overview of the Components, PageTemplates and Pages used in your storefront:
Useful Flexible Search Queries
// Component list
select
{ct.code},
{c.id},
{ct.extensionName},
count(*) as cnt
from {
AbstractCMSComponent as acc
join ComposedType as ct on {ct.pk} = {acc.itemtype}
join CatalogVersion as cv on {cv.pk} = {acc.catalogversion}
join Catalog as c on {cv.catalog} = {c.pk}
}
where {c.id} LIKE '%ContentCatalog' and {cv.version} = 'Online'
group by {ct.code}, {cv.version}, {c.id},{ct.extensionName}
order by cnt desc
// Page Templates
select
{ct.code},
{c.id},
{pt.name},
{pt.frontendTemplateName}
from {
PageTemplate as pt
join ComposedType as ct on {ct.pk} = {pt.itemtype}
join CatalogVersion as cv on {cv.pk} = {pt.catalogversion}
join Catalog as c on {cv.catalog} = {c.pk}
}
where {c.id} LIKE '%ContentCatalog' and {cv.version} = 'Online'
order by {ct.code}
// Pages
select
{ct:code},
{c:id},
{ap:name[de]},
{ap:uid}
from {
AbstractPage as ap
join ComposedType as ct on {ct.pk} = {ap.itemtype}
join CatalogVersion as cv on {cv.pk} = {ap.catalogversion}
join Catalog as c on {cv.catalog} = {c.pk}
}
where {c.id} LIKE '%ContentCatalog' and {cv.version} = 'Online'
order by {ct.code}
// Pages, Components and Slots
select
{c.id},
{cv.version},
{p.uid} as "Page",
{pt.uid} as "Template",
{s4p.position} as "Template assigned position",
{st.uid} as "content slot id 4t",
{st.active} as "content slot 4t active",
{sn.templatePOS} as "pos",
{sn.name} as "template available position",
{comp.uid},
{compt.code},
{comp.visible}
from {
AbstractPage as p
join CatalogVersion as cv on {cv.pk} = {p.catalogVersion}
join Catalog as c on {c.pk} = {cv.catalog}
join PageTemplate as pt on {pt.pk} = {p.masterTemplate}
join ContentSlotForPage as s4p on {s4p.page} = {p.pk}
join ContentSlot as st on {st.pk} = {s4p.contentSlot}
left join ContentSlotName as sn on {sn.template} = {pt.pk} and {sn.name} = {s4p.position}
join ElementsForSlot as e2s on {st.pk} = {e2s.source}
join AbstractCMSComponent as comp on {comp.pk} = {e2s.target}
join ComposedType as compt on {compt.pk} = {comp.itemtype}
} where
{cv.version} = 'Online' and {c.id} like '%ContentCatalog'
order by {cv.version},{c.id},{p.uid},{sn.templatePOS},{comp.uid}
// Templates, components and slots
select
{c.id},
{cv.version},
{p.uid},
{pt.uid},
{s4t.position} as "template assigned position",
{st.uid} as "content slot id 4t",
{st.active} as "content slot 4t active",
{s4t.allowOverwrite} as "template allow overwrite",
{sn.templatePOS} as "pos",
{sn.name} as "template available position",
{comp.uid},
{compt.code},
{comp.visible}
from {
AbstractPage as p
join CatalogVersion as cv on {cv.pk} = {p.catalogVersion}
join Catalog as c on {c.pk} = {cv.catalog}
join PageTemplate as pt on {pt.pk} = {p.masterTemplate}
join ContentSlotForTemplate as s4t on {s4t.pageTemplate} = {pt.pk}
join ContentSlot as st on {st.pk} = {s4t.contentSlot}
left join ContentSlotName as sn on {sn.template} = {pt.pk} and {sn.name} = {s4t.position}
join ElementsForSlot as e2s on {st.pk} = {e2s.source}
join AbstractCMSComponent as comp on {comp.pk} = {e2s.target}
join ComposedType as compt on {compt.pk} = {comp.itemtype}
} where
{cv.version} = 'Online' and {c.id} like '%ContentCatalog'
order by {cv.version},{c.id},{p.uid},{sn.templatePOS},{comp.uid}
Component Inventory
ID
|
Page
|
Component
|
Implementation
|
Render in SSR
|
Nested
|
Data Needed
|
Notes
|
Component Screenshot
|
---|---|---|---|---|---|---|---|---|
10.1 | PDP | FinancingWidgetComponent | FinancingWidgetController | Yes | No | User, Cart, FinancingPreferences | Custom Layout | |
10.2 | PDP | PersonalizedRecommendationsComponent | PersonalizedRecommendationsController | No | No | RecommendationList | Recommendation Engine could be queried directly | |
10.3 | PLP | LastPurchases.jsp | JSP | Yes | No | Previous Orders | New Component needed |
In the standard accelerator, a PageController (based on AbstractPageController) prepares the context needed to render a page. In Composable Storefront most of this work is already performed by the framework, but it’s a good idea to manually check for logic that might need to be moved to a custom OCC extension or an individual component.
Step 2 – Perform a GAP Analysis
For each component, identify if there is a corresponding OCC API that provides the needed data (and corresponding Injectable Service), otherwise describe the OCC Extensions that will be needed. Also, set a development priority (or importance) for each component.
Component GAP Analysis
10.1 A PDP FinancingWidgetComponent Calculates Product finance options Customer, Cart, FinancingPreferences Cart, Users FinancingPreferences
10.2 B PDP PersonalizedRecommendationsComponent Using Analytics recommends best rated products RecommendationList N/A Recommendations
10.3 B PLP LastPurchases.jsp Displays the last 3 online purchases Previous Orders Orders
ID
|
Priority
|
Page
|
Component
|
Description
|
Data Needed
|
Existing OCC
|
Missing OCC
|
---|---|---|---|---|---|---|---|
10.1 | A | PDP | FinancingWidgetComponent | Calculates Product finance options | Customer, Cart, FinancingPreferences | Cart, Users | FinancingPreferences |
10.2 | B | PDP | PersonalizedRecommendationsComponent | Using Analytics recommends best rated products | RecommendationList | N/A | Recommendations |
10.3 | B | PLP | LastPurchases.jsp | Displays the last 3 online purchases | Previous Orders | Orders |
Step 3 – Start the API Implementation
Using an API First approach, create missing OCC Extensions and define the interfaces based on the semantics of existing OCC services. Start with empty (or mock) implementations as this will allow the front-end team to start work in parallel in the next step.
Use Swagger CodeGen to automatically generate the Typescript Angular client code needed in the front-end. Augment DTOs with necessary missing fields.
Step 4 – Implement the CMS Pages and Components
Create a base web content management system (WCMS) structure that duplicates your current storefront and launch your Composable Storefront application. Open your Console Tab in Chrome Developer Tools and you will see a warning for every CMS Component that does not have a corresponding Angular Component, there will also be a warning for the available CMS Slots.
Verify that this information matches your CMS Component Inventory
Composable Storefront
In the headless storefront the front-end is loaded on the browser, and the page structure and layout is retrieved from the server (unless it has a static layout). The Composable Storefront Components (see Modules and Components above) are used to build the page on the client side, and they execute OCC Calls to the server (see OCC APIs above) to retrieve data needed for rendering. The initial page might, however, be built initially on the Server (using a technology known as Server Side Rendering – SSR), for performance and SEO purposes. State is kept on the Client side.
Proceed to re-implement the new CMS components and Pages in Angular. Use the CMS Component Schematic add-cms-component command to generate skeleton classes (see also: Creating Pages and Components and Create a new page in Spartacus ) and add your custom logic.
Conclusion
This article provided a more technical introduction to Composable Storefront and the techniques needed to migrate an existing accelerator storefront. A migration can involve a significant amount of re-engineering but there are tangible performance and maintenance benefits that make it worth it. Careful preparation is key to a successful migration.
If you have any questions, please do not hesitate to reach out to sapcx-services@sap.com . If you have technical questions as you develop on Composable Storefront we encourage you post your question to StackOverflow with the spartacus-storefront tag.