Scaling Multi-Channel E-Commerce with Microservices and a Headless CMS
Tags: microservices, webdev
Published: 2021-07-30 20:43:56

Having a multi-channel e-commerce solution helps you reach new customers by giving you distribution on a variety of channels. Brands and retailers can now sell through traditional catalogs and POS, online stores, third-party sellers, and even social media.

From a technical perspective, supporting a multi-channel e-commerce workflow isn’t easy. Doing it well demands you take the right technical approach and design an architecture that can handle rapid change without limiting your potential for growth.

Microservices, APIs, and a headless CMS can form the basis for solving this problem. In this guide, I’ll explain the benefits of using this kind of architecture, and I’ll share some examples of how such a system works in practice, with workflows and code samples.

## What is Multi-Channel E-Commerce?

Multi-channel e-commerce is selling your products in more than one place online. We usually think of online stores when considering e-commerce, but there are plenty of other ways to reach customers.

Apps, social media, and marketplaces such as Amazon that allow third-party sellers are all good places to sell goods online. Tablets in physical locations, such as stores or trade events, are also popular.

Selling in multiple channels brings new challenges. Your backend infrastructure needs to support everything it’s connected to, and your developers need to be ready to handle the extra work of building and supporting these channels.

## What is a Headless CMS?

A headless CMS is a content management system that keeps the [backend independent](https://resources.fabric.inc/blog/glossary/headless-cms) from frontend delivery channels.

The backend makes its services available to these channels via APIs. These all use the same underlying data, so things like product or customer information can stay the same across all of them.

For example, if you have a product available on both Amazon and Google Shopping, your headless API can route its pricing data from a common source like a PIM, meaning the price, product descriptions, and images are identical on both channels.

## What are Microservices?

Microservices are encapsulated services that each have their own interface. They can be updated and modified without affecting other services. If the interface stays consistent, internal changes shouldn’t affect other components.

For example, if you want to upgrade the security on your orders, that’s easier if your orders are handled by a set of microservices. If you were tied into a monolith, such as Oracle ATG, you’d have to upgrade the whole platform.

With microservices, each individual service can be deployed and scaled on its own container. With AWS, for example, you pay for what you use, and if one service is suddenly in demand, Amazon’s cloud can manage the upsurge without problems and without affecting the rest of your backend.

That’s useful if some part of your infrastructure sees a spike in traffic. Perhaps you have a holiday product that gets mentioned in an article. A lot of people will look at your catalog. A few days later (hopefully), a lot of them will come back and buy the product. Amazon can let you ride the wave of traffic to your catalog and then handle the increased demand on your ordering system.

## Building vs. Buying

Once you understand the requirements of multi-channel e-commerce, it becomes clear that you need some sort of headless, API-driven solution, and microservices offer clear benefits when it comes to scaling and modifying features.

The hard question is whether you should build these services yourself or use a third-party solution.

### Building multi-channel e-commerce microservices
Here’s an example of how data might flow through a microservices-based setup, with an API gateway receiving calls and a service mesh routing calls from your checkout service to the packing and accounts services.

![API Gateway](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/adm9mzkbwyvh1awy1zup.png)

To set up an architecture like this, you first need to decide what services you want it to provide and how they will connect. Once the essentials are in place, you can build from there. Let’s look at a couple of the key components you’ll need.

### Order management system (OMS)

An order management system deals with taking orders and handling different payment methods, as well as updating inventory, invoicing, and passing information to your delivery and accounting teams.

When an order is placed, data flows from a frontend application to your backend infrastructure, which is responsible for distributing it among the other services that need it. Your product database will be updated, as will your accounts, and the warehouse will need to know what to deliver.

As orders involve the interaction of multiple services, they are a good test of your architecture, as well as being essential to get right.

### Product information manager (PIM)

Storing product information is a key component of any e-commerce business with large and growing volumes of data to manage. The fields and relationships between them may evolve as new sales channels provide new ways to present them.

The PIM provides information on products to your customers and allows for manual updates by staff, as well as automatic updates via other services, such as inventory.

With a headless architecture, you can keep all this in one place so that updates will be reflected everywhere. You can add fields and redefine existing ones as required, and also expand your range of services to support new kinds of queries.

### API gateway and service mesh

Another key component to this architecture is your API gateway. It routes API calls to the correct services from the various frontend channels. You can change the routing, which allows you to easily switch between different services or versions of them.

For example, you could swap in a newer version of a service for testing or swap it out if you need to roll back to an earlier version. You can also route services from existing monoliths to newer microservices during a migration.

A service mesh acts as an intermediary between your services and controls the data flow between them. A service mesh can handle logging, load balancing, and encryption.

It can also enforce call limits on your services and provide a layer of security, letting you choose what data services are allowed to send to others. You may want to track which of your channels are calling particular services to help you identify issues or usage spikes.

### Other services

Aside from the core services and gateway layers, there are many other supporting services that make up a headless, e-commerce microservice platform.

A personalization service can bring together the information you collect across all your channels and use it to customize the user experience on each one. A promotions and loyalty service can apply discounts to all your channels or specific ones. You could allow users to collect points or earn rewards from social media shares and then spend them on your web store, for example.

## Example: Processing Orders from Multiple Channels

Let’s look at an example of how a microservice might deal with a call to its API. I’ll describe a workflow for processing orders using a prebuilt API, but if you build your own, it should be similar.

When the customer has selected their items and verified their order details, the order is sent via the checkout service as a post request to the checkout endpoint.

All channels can send the same request, regardless of their platform or UI. We can add or remove new channels without changing the backend, provided they send requests in the same format.

The post request is sent to: ```POST /api-order/checkout```

To send it in JQuery (assuming you have a web frontend), you can do something similar to this:

``` JSON

$.ajax({
    type: "POST",
    contentType: "application/json",
    url: "/api-order-checkout",
    data: JSON.stringify(data),
    dataType: 'json',
    success: function (data) { //using data formatted as in the section below
        //let the user know it has completed successfully
    },
    error: function (e) {
        //let the user know it has gone wrong
    }
});
```

The request is formatted like this:

``` JSON

{
    "cartId": "5f1b4158bb24210008b9a3f0",
    "customerEmail": "test@fabric.inc",
    "customerPhoneNumber": {
        "number": "07780811973",
        "kind": "Mobile"
    },
    "paymentDetails": [
        {
            "transactionDetails": {
                "paymentType": "CARD",
                "cardNumber": "4111111111111111",
                "expDate": "1122",
                "cvv": "999",
                "cardHolderFullName": "Joe Demo",
                "metadata": {}
            },
            "paymentIdentifier": {
                "cardIdentifier": "1111"
            },
            "paymentMethod": "Visa",
            "paymentKind": "Fabric User",
            "amount": "11.27",
            "currency": "USD",
            "conversion": 1,
            "billToAddress": {
                "name": {
                    "first": "Joe",
                    "last": "Demo"
                },
                "email": "demo@fabric.inc",
                "phone": {
                    "number": "07780811973",
                    "kind": "Mobile"
                },
                "street1": "600 CONGRESS AVE",
                "street2": "",
                "city": "AUSTIN",
                "state": "TX",
                "country": "US",
                "zipCode": "1003"
            }
        }
    ],
    "estimatedTax": {
        "itemsTaxes": [
            {
                "lineItemId": 1,
                "amount": 2
            },
            {
                "lineItemId": 2,
                "amount": 2
            }
        ],
        "shipToTaxes": [
            {
                "shipToId": "5f1b41892e8f1c0008fc20e8",
                "amount": 1.5
            }
        ]
    },
    "shipFrom": {
        "street1": "888 Broadway",
        "street2": "",
        "city": "New York",
        "state": "NY",
        "country": "USA",
        "zipCode": "46282"
    }
}
```

The API gateway will route this data to the relevant service. If the request is received and processed correctly, the backend will send a response formatted as follows:

```JSON

{
"checkoutComplete": true,
"orderId": "4679-1016-66250"
}
```

So the frontend can present a confirmation message to the user, and of course, this will vary depending on the sales channel you’re using.

The backend can send further requests to other services, as determined by you and managed by your service mesh. As shown in the application diagram above, these could include calls to the shipping or promotions services, but the options are endless. To users, the interface is simple, while the backend implementation can be as complex as it needs to be.

## Key Takeaways

Headless, microservices-based architectures have several advantages. Their modular nature makes them easy to change and gives you the flexibility to evolve your systems over time. They are also scalable, allowing you to allocate resources efficiently as you grow.

Building them yourself is time-consuming and expensive, so using a prebuilt solution can give you a headstart. These building blocks can get you up and running quickly and keep you ahead of the competition. With your architecture in place, you can add features and support new services, confident that your setup can handle their requirements.