
Hello Sitecore developers, what a wonderful time we are in. As a developer you now have a smorgasbord of lovely sandwiches and delicacies 🙂 You can go all in with Headerless and use Sitecore JSS with NextJs(React). Or go for classic MVC with Asp.Net Core. And best of all, it’s all Docker containers. This means an easy setup on your developer machine, and an easy deploy to production (using Kubernetes). What a wonderful time we are living in 🙂
I recently had the chance to play a wonderful game – Alien: Isolation. If you are an Aliens movie fan, you must play Alien Isolation and of course the addon – Alien: Isolation – Last Survivor. What a game!

The game is so good that I just had to write a blog post about handling json configs in your Sitecore Commerce Solution using PlaceholderFor.
So what is PlaceholderFor? If you look in your json configs in Sitecore.Commerce.Engine, say the config.json or the policy configs in data\Environments.
Example from the config.json
{
"AppSettings": {
"DeploymentId": "PlaceholderForDeploymentId",
"EnvironmentName": "PlaceholderForEnvironmentName"
}
}
Example of the Plugin.SQL.PolicySet-1.0.0.json
{
"$type": "Sitecore.Commerce.Core.PolicySet, Sitecore.Commerce.Core",
"Id": "Entity-PolicySet-SqlPolicySet",
"Version": 1,
"IsPersisted": false,
"Name": "SqlPolicySet",
"Policies": {
"$type": "System.Collections.Generic.List`1[[Sitecore.Commerce.Core.Policy, Sitecore.Commerce.Core]], mscorlib",
"$values": [
{
"$type": "Sitecore.Commerce.Plugin.SQL.EntityStoreSqlPolicy, Sitecore.Commerce.Plugin.SQL",
"TrustedConnection": "PlaceholderForSharedTrustedConnection",
"UserName": "PlaceholderForSharedDatabaseUserName",
"Password": "PlaceholderForSharedDatabasePassword",
"Server": "PlaceholderForSharedDatabaseServer",
"Database": "PlaceholderForSharedDatabaseName",
"AdditionalParameters": "",
"ConnectTimeout": 20,
"CommandTimeout": 180,
"CleanEnvironmentCommandTimeout": 120000
}
]
}
}
Notice the PlaceholderForXXXXX, and it does exactly what it says.
The placeholder will be replaced by the environment variables you have set in your docker compose file/and in your Kubernetes specs(hopefully using secrets). This is a genius and a wonderful way to change configs. Without having to rebuild images every time you do a change…
I took this for granted, but recently, a variable needed to be changed depending on whether it’s in test or in production. In this case, it was a base URL for a rest API and a setting in a custom policy config.
At first I had the values “hardcoded” in config.json and my MyCustom.PolicySet-1.0.0.json.
In config.json
"MySolution": {
"SomeBaseAddress": "https://abaseurl"
}
And used in code, like this:
public class ConfigureSitecore : IConfigureSitecore
{
private readonly IConfiguration _configuration;
public ConfigureSitecore(IConfiguration configuration)
{
_configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.Sitecore().Services.AddHttpClient<IMyService, MyService>(client =>
{
client.BaseAddress = new System.Uri(_configuration.GetSection("MySolution:SomeBaseAddress").Value);
});
....
A bunch of pipeline configurations
....
}
}
In MyCustom.PolicySet-1.0.0.json.
{
"$type": "Sitecore.Commerce.Core.PolicySet, Sitecore.Commerce.Core",
"Id": "Entity-PolicySet-MyCustomPolicySet",
"Version": 1,
"IsPersisted": false,
"Name": "MyCustomPolicySet",
"Policies": {
"$type": "System.Collections.Generic.List`1[[Sitecore.Commerce.Core.Policy, Sitecore.Commerce.Core]], mscorlib",
"$values": [
{
"$type": "MySolution.Commerce.Plugin.MyPlugin.Policies.MyCustomPolicy, MySolution.Commerce.Plugin.MyPlugin",
"AVariableThatNeedsToBeChangeIfItIsInTestOrInProduction": "ChangeMePlease"
}
]
}
}
And used in code, like this:
private async Task<Order> DoSomeChanges(Order order)
{
var myCustomPolicy = CommercePipelineExecutionContext.GetPolicy<MyCustomPolicy>();
order.GetComponent<ACustomComponentThatShowSomething>().Message = myCustomPolicy.AVariableThatNeedsToBeChangeIfItIsInTestOrInProduction;
Some code here...
}
For config.json I could go for the “a json for each environment”, like config.Development.json and so on. But for the MyCustom.PolicySet-1.0.0.json, that was indeed trickier. So what to do?
PlaceHolderFor to the rescue 🙂
First out is the config.json, it will now look like this, notice that the value has changed to PlaceholderFor and the variable name.
"MySolution": {
"SomeBaseAddress": "PlaceholderForSomeBaseAddress"
}
And MyCustom.PolicySet-1.0.0.json will now look like this, notice that the value has changed to PlaceholderFor and the variable name.
{
"$type": "Sitecore.Commerce.Core.PolicySet, Sitecore.Commerce.Core",
"Id": "Entity-PolicySet-MyCustomPolicySet",
"Version": 1,
"IsPersisted": false,
"Name": "MyCustomPolicySet",
"Policies": {
"$type": "System.Collections.Generic.List`1[[Sitecore.Commerce.Core.Policy, Sitecore.Commerce.Core]], mscorlib",
"$values": [
{
"$type": "MySolution.Commerce.Plugin.MyPlugin.Policies.MyCustomPolicy, MySolution.Commerce.Plugin.MyPlugin",
"AVariableThatNeedsToBeChangeIfItIsInTestOrInProduction": "PlaceholderForAVariableThatNeedsToBeChangeIfItIsInTestOrInProduction"
}
]
}
}
And now to the crème de la crème(where we set the variable values).
The docker-compose.yml for the devs.
(The values should be set in the .env file)
engine-shops:
isolation: ${ISOLATION}
image: ${XC_SITECORE_DOCKER_REGISTRY}sitecore-xc-engine:${XC_PACKAGES_TAG}
depends_on:
mssql:
condition: service_healthy
solr:
condition: service_started
id:
condition: service_started
redis:
condition: service_started
cm:
condition: service_started
environment:
COMMERCEENGINE_MySolution__SomeBaseAddress: "https://baseaddresstest.com"
COMMERCEENGINE_AVariableThatNeedsToBeChangeIfItIsInTestOrInProduction: "You are now running in test"
And in production, the spec for engine-shops.yaml (in Kubernetes):
(The values should be set as secrets)
apiVersion: v1
kind: Service
metadata:
name: engine-shops
spec:
selector:
app: engine-shops
ports:
- protocol: TCP
port: 5000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: engine-shops
labels:
app: engine-shops
spec:
replicas: 1
selector:
matchLabels:
app: engine-shops
template:
metadata:
labels:
app: engine-shops
spec:
nodeSelector:
kubernetes.io/os: windows
containers:
- name: sitecore-xc-engine
image: {registry}/{sxc-project}/sitecore-xc-engine:{commerce-version}
ports:
- containerPort: 5000
env:
- name: COMMERCEENGINE_MySolution__SomeBaseAddress
value: https://baseaddressprod.com
- name: COMMERCEENGINE_AVariableThatNeedsToBeChangeIfItIsInTestOrInProduction
value: You are now running in prod
This means that the values we set in docker-compose.yml and in engine-shops.yaml, will replace the “PlaceholderForSomeBaseAddress” and “PlaceholderForAVariableThatNeedsToBeChangeIfItIsInTestOrInProduction” in the configs.
How cool is that!
I really like this approach because you change the variable values(in the configs) when you build the containers/pods. Meaning you don’t have to build new images every time there is a change.
That’s all for now folks 🙂
One thought on “Handle your json configs in your Sitecore Commerce Solution like Ripley(in Alien)”