Migrating an Accelerator Storefront to Composable: The Inventory-Driven Method
A migration method that converts "rewrite the frontend" into a checklist: FlexibleSearch queries that inventory your CMS estate, component gap analysis, API-first OCC development, and the console-warning-driven implementation loop.
"Migrate the storefront" is not a plan; it is a wish. The migrations that land on schedule all share one property: before anyone wrote Angular, someone produced a complete inventory of what the current storefront actually is (every page, template, slot, and component, with its data needs) and converted the rewrite into a tracked checklist. This guide is that method, distilled from the CX Works original and updated for the composable storefront era, with the accelerator's Q3 2027 removal date (see the storefront strategy guide) supplying the deadline the original never had.
Migrate or Rebuild?#
The recommendation has always been to treat the move as a chance to rethink the experience, not to transliterate JSP into Angular pixel by pixel. But even when the mandate is "same experience, new stack", run the inventory below first without implementing anything: the resulting numbers (components total, custom components, missing APIs) are the honest basis for choosing between migration and greenfield, and producing them costs days, not months.
One operational warning that survives from the original: running accelerator and composable storefronts in parallel is possible and useful for de-risking cutover, but keep the window short. Two storefronts on two stacks means double testing, double content verification, and customers bouncing between inconsistent experiences on shared sessions. Parallel run is a cutover technique, not an operating model.
Prerequisites#
- A platform version where the OCC APIs are ordinary extensions rather than AddOns, with any legacy OCC AddOns converted. (On 2211 this is a given; on stragglers it is the first work item.) The standard
commercewebservicesOCC is the supported surface; the old template extensions are sunset with the accelerator. - The composable storefront release notes and feature matrix reviewed against your storefront's feature list, so parity gaps surface in planning.
- A working CCv2 pipeline including the JavaScript storefront build (
js-storefront/, per the first-deployment guide), and the composable app scaffolded from libraries (never forked; see the getting-started guide). - Teams staffed for the real split: Angular/RxJS on one side, OCC extension development on the other, with the OCC contract co-owned.
The Architectural Translation#
What actually happens to your code: the accelerator's PageController layer dissolves. Its responsibilities split three ways: page structure moves to the CMS page/slot/component data (already in your database), rendering logic moves into Angular components, and data assembly moves into OCC endpoints. Facades and services underneath mostly survive intact, now serving OCC instead of JSP models: this is why accelerator projects with fat controllers migrate worse than ones that kept logic in facades, and why the code review playbook has been telling you to keep controllers thin for years.
Two structural differences to internalize before estimating:
- Everything is a component. The accelerator tolerated layout living in JSP fragments outside any CMS component; composable does not. Every such fragment (that hardcoded promo strip in the footer JSP) must be componentized: given a CMS component type and an Angular implementation. The inventory queries below will not find these; a manual pass over the JSP templates will, so schedule one.
- Check the page controllers for hidden logic.
AbstractPageControllersubclasses often accumulated context preparation that the framework now handles, plus genuine business logic that must move to an OCC extension or a component. Read each one during inventory; the ones that are pure boilerplate delete, the ones that are not become API work items.
Content catalog changes are also part of the scope: composable expects its own sample-data-style page structure (different templates and slot names than the accelerator's), and your migration includes mapping or rebuilding the content catalog accordingly.
Step 1: Inventory the CMS Estate#
The database already knows what your storefront is. Four FlexibleSearch queries extract it (run against the Online version of your content catalog):
-- Component types in use, by frequency: your workload, ranked
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 (frontendTemplateName shows the JSP binding being retired)
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'
plus the analogous queries for pages, and the two join-heavy ones mapping components to slots to pages and components to slots to templates (page-assigned versus template-assigned slots migrate differently, since template slots repeat across every page using the template). The full set is in the queries above extended over ContentSlotForPage/ContentSlotForTemplate, ElementsForSlot, and ContentSlotName.
The extensionName column does the triage for you: components from platform extensions are almost certainly covered by the standard libraries (composable ships nearly all responsive B2C components, and B2B coverage matured years ago); components from your extensions and from third-party addons are the actual migration workload.
Capture the result as a living component inventory:
| ID | Page | Component | Current implementation | SSR needed | Data needed | Notes |
|---|---|---|---|---|---|---|
| 10.1 | PDP | FinancingWidgetComponent | FinancingWidgetController | Yes | User, cart, financing prefs | Custom layout |
| 10.2 | PDP | PersonalizedRecommendationsComponent | Controller + engine call | No | Recommendation list | Engine could be queried directly from the client |
| 10.3 | PLP | LastPurchases.jsp | Raw JSP fragment | Yes | Previous orders | Needs componentization first |
The SSR column earns its place: components that must render server-side (SEO-relevant, above the fold) carry the no-window constraint and deserve earlier, more careful implementation.
Step 2: Gap Analysis#
For each inventory row, answer one question: does an OCC endpoint already serve this component's data? The Swagger UI of your installation is the checklist. The output extends the inventory:
| ID | Priority | Component | Data needed | Existing OCC | Missing OCC |
|---|---|---|---|---|---|
| 10.1 | A | FinancingWidgetComponent | Customer, cart, financing prefs | Cart, Users | FinancingPreferences |
| 10.2 | B | PersonalizedRecommendationsComponent | Recommendation list | none | Recommendations (or direct engine call) |
| 10.3 | B | LastPurchases | Previous orders | Orders | none |
Prioritize by page criticality and revenue path, not by implementation ease; the checkout-adjacent components are the ones whose delay moves the cutover date. Note also the 10.2 pattern: some data that flowed through commerce controllers only because the accelerator lived server-side can now go straight from the browser to its source system, removing commerce from the loop entirely. Each such case is scope you get to delete.
Step 3: API First#
Build the missing OCC surface before the components that need it:
- Create the custom OCC extension(s) and define the endpoints' contracts, modeled on the semantics of the standard OCC services (pagination,
fieldssupport, error shapes), so your API feels native to the framework's data-loading patterns. - Ship them with mock implementations immediately. The contract existing, even returning canned data, unblocks the frontend team completely; the real service-layer wiring proceeds in parallel. This single sequencing decision is the difference between the teams pipelining and the frontend team idling behind backend sprints.
- Generate the TypeScript client types from the OpenAPI definitions rather than hand-writing them, and extend DTOs where the standard ones lack fields.
- Put the new contracts under the same regression discipline as the standard ones from day one (the OCC contract-hardening guide applies to your custom surface doubly, since no SAP release notes will ever warn you about your own drift).
Step 4: Implement, Warning by Warning#
The implementation loop is pleasingly mechanical:
- Stand up the composable app against a content catalog structured for it (base WCMS structure duplicating your storefront's pages).
- Open the browser console. The framework logs a warning for every CMS component type on the page with no Angular mapping, and lists the available slots. This console output is your progress tracker; reconcile it against the Step 1 inventory and burn it down.
- Per component: generate the skeleton with the CMS component schematic (
ng g ...:add-cms-component, wiring theCmsConfigmapping, module, and component in one step), then implement the template and logic against the OCC services from Step 3. - Componentize the JSP-fragment stragglers as you reach them: CMS component type in items.xml/ImpEx, content in the catalog, Angular implementation, mapping.
Work page by page in priority order (homepage, PDP, PLP, cart, checkout), keeping each page shippable behind the parallel-run door. The checkout deserves its dedicated test pass across every payment and express-checkout path, for the same reasons the analytics guide gives: it is where the flows fork.
Cutover#
With the inventory at zero warnings and the regression suites green on both sides of the contract: DNS/routing switches the storefront endpoint to the JS storefront, the accelerator aspect stays deployed but dark for the agreed fallback window, and then, the step teams forget, the accelerator webapp and its addons come out of the manifest and localextensions.xml, shrinking every future build and closing the door the deprecation clock was holding open. The content catalog, the facades, the services, and the platform underneath did not notice the frontend changed, which was the entire point of the architecture you just migrated to.