Architecture Overview

This article targets developers who would like to create the perfect search experience on their ecommerce stores, using either high-level or low-level components.

📘

Note:

We consider the reader has some knowledge of Javascript frameworks such as React.js, Vue.js or Angular.js.

🚧

Important:

In this article, the following examples use advanced JS features, like imports. In consequence, it is assumed that an appropriate environment is set up to support these examples (compilation via Babel/Webpack for instance).

The Findify Merchant JavaScript library (we will refer to it as "M.js" in the rest of this reference) is a multilayered library, consisting of a few abstraction layers, that you can leverage. Those layers are given below, from the lowest-level abstraction to the highest:

  • SDK
  • Agents
  • Providers + Connectors
  • UI Components

Two additional modules are present: Analytics and Bundle

SDK

The SDK is a promise-based API that communicates with the Findify APIs.

It allows you to query our API using both POST & JSONP request methods, thus allowing it to work within both Node.js and browser.

To know more about the in-depths of the SDK, you can consult:

Agents

Agents are a higher-level wrapper around Findify’s SDK. They provide an event-based interface to access data returned by the Findify APIs.

📘

Attention:

Each Agent wraps the response from the Findify SDK into an Immutable.Map from the Immutable.js library. You can learn more about it here.

Agents can be of several types:

  • Search
  • Autocomplete
  • Recommendation
  • Content
  • Smart Collection

The agent types refer to the high-level features, offered by Findify.

Search: The Search agent works with the Findify Search API. For instance, this agent is used to display the search results in the M.js for your store. The search agent also provides some information about faceting, currently used filters, and available filters, products, prices, variants, etc.

Autocomplete: The Autocomplete agent provides information about trending searches, product matches, and suggestions.

Recommendation: The Recommendation agent is used to load products for the recommendation widgets. It allows the user to control the widgets' settings, the widgets' types, location, number of products displayed... any information that you would need to render a recommendation widget.

Content: The Content agent fetches content items from Findify’s Search API. Content items are non-product documents such as Instagram pictures, Youtube videos, blog pages etc.

Smart Collection: The Smart Collection agent loads the smart collection configuration (if filters are shown, for example, which filters are shown to the customer) and products to display. The logic there is very similar to the Search agent.

Agents: Examples

In order to work with our agents, you need to:

  • Initialize the event listeners for changes to the fields you would like to listen to. For example, listening to 'change:items' event will yield a function call every time a new field 'items' arrives from the Findify API.
  • Call the method .set() on the Agent instance, outlining which field you would like to modify in your request. Calls to .set() are buffered, so the subsequent .set() calls will get merged into one.
  • Once a new response from the API is received, the respective events for each changed field are fired.

The following example assumes a browser-like environment and compilation via Webpack:

import { SearchAgent } from '@findify/agent';
const config = {
  url: 'https://api-v3.findify.io',
  key: '2e963f3e-38bd-4c00-9636-c00e48945eb7',
  method: 'jsonp',
  timeout: 5000,
  jsonpCallback: 'findifyCallback',
  retryCount: 1,
  user: {
    uid: 'test1',
    sid: 'ssid1',
  },
};

const agent = new SearchAgent(config)
agent.on('change:items', (items) => console.log('Got items!', items.toJS()))

agent.set('q', 'black')
agent.set('q', 'white')

// You should get items related to query 'white'

📘

Note:

The next parts describe React part of M.JS. To feel comfortable reading those parts you might need some knowledge about React.js - a library for building interfaces by Facebook or similar technology, like Vue.js or Angular.js.

Providers

Providers are an interfacing layer between Agents and Connectors. Their function is to connect to agent, preconfigure it as and if needed, set up analytics, and pass it and its response down via React Context.

📘

Useful Link:

You can find more information on React Context here: https://reactjs.org/docs/legacy-context.html.

Providers, similar to Agents, also have types:

  • AutocompleteProvider works with AutocompleteAgent
  • SearchProvider works with SearchAgent
  • SmartCollectionProvider works with SmartCollectionAgent
  • RecommendationProvider works with RecommendationAgent
  • ContentProvider works with ContentAgent

🚧

IMPORTANT:

Mixing Provider and Agent types is not recommended, as it can lead to things not working as expected.

Provider: Invocation Example

import { SearchAgent } from '@findify/agent';
import { AutocompleteProvider } from '@findify/react-connect'
  
