Stream Data Optimizations

Data fetched from backend systems as configured by the content managers is crucial to Frontastic. Since we do not store any data ourselves but load all data from third party systems we already implement some optimizations ourselves:

  • Only load streams assigned to tastics
  • Load all streams in parallel
  • Optionally cache stream data
  • Limit fetched item count

Still the data fetched has to be transmitted to the server side rendering and to the client, so it can make sense to optimize the data for this. this could already be done using a API decorator but we have an additional concept for this which knows more about the context of the stream, the StreamOptimizer.

Stream Optimizer

A stream optimizer will receive the data of all streams and some context information:

  • The current node & page
  • The current context 8customer, project, session, …)
  • The tastics currently using this stream
  • The stream itself and its parameters

Based on this information you can programatically decide if you want to remove data from the stream.

It is very common, for example, that everything which refers to product lists needs a lot less data about a product then a product detail page needs. While Frontastic by default also provides all product lists with all the product data we can use a stream optimizer to limit the transmitted data. For this we must do two things:

1) Create the Optimizer 2) Register the Optimizer

Create The Optimizer

The Optimizer right now is a PHP class which will receive all data from all streams and can return updated data based on this. In this case we remove a lot information from the products in a product-list stream, because those are not necessary for any of the product lists in our project:

namespace My\StreamOptimizer;

use Frontastic\Catwalk\FrontendBundle\Domain\StreamOptimizer;
use Frontastic\Catwalk\FrontendBundle\Domain\StreamContext;
use Frontastic\Catwalk\FrontendBundle\Domain\Stream;

use Frontastic\Common\ProductApiBundle\Domain\Product;
use Frontastic\Common\ProductApiBundle\Domain\Variant;

class MinimalProduct implements StreamOptimizer
{
    /**
     * @return mixed
     */
    public function optimizeStreamData(Stream $stream, StreamContext $streamContext, $data)
    {
        // Ignore all streams but product lists
        if ($stream->type !== 'product-list') {
            return $data;
        }

        foreach ($data->items as $index => $product) {
            $data->items[$index] = new Product([
                'productId' => $product->productId,
                'slug' => $product->slug,
                'name' => $product->name,
                'variants' => [
                    new Variant([
                        'sku' => $product->variants[0]->sku,
                        'price' => $product->variants[0]->price,
                        'images' => $product->variants[0]->images,
                        'attributes' => array_intersect_key(
                            $product->variants[0]->attributes,
                            // Product attributes we want to keep
                            array_flip(['designer', 'badges'])
                        ),
                    ])
                ]
            ]);
        }

        return $data;
    }
}

In this stream optimizer we create new products with a very small data subset from the existing products. While this seems irrelevant stripping data will help a lot because it reduces time spent in en- & decoding data and the amount of data transmitted over network.

In practice you can decide based on the tsatics using the current stream which attributes you want to keep. Take a look at the StreamContext for all these context information.

Register the Optimizer

The next thing you need to do is registering the Optimizer inside the dependency injection container using the service tag frontend.streamOptimizer:

<service id="My\StreamOptimizer\MinimalProduct">
    <tag name="frontend.streamOptimizer" />
</service>

After this all product lists will already be stripped down on the server and you should see less data in the frontend and thus increased overall performance.

Default Implementation

Since the Optimizer implemented above is a very common case we provide you with a simple default implementation you can use by adding the following to your dependency injection container configuration:

<service id="Frontastic\Catwalk\FrontendBundle\Domain\StreamOptimizer\MinimalProduct">
    <argument type="collection">
        <argument>designer</argument>
        <argument>badges</argument>
    </argument>

    <tag name="frontend.streamOptimizer" />
</service>