CCv2 Aspects, Clustering, and Scaling: Designing Inside a Fixed Footprint
How the SAP Commerce Cloud cluster is actually shaped: the aspect model and what runs where, why backgroundProcessing has a fixed resource budget and how to write bulk jobs that respect it, self-service scaling, and the decision framework for what lives outside the subscription.
On-premise SAP Commerce let you shape the cluster to your habits: as many node groups as you liked, JVMs sized per workload, whatever third-party binaries the ops team would tolerate on the boxes. CCv2 inverts that. The cluster shape is a product decision expressed through aspects, the resource footprint is part of your subscription, and anything that does not survive ant all inside the build process does not run inside the subscription at all. Teams that internalize this early design clean solutions; teams that fight it ship cronjobs that fall over at production scale.
The Aspect Model: What Runs Where#
An aspect is a class of nodes with a defined role, its own service properties, and its own share of the environment's resources. The working set:
| Aspect | Runs | Notes |
|---|---|---|
accstorefront | Accelerator storefront traffic | Public-facing; scaled for customer load |
api | OCC REST, other web services | The composable storefront's backend surface; SmartEdit also distributes here |
backoffice | Backoffice, admin UIs | Manually triggered cronjobs run on the node that triggered them |
backgroundProcessing | Automatic cronjobs, imports, sync, business processes | Fixed footprint; the aspect everything heavy lands on |
admin | Platform update and initialization runs | Appears during deployments |
Three placement facts with daily consequences:
- Automatically triggered cronjobs run on
backgroundProcessing. A trigger firing at 02:00 lands there regardless of what you imagined. Size your jobs for that aspect's budget. - Manually triggered jobs run where you clicked. Trigger a full catalog sync from Backoffice and it executes on the
backofficenode, competing with every editor's session. Train the ops team: heavy jobs get triggered via a trigger or moved explicitly, not run ad hoc from the admin UI. Node groups on cronjobs (nodeGroupon the job, matched in the aspect's service properties) give you deterministic placement. - hAC is everywhere, SmartEdit is on
backofficeandapi. When something "works on one node and not another", check which aspect you are actually on; the per-aspect property files in the manifest are the usual culprit.
Aspects are configured in manifest.json: per-aspect properties, webapps included per aspect, and context roots. The catalog of what you can tune per aspect is the same properties mechanism the rest of the platform uses, so environment parity problems are usually manifest problems.
"aspects": [
{
"name": "backgroundProcessing",
"properties": [
{ "key": "cronjob.timertask.loadonstartup", "value": "true" },
{ "key": "cluster.node.groups", "value": "integration,batch" }
],
"webapps": [
{ "name": "hac", "contextPath": "/hac" }
]
}
]
The Fixed Footprint Rule and Bulk Job Design#
The backgroundProcessing aspect has a resource budget you cannot lean on at 2 a.m. There is no "give this one job a bigger JVM" lever. The CX Works era guidance remains the law of the land, and it is worth stating as engineering rules rather than advice:
- Stream, never slurp. A price import that loads the full file into memory works in dev with 5,000 rows and dies in production with a 400 MB file. Read in batches, release references, keep the memory profile flat across the run. The platform's
ImportServicewith ImpEx already behaves this way; hand-rolledreadAllLines()jobs do not. - Batch writes and clear the session cache. Long-running jobs that touch millions of items must call
modelService.detachAll()(or work through the raw Jalo-free batch APIs) on an interval, or the session-level cache grows until the node stops answering health checks and Kubernetes recycles it mid-job. - Make jobs resumable and abortable. A recycled pod is normal cloud weather, not an outage. Jobs that track progress (last processed PK, file offset in the cronjob's own model) restart cleanly; jobs that assume a single uninterrupted run corrupt their own state. Implement
isAbortable()honestly. - Distribute what is genuinely big. Distributed ImpEx and the ServiceLayer job splitting patterns exist precisely so a bulk task can spread across the aspect instead of betting everything on one node's heap.
Migrating teams should audit every existing bulk job against these rules before the first production-scale dataset arrives, because that is the exact moment the difference between on-premise habits and CCv2 reality bills you.
Scaling: What You Control and What SAP Does#
Scaling in CCv2 has two layers. Underneath, a scale operator watches for pods that cannot be scheduled and grows the cluster with new worker VMs; that machinery is SAP's business and invisible to you. At your layer, the Cloud Portal's scaling of service resources lets you adjust how much capacity a service gets within your subscription's entitlement, and your commercial agreement defines the ceiling.
The architectural consequences:
- Design for horizontal, not vertical. You can get more nodes; you cannot get a meaningfully fatter single node on demand. Anything that only scales by "more heap on one box" (giant in-memory caches, single-threaded imports, session-pinned state) is a design smell here.
- Storefront scale-out is the easy dimension. Stateless-ish web traffic spreads; that is what the platform is built for. The hard dimension is
backgroundProcessing, where more nodes only help if your jobs actually distribute (see above). - Peak events are a process, not a button. The high-traffic guide covers the full drill; the architecture-level point is that capacity conversations with SAP happen before Black Friday, and your job is to arrive with load-test evidence, not a hunch.
Inside or Outside: The Hosting Decision Framework#
The build process is fixed: your code, built by the platform's standard targets, plus the packages the manifest declares. If a component needs its own server, daemon, binary installation, or deployment lifecycle, it lives outside the subscription. Common calls:
- CI/CD: outside, always. The portal builds what you push, but pipelines with per-commit builds, test stages, and quality gates are your own infrastructure driving the Commerce Cloud APIs (the continuous delivery guide covers the wiring). Latency between your CI and your Git host matters more than proximity to Commerce.
- Image processing: the platform's media conversion service (
cloudmediaconversionin the manifest) covers the standard resize-and-format pipeline that used to require ImageMagick on the box. Needs beyond it (AI cropping, DAM workflows) are a third-party service. - Email: there is no SMTP server in the subscription. Commerce is an SMTP client; you bring the service (the transactional email and SES guides cover the options and the deliverability homework).
- Custom applications: anything with its own runtime goes to your own Azure subscription, ideally in the same region as your Commerce environment to keep synchronous call latency in single-digit milliseconds. Asynchronous integrations tolerate distance; checkout-path calls do not. If the component is an event consumer or a lightweight function, BTP and Kyma (see the event-driven extensions guide) are the platform-native answer.
The pros and cons table from the original CX Works material still summarizes it: external hosting buys you scalability and takes CPU pressure off the Commerce aspects, and costs you latency plus infrastructure you now operate.
Cache Coherence in a Cluster You Do Not Operate#
Every node keeps its own region caches, and invalidation messages keep them coherent; that machinery is platform-provided and works without your attention. What is yours: sizing region caches relative to a heap you do not choose. Fixed maxEntries values tuned for your old on-premise 32 GB JVM are wrong on every CCv2 node size at once. Express cache regions relative to the runtime instead, as the persistence and caching guide details (the SpEL Runtime.maxMemory() pattern), so one configuration is correct on any node the platform hands you.
The Checklist#
- Every cronjob assigned to an explicit node group; no heavy job triggerable ad hoc from Backoffice without a documented reason
- Bulk jobs audited: streaming reads, batched writes with periodic detach, abortable, resumable after pod recycle
- Manifest aspects reviewed: per-aspect properties intentional, hAC/SmartEdit placement understood
- Region cache sizes expressed relative to heap, not absolute
- Third-party components mapped inside/outside with latency budgets; synchronous dependencies co-located in-region
- Peak capacity plan agreed with SAP ahead of the season, backed by load tests
The fixed footprint is not a limitation to escape; it is the contract that lets SAP run thousands of these clusters reliably. Design your workloads to be good tenants and the platform is genuinely boring to operate. Fight it and every quarter's peak event becomes a war story.