Venture Internationalization with Yves & Zed

Fabian Wesner
Fabian Wesner CTO Spryker
27. February 2014 in

Technology

We deal with shops in multiple countries, facing a great number of country-specific requirements, especially from an architectural point of view. Today we want to introduce two entirely different approaches on handling this challenge, including our philosophy on prudent data management.

Architectural Demands

Most of our e-commerce ventures have shops in multiple countries. For example, online tire retailer Tirendo is present in Germany, France, Italy, Switzerland, Sweden, Holland, Poland and a few other countries. Similarly, nutrition and supplement website nu3 is present in Germany, Austria, Switzerland, Brazil with an international store that ships to the whole of Europe. In this article we will explain how we tackled the issue of venture internationalization through Yves & Zed.

Adapting a shop system to local specifications and languages is always difficult from an architectural point of view. This is because there are usually a lot of requirements that only apply to a specific country, and also most of the features affect both the front end and the business layer of the software. For example:

  • Brazil requires implementing the Boleto payment method, and in the Netherlands Ideal is needed.
  • In Russia cash on delivery is the most common payment method, and it’s also considered best practice to call the customer before shipping.
  • In Arab countries we need a right to left website layout.
  • There may be different fulfillment providers for Germany and France.

Two Approaches

We considered two basic approaches to implement internationalization:

1. Copy the Whole Code for Every Country

To launch a new country, a fork is created by copying the full code into another repository. This works well in cases when there is only a loose organizational and operational coupling between local shops. For instance at nu3, the Brazilian shop has a separate code pool from their European counterparts, because the difference between doing e-commerce in Brazil and Europe is so huge that we don’t really use a shared feature backlog.

This approach has some obvious disadvantages. For example, the disconnected code pools will diverge very quickly and high efforts need to be made to share newly developed features among the countries. Furthermore, different code pools usually require different development teams, which adds cost.

2. Same Code Base for All Countries with Switches (Stores)

When local shops are similar enough, we think it’s a good idea to maintain a single code base and to add switches to the code for country-specific behavior. All new features are immediately available in all countries, but we are still able to implement localized features and use different hosting locations.

To avoid spaghetti code with thousands of switches across the code, it is essential to have a pragmatical architectural solution. We base our approach around the concept of a ‘store’, which is defined as an organizational unit of the company. A store can be a country-specific shop, but also a white label shop within the same country, or an international shop that serves a bigger region.

Technically it works like this. The web server detects the store name (e.g. ‘DE’) from the URL and forwards it to Yves & Zed as an environment variable. Inside of Yves & Zed we always use factories to initiate new classes. These factories either return an instance of a store-specific class when available, or an instance of a general class. To create store-specific models, controllers and views, store-specific classes are derived from general classes and suffixed with the store name. The factory will use them automatically.

General model for catalog:

class Catalog {}

Localized class for Germany:

class CatalogDE extends Catalog {}

The factory returns an instance of CatalogDE in the German store only, and otherwise of Catalog:

$factory->getCatalog();

This approach comes with a drawback. Before every deployment to production we need to test the functionality of all stores, but we think the added effort that comes with store-specific classes is easily outweighed by the ability to automatically share all other features across stores.

What about the Data?

Stores share code, but not data. We use one dedicated database per store to avoid the high complexity that comes from mixed currencies and different languages. Another benefit is the possibility to use separated hosting providers for different stores.

In some cases we still need to share data. For the most cases we make use of a queue to broadcast information. For example we do that in our CMS, where newly created pages need to appear in all stores. Another approach is to use a centralized application that connects the stores. This approach works well for a shared inventory when we need to synchronize the stocks among all stores.

Nevertheless, the databases of all stores use the same schema. From a technical perspective we could easily allow country-specific schemas, but the result would be a very high number of store-specific classes. For instance if we would add a column ‘cpf’ to the customer table just for Brazil, then we would need to respect that in all layers of the application and we would have the risk of naming collisions. Therefore it is better to find a schema that is used by all stores and to accept unused / empty columns in some stores.

Of course it would be cumbersome if we would have to look into all stores individually to assess the overall performance of the company, therefore our integrated data warehouse has means to aggregate data from different store databases into consolidated reporting.

 

Copyright 2014 Project A Ventures | All code in this post is licensed under the MIT License unless otherwise declared.

Still got questions?
Ask the author for further information.