Architecture
Below is a high-level architecture for the SDK, detailed in some cases at the class-level.
SDK Central
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’sApplication.onCreate().
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” builderPlacementRecommendationsBuilder
: “recsForPlacements” builderProductBuilder
: “getProducts” builderStrategyRecommendationsBuilder
: “recsUsingStrategy” builderUserPreferenceBuilder
: “user/preferences” read/write builderUserProfileBuilder
: “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
The 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.
Networking
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.
Request Management
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 usesHTTPUrlConnection
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.
API Client
The 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 RichRelevance
class.
Requests
API requests originate from request builders (see above) as WebRequest
objects which are executed via a WebRequestManager
. This 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.
Response
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.
Connectivity
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.
Click Tracking
Product recommendation requests may return a set of RecommendedProduct
. Clicks on these can be tracked either by calling trackClick()
directly on the object or viaRichRelevance.trackClick(RecommendedProduct)
.
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 theACCESS_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.
Logging
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 RichRelevance.setLoggingLevel(RRLog.LogLevel int)
.
Error conditions and handling
Runtime Errors
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.
Input Validation
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.
Security
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.