Below is a high-level architecture for the SDK, detailed in some cases at the class-level.
There is a central SDK class ( RichRelevance ) that is the entrypoint for all SDK interactions. It includes the following functionality:
- Configure logging
- Access default API client
- Request builder factory methods
This class must be initialized before the SDK is used by calling
RichRelevance.init(Context, ClientConfiguration). This is typically done in the containing application’s
Constructing API Requests (Builders)
All API requests are constructed using an implementation of the builder pattern. There are several request builders that provide API endpoint abstractions. The builders are as follow:
RequestBuilder: base class for all builders. Includes common functionality as well as the ability to add any arbitrary key/value pairs to a request.
PlacementsBuilder: intermediate class for PlacementRecommendationsBuilder and PlacementPersonalizeBuilder. Includes common functionality.
PlacementPersonalizeBuilder: “personalize” builder
PlacementRecommendationsBuilder: “recsForPlacements” builder
ProductBuilder: “getProducts” builder
StrategyRecommendationsBuilder: “recsUsingStrategy” builder
UserPreferenceBuilder: “user/preferences” read/write builder
UserProfileBuilder: “UserProfile” builder
Each builder has type-safe methods for setting relevant values, the ability to add a completion listener, and an
execute() method which performs the request via the set client (by default this is set to the SDK’s default client). There are also helper methods for common use-cases that create pre-configured builders (see next section). Internally, the builders are also responsible for parsing the result of the web request into domain specific objects.
Builder “Helper” Methods
RichRelevance class contains several “factory” methods that vend pre-configured request builders for common API use-cases. These methods are separated into two groups, one for fetches and one for tracking. The former includes requests that expect responses such as
recommended products, while the latter includes tracking requests that are essentially fire and forget.
The network operations performed by this SDK are at this time, not diverse enough to warrant inclusion of any third-party libraries such as OkHttp or Volley. All calls at this time are HTTP GET, all payloads are JSON, and there are only a handful of endpoints and paths. In the future, more granular, REST-style API changes might warrant the introduction of such a library. With that said, a heavily modified and stripped down version of our Android WebServiceManager library is included in order to manage some of the lower level pieces of placing web requests.
The SDK uses an instance of a
WebRequestManager to perform all network operations. The manager will maintain a maximum number of simultaneous connections and handles the background threading, queueing, execution, and callbacks for each request it is passed. In this way, the SDK’s network traffic is throttled (see Note below) in an attempt to avoid consuming too much network throughput from the containing app. Under the hood, the manager uses
HTTPUrlConnection to perform requests.
Note: throttling is executed by limiting HTTP connections to 5. Simultaneous requests are queued and deferred until one of those 5 connections becomes available.
It is also worth noting that click tracking uses a second
WebRequestManager instance to keep view requests from “clogging” the rest of the SDK’s functionality. This click tracking manager will only use one more connection.
RichRelevanceClient is the dispatch point of the SDK. It handles obtaining the request information from a
RequestBuilder, executing the request through the
WebRequestManager, and passing the result back to any callback associated with the
RequestBuilder. The client also contains a
ClientConfiguration which contains a set of user credentials for the API. This is set up in the SDK initialization, but can be modified in the clients at a later time if needed.
It is possible to create a standalone instance of this class, but more advisable to use the one supplied by
getDefaultClient() found on the
API requests originate from request builders (see above) as
WebRequest objects which are executed via a
WebRequest object contains all the elements of the request inside a
WebRequestBuilder object (URL, HTTP method, parameters, headers, OAuth settings, etc) as well a way to parse the response of the request back into domain specific model objects. The
RequestBuilder base class internally maintains the parameters of the web call via methods which delegate to the underlying WebRequest object.
RequestBuilder is also responsible for the parsing of the response since the response format is just as domain specific as the parameters. The base class does some error handling and extracts the JSON response if possible. It then delegates the JSON deserialization to the implementing subclasses.
In low or no network scenarios, for the majority of calls, the SDK does not queue or retry failed requests. If no network is present and concretely detectable, requests will not be attempted at all and appropriate errors will be reported to calling code.
Product recommendation requests may return a set of
RecommendedProduct. Clicks on these can be tracked either by calling
trackClick() directly on the object or via
Click tracking is an exception to the rules on connectivity: if a request fails, it will be retained in an in-memory-queue. The SDK will attempt to listen for network state changes to flush it’s queue when the network becomes available. This will only function if the containing application holds the
ACCESS_NETWORK_STATE permission, but will not crash if the permission isn’t held, therefore the permission is advantageous but optional. In either case, the next time a click track is sent by the containing application, the
ClickTrackingManager will first attempt to flush its queue. The containing application may also call
RichRelevance.flushClickTracking() to manually trigger a queue flush attempt.
The SDK logs to the system logs via
android.util.Log and is configurable with the standard log levels (VERBOSE, DEBUG, INFO, WARN, ERROR, NONE). By default, logging is set to NONE, so as to not incur logging overhead for production releases. The
RRLog class is the location in which all logging logic lives. Levels can be configured via
Error conditions and handling
All SDK calls that make remote requests provide error feedback as part of completion handlers. Furthermore, a best effort has been made to ensure that any SDK thrown exceptions prevent the hosting app from crashing while still maintaining integrity of the SDK. See
Error.ErrorType for a list of possible error codes.
API Response Validation
Despite the fact that the API is stable and well-documented, assumptions are not made as per the presence of fields in API responses. Expected fields are validated and calls fail gracefully in the face of unexpected API behavior.
All assumptions regarding SDK inputs (arguments passed to SDK methods by the parent app) are validated directly and handled accordingly. If the method consumes an error handler, the handler is invoked with an appropriate error code, otherwise the operation is aborted and error is logged.
All SDK requests are performed over HTTPS by default and include API authentication parameters to ensure the maximum level of security. In situations where the API supports it, OAuth 1.0 is used to sign requests. The SDK does not store any data to disk, therefore on-disk encryption is not a concern at this time.