const config = {
  url: 'https://api-v3.findify.io',
  key: '2e963f3e-38bd-4c00-9636-c00e48945eb7',
  method: 'jsonp',
  timeout: 5000,
  jsonpCallback: 'findifyCallback',
  retryCount: 1,
  user: {
    uid: 'test1',
    sid: 'ssid1',
  },
};

const agent = new SearchAgent(config)
const DemoComponent = () => (
  <AutocompleteProvider apiKey='2e963f3e-38bd-4c00-9636-c00e48945eb7' agent={agent} config={mjsConfig}>
  {/* All descendants of AutocompleteProvider will have Agent provided to them via Context */}
</AutocompleteProvider>
)

All descendants of AutocompleteProvider will now have following keys in context:

  • $$$findify - object containing following properties:
    • agents - array of agents connected
    • nested - boolean to indicate whether this is not the first provider in the chain of React DOM tree
  • $$$analytics - Findify Analytics object
  • $$$config - MJS Configuration in a form of Immutable.Map()

🚧

Warning:

Connecting directly to context of a Provider is not a pattern we encourage and it is provided here only so you would understand how the insides of Providers work. We recommend Providers to be used together only with Connectors.

Connectors

📘

Useful Link:

You may learn more about Higher Order Component pattern at https://reactjs.org/docs/higher-order-components.html.

Connectors are the last link between data and UI representation. A Connector is a Higher Order Component (HOC).

Connectors connect to the React Context of a Provider, establish event listeners on Agent, and handle data transformation - such as integrating analytics and transforming Agent response to inner MJS data structures. Connectors differentiate between types of response fields.

There are several connectors available to use, along with the possibility to create a custom Connector via createConnect function:

  • connectBanner - extracts banner from API response and passes it down
  • connectBreadcrumbs - works with query field, extracting enabled filters and transforming them to MJS internal structures
  • connectConfig - does not extract any response field, it just takes MJS Configuration and passes it down
  • connectFacets exposes facets response field with currently selected facets
  • connectItems is used where product information is needed. Search, Smart Collections, Autocomplete are examples of places where it is used
  • connectPagination - parses response metadata and forms information needed for pagination, like total number of pages, current page and base navigation methods
  • connectQuery - extracts query from request. Note: this is not just text query, this is also a list of enabled filters!
  • connectSort - extracts sort field from metadata, provides information on currently selected sorting method and gives a method for updating sorting directly
  • connectSuggestion - gives you access to suggestions from Autocomplete agent

🚧

Keep in Mind:

Use connectConfig only if you do not need any response fields, and not in combination with any of connectXXX or custom created connectors. Every Connector naturally passes down M.JS Configuration.

Connectors: Examples

Let's try to connect an ordinary React component to one of our Connectors:

import React from 'react'
import { connectItems } from '@findify/react-connect'

  
const ItemCounter = ({items}) => <h1>Total number of items: { items && items.size }</h1>  
  
const ConnectedItemCount = connectItems(ItemCounter)

You may now use ConnectedItemCount to see the number of items present in a Search, Smart Collection or Autocomplete.

But, what if we need a custom Connector? We can also do this.

import React from 'react'
import { createConnect } from '@findify/react-connect'

const withNumberOfItems = createConnect({
  field: 'items', // we take items field
  // and then, using mapProps, create a new prop to be passed down to components
  mapProps: (items) => ({
    totalItems: items && items.size || 0
  }),
})

// Remember ItemCounter component from previous example? We can do it like this now!
const ItemCounter = ({ totalItems }) => <h1>Total number of items: {totalItems}</h1>  
  
const ConnectedItemCount = withNumberOfItems(ItemCounter)

UI Components

UI Components are what your customer sees and what you can customize via our Findify IDE. Search Results, Product Cards, Autocomplete, Filters - everything is built on top of SDK, Agents, Providers and Connectors.

Stack Diagram

Finally, you can also take a look at the data flow diagram inside M.JS to make things even clearer:

200

Hint: Open image in new tab so see clearly.

Notice that M.JS is also split into two parts - React-based part and Raw JS Layer.

If you would like to build a custom integration with Vue or Angular, for example, you will have to get rid of the React view layer - but you can still use the pre-developed SDK, Analytics and Agents.

If you happen to be using React.js, as we are, you have much better chances to also reuse Connectors and Providers, so you will have to swap out only Components. You might even be able to do so inside of our Findify IDE.

We hope this article made things clearer on how M.JS works inside, so it will be easier for you to tackle developing custom integrations from the ground-up using parts of our M.JS stack. If you have any questions, please send them to [email protected].