HowTo - Set up multiple stores

Edit on GitHub
Cloud environment restrictions

Currently, Spryker Cloud Commerce OS does not support all the multi-store configuration options, like creation of different regions. Soon we are going to publish the documentaion on all the available configuraiton options for the cloud environment. In the meantime, if you want to set up multiple stores, contact support to learn about available configuration options.

With the Spryker Commerce OS, you can create multiple stores for different scenarios per your business requirements. The multi-store setup is very versatile and customizable. For example, you can:

  • Build one store for multiple countries and languages or separate stores for each region.

  • Make abstract products, discounts, and other logic and code shared between stores or create a dedicated setup for each of them.

  • Define separate search preferences to create an entirely different set of rankings, rules, and settings per store. For example, a date format or a currency.

  • Set up a default store.

Multi-store setup infrastructure options

Multi-store setup 1: Database, search engine, and key-value storage are shared between stores.

multi-store setup 1

Due to the resources being shared, the infrastructure costs are low. This setup is most suitable for B2C projects with low traffic and small amount of data like products and prices.

Multi-store setup 2: Each store has a dedicated search engine and key-value storage while the database is shared.

multi-store setup 2

This setup is most suitable for B2B projects with high traffic and a big amount of data.

Multi-store setup 3: Each store has a dedicated database, a search engine, and a key-value storage.

multi-store setup 3

This setup is most suitable for the projects with the following requirements:

  • Completely different business requirements per store, like business logic and features.

  • Independent maintenance and development flow.

  • Separated data management for entities like products, customers, and orders.

  • On-demand setup of any type of environment per store, like test, staging, or production.

It’s the most expensive but flexible option in terms of per-store scaling and performance.

Setting up multiple stores

To set up multiple stores, do the following.

Configuring code buckets

Code buckets provide an easy way to execute different business logics in runtime based on different HTTP or console command requests. To configure code buckets, see Code buckets.

Configuring stores

To configure stores, do the following.

1. Define the stores

Define the desired stores in config/Shared/stores.php. In the example below, we define DE and AT stores:

config/Shared/stores.php
<?php

$stores = [];

$stores['DE'] = [
    'contexts' => [
        '*' => [
            'timezone' => 'Europe/Berlin',
            'dateFormat' => [
                'short' => 'd/m/Y',
                'medium' => 'd. M Y',
                'rfc' => 'r',
                'datetime' => 'Y-m-d H:i:s',
            ],
        ],
        'yves' => [],
        'zed' => [
            'dateFormat' => [
                'short' => 'Y-m-d',
            ],
        ],
    ],
    // locales configuration
    'locales' => [
         'en' => 'en_US',
         'de' => 'de_DE',
    ],
    // country and currency configuration
    'countries' => ['DE'],
    'currencyIsoCode' => 'EUR',
    // Queue pool is the mechanism which sends messages to several queues.
    'queuePools' => [
        'synchronizationPool' => [
            'AT-connection',
            'DE-connection',
        ],
    ],
    'storesWithSharedPersistence' => ['AT'],
];

$stores['AT'] = [
    'contexts' => [
        '*' => [
            'timezone' => 'Europe/Vienna',
            'dateFormat' => [
                'short' => 'd/m/Y',
                'medium' => 'd. M Y',
                'rfc' => 'r',
                'datetime' => 'Y-m-d H:i:s',
            ],
        ],
        'yves' => [],
        'zed' => [
            'dateFormat' => [
                'short' => 'Y-m-d',
            ],
        ],
    ],
    'locales' => [
         'en' => 'en_US',
         'de' => 'de_AT',
    ],
    'countries' => ['AT'],
    'currencyIsoCode' => 'EUR',
];

return $stores;

2. Optional: Define store-specific configuration

  1. For one or more stores you’ve defined in config/Shared/stores.php, define separate store-specific configuration. For example, config/Shared/config-default_docker_de.php is the configuration file for the DE store in the docker environment.

  2. To apply the defined store-specific configuration, adjust the related deploy file in the environment: section. In the following example, the docker_de environment name points to the config/Shared/config-default_docker_de.php store-specific configuration file. For more information about this deploy file parameter, see environment:.

....

environment: docker_de

image:
  tag:

3. Define the default store

Define the default store in config/Shared/default_store.php. In the following example, we define DE as the default store.

<?php

return 'DE';
...

4. Adjust data import configuration

Adjust all the import files and import configuration to import data for the stores you’ve added. For example, define the import source for the DE store you’ve added:

#2. Catalog Setup data import
...
- data_entity: product-price
  source: data/import/common/DE/product_price.csv
...   

5. Configure installation recipes

Add the new stores to the installation recipes in config/install/* as follows:

...

stores:
    ...
    - {STORE_NAME}
...    

For example, to add the AT and DE stores, we adjust an installation recipe as follows:

...

stores:
    ...
    - DE
    - AT
...    

Configuring the deploy file

According to the desired infrastructure setup, configure the deploy file for the multi-store setup. In the example below, we configure the multi-store setup 1: database, search engine, and key-value storage are shared:

Deploy file configuration for the multi-store setup 1
......

regions:
    EU:
        services:
            mail:
                sender:
                    name: No-Reply
                    email: no-reply@example.com
            database:
                database: database-name
                username: database-username
                password: database-password
        stores:
            DE:
                services:
                    broker:
                        namespace: de_queue
                    key_value_store:
                        namespace: 1
                    search:
                        namespace: de_search
                    session:
                        namespace: 2
            AT:
                services:
                    broker:
                        namespace: at_queue
                    key_value_store:
                        namespace: 3
                    search:
                        namespace: at_search
                    session:
                        namespace: 4
....                        

We use the following configuration parameters in this example:

  • Region defines one or more isolated instances of the Spryker applications that have only one persistent database to work with. Visibility of the project’s Stores is limited to operate only with the Stores that belong to a Region. Region refers to geographical terms like data centers, regions, and continents in the real world.

  • Store defines the list of Stores and store-specific settings for Services.

For more information about deploy file configuration, see Deploy file reference - 1.0.

Defining the store context

You can define stores by domains or by headers. We recommend defining stores by domains, which is supported by default.

Defining stores by headers is not supported by default, but you can use the following workaround.

The workaround is only supported by the multi-store store setup 1, when all the resources are shared. With the other setup, you need to manage the infrastructure configuration on the application level.

public/Glue/index.php

<?php

...
require_once APPLICATION_ROOT_DIR . '/vendor/autoload.php';

// Add this block
if (isset($_SERVER['HTTP_APPLICATION_STORE'])) {
    putenv('APPLICATION_STORE=' . $_SERVER['HTTP_APPLICATION_STORE']);
}

Environment::initialize();

...

To check if the workaround works, in the browser console, run the following:

fetch("http://{domain-name}/catalog-search", {
  "headers": {
    "upgrade-insecure-requests": "1",
    "application-store": "MY_STORE"
  },
  "referrerPolicy": "strict-origin-when-cross-origin",
  "body": null,
  "method": "GET",
  "mode": "cors",
  "credentials": "omit"
}).then(r => console.log(r.text()));

You’ve successfully added the stores and can access them according to how you’ve defined their context.