Lean Analytics with the Adobe Client Data Layer

This is a guest post by my colleague Ben Wedenik, one of the people who wrote the Adobe Client Data Layer Extension for Launch.

When my colleagues and I started to develop the “Adobe Client Data Layer” Launch Extension, we wanted to make the concept and technology of an Event Driven Data Layer (EDDL) available to anyone. Just recently, we released version 1.1.3 of the Extension and underlaying data layer.

In this article I want to give you some best practices about how the extension can be actually used in an elegant and scalable way to get your tracking setup to the next level.

As Jan has already written a nice post about what the Adobe Client Data Layer (ACDL) is, I’m for now assuming that you’re familiar with the concept behind it. If not, I’d recommend you to read his article first

Let’s put together a wish list of features we would like to cover:

  • Tracking of arbitrary events.
    • Pageload
    • Downloads
    • Social shares
    • Navigations
    • Video interactions
    • Clicks
    • Etc.
  • “Global” variables which are set on every hit.
    • Pagename
    • Language
    • Timestamp
    • Etc.
  • Ability to set specific variables for specific events.
    • E.g. CTA label on a CTA click event.
  • Ability to set specific Adobe Analytics events for specific events.
    • E.g. Set event123 if a CTA click is registered.
  • Low complexity and easy visual editing.

If you’re a huge fan of having tons of rules and data elements, you should probably stop reading now because the setup I’m going to propose will be lean (really lean).

Two rules and one data element – that’s all it needs to cover the items of our wish list.

Let’s have a closer look of what the respective components are going to do:

  • Clear and Set Variables — This Rule is going to be setting all the variables we need.
  • Fire Beacon — This Rule fires the Adobe Analytics beacon.
  • Event Mapping Table — In this Data Element we will define our events and some additional variables if needed.

To make it easier to follow and less abstract, I’m going to illustrate my setup based on a fictive scenario containing three events with different requirements. This can be scaled up to fit many more events, while the concepts stay the same.

Those are the given requirements in our case:

  • Pageload
    • event1
    • eVar1 = Pagename
    • eVar2 = Language
    • eVar3 = Page Category
    • Those eVars should be set on every hit = global variables
  • CTA Click
    • event2
    • eVar4 = CTA Label
  • App Download
    • event3
    • eVar5 = iOS or Android

    Rule 1: [10] Clear and Set Variables

    [screenshot]
    Rule 1 – Clear and Set Variables
    The Event is basically a push listener to all events with an execution order of 10, which is defined to make sure, this Rule is fired before the following fire beacon Rule.
    [screenshot]
    Rule 1 – Data Layer Push Event Type Settings
    Alternatively – if your website is firing events you don’t want to track – one can simply add multiple listeners to specific events:
    [screenshot]
    Rule 1 – Clear and Set but with specific events
    So far so good, this Rule should now fire whenever one event (or the respective events) is pushed into the ACDL. Let’s go ahead and define the desired variables in the action.
    [screenshot]
    Set eVars
    In this rule we can set all the required eVars. As you can see, eVar1-3 are populated by referring to the “fullState” of the ACDL. This is basically the value of the getState() function at the time the event is captured. By doing so, we are sure that the page name, language and page category are always going to be set even though this information might not be directly set on the data layer event.eVar4 and eVar5 get their values directly from the “eventInfo”, which is the part of the ACDL which is not persisted into the state.But why does this setup work? Well, if there is no value in the respective eventInfo object, the eVar will remain empty and thus will not be included on the server call. Thus, the pageload will only contain eVar1-3 while, for example, the CTA Click will be fired with eVar1-4.We’ll update this rule in the end again to support setting our events based on the mapping table.

    Rule 2: [90] Fire Beacon

    In this rule the Adobe Analytics beacon is going to be fired. Based on the respective event, this should result in a pageview or not.

    [screenshot]
    Rule 2 – Fire Beacon
    The defined Launch event trigger should be the same as in the previous rule. This means either “All Events” or your specific list of events, now with a higher order, in my example I’m using 90 to make sure the rule is fired after clear and set variables is executed. To avoid the need of having two separate rules to either fire a pageview or a custom link, I’m using little piece of JavaScript which does the magic for us.
    The custom code needs to be added in the Custom Code section of the “Adobe Analytics – Set Variables” action, as we need a reference to the s object to call s.t() or s.tl().
    [screenshot]
    Analytics Custom Code

    s.useBeacon = true;
    if (event.message.event === 'pageload') {
        s.t();
    } else {
        s.tl(true, 'o', event.message.event);
    }
    

    In case of a pageload event, a pageview is triggered, else a custom link with the event name is issued.

    Data Element: eventMappingTable

    In the mapping table we can now define which event should be set on each ACDL event.

    [screenshot]
    Event Mapping Table
    The data element we base our mapping table on is not a “real” data element in fact. I’m simply referring to the event name (of the pushed ACDL event) available once this mapping table will be evaluated. The output is the respective event.

    Gluing the mapping table with the set variables rule

    As a last step the integration of the mapping table into the clear and set variables Rule is needed.

    This requires some custom code, which also needs to be added in the Custom Code section of the “Adobe Analytics – Set Variables” action, as we need the reference to the s object to set additional events.

    The following code will call the mapping table, pass through the ACDL event and then set the respective event based on the result:

    /**
     * Sets AA events based on an input string
     * @param mappedEvent name of the event to set, e.g. 'event1'
     */
    function setAnalyticsEvent(mappedEvent) {
        if (typeof mappedEvent !== 'undefined' && mappedEvent !== null && mappedEvent !== '') {
            // Set mappedEvent into s.events
            if (typeof s.events === 'undefined') {
                s.events = '';
            } else if (s.events !== '') {
                s.events += ','
            }
            s.events += mappedEvent;
    
            // Update s.linkTrackEvents to contain mappedEvent
            if (typeof s.linkTrackEvents === 'undefined') {
                s.linkTrackEvents = '';
            } else if (s.linkTrackEvents !== '') {
                s.linkTrackEvents += ',';
            }
            s.linkTrackEvents += mappedEvent;
    
            // Add events to s.linkTrackVars if not already present
            if (typeof s.linkTrackVars === 'undefined' || s.linkTrackVars === null) {
                s.linkTrackVars = '';
            }
            if (s.linkTrackVars.indexOf('events') === -1) {
                if (s.linkTrackVars !== '') {
                    s.linkTrackVars += ',';
                }
                s.linkTrackVars += 'events';
            }
        }
    }
    
    // Retrieve the event to set from the mapping table, passing in the current event.
    setAnalyticsEvent(_satellite.getVar('eventMappingTable', event));
    

    Congratulations! You’re done!

    With this setup it is straight forward to add further events and eVars. Of course, the default variables like pagename can be set in the Clear and Set Variables rule too. Looking at our requirements, we can tick off all the points from the wishlist:

    • Tracking of arbitrary events
      • All events pushed into the ACDL will be available in Launch.
    • “Global” variables which are set on every hit
      • Using the fullState of the ACDL Extension this is easy to set up.
    • Ability to set specific variables for specific events
      • By leveraging the mapping table extension, this can be achieved flexibly.
    • Ability to set specific Adobe Analytics events for specific events.
      • By using the event information directly this can be done within one rule.
    • Low complexity and easy visual editing.
      • Using the given Adobe Analytics Set Variables action we can work visually.
      • Setting events based on the mapping table is intuitive and readable without the need of understanding JavaScript.

    Pro Tip

    With a few more lines of JavaScript you can boost your mapping table to support setting multiple events at once. Simply return ‘event1,event2,event3’ from the mapping table and iterate through the list in your code.

    If you are eager, you can even go one step further by adding support for structures like this:

    ‘event1,event2=456,eVar7=%event.message.eventInfo.myAttribute%,eVar9=%anotherMappingTable%’

    I’ve implemented this successfully in real world projects and the usage is absolutely flexible, as the mapping table supports regex matching as well.

    One more hint – mapping tables can be nested – that way you can look at multiple keys to reproduce nested if-else structures in a visual and scalable way.

    Addendum

    With those events, you can try out the setup on your website (I know, we all love copy-paste):

    adobeDataLayer.push({
        event: "pageload",
        page: {
            name: "Homepage",
            language: "en",
            category: "Home"
        }
    });
    
    adobeDataLayer.push({
        event: "cta-click",
        eventInfo: {
            ctaLabel: "Switch to ACDL now!"
        }
    });
    
    adobeDataLayer.push({
        event: "app-download",
        eventInfo: {
            appDownloadOs: "Android"
        }
    });
    

2 thoughts on “Lean Analytics with the Adobe Client Data Layer

  1. Ben, Jan,

    Could you share your best practices on how you overwrite arrays?

    Here is one of my scenarios. The page contains a facet / search filter with various groups with multiple items in each. Every time a visitor applies the filter by ticking boxes or by selecting values in drop-downs, the DL should be updated (and a beacon should be sent) with the current mix of selected items.

    For simplicity, let’s say that the first DL call may look as follows (with 3 items preselected):

    window.adobeDataLayer.push({
        "event": "click",
        "data": {
            "items": [
                'item1',
                'item2',
                'item3'
            ]
        }
    });
    

    then the visitor changes the filter by unselecting item1 and item3 and selecting item4. In this case, there should be two DL calls 1) to reset the array to null 2) to set the array with the selected items.

    window.adobeDataLayer.push({
        "data": {
            "items": null
        }
    });
    window.adobeDataLayer.push({
        "event": "click",
        "data": {
            "items": [
                'item2',
                'item4'
            ]
        }
    });
    

    Is there a way to make this in a single DL call to just overwrite the array with the new values?

    Another challenge is when an item should be appended to the array. With the approach mentioned above this requires to first read the DL and then push a new item with the just read items to avoid the first one to be overwritten.
    In other words, if now item5 should be appended, the developer can’t just fire the DL as follows

    window.adobeDataLayer.push({
        "event": "click",
        "data": {
            "items": [
                'item5'
            ]
        }
    });
    

    since this would result in item5 and item4 to be returned by getState()

    How do you approach this type of scenario? For example, could you share what you do when a product gets added to the basket?

    Like

    1. Hi Andrey

      Thanks for reaching out, those are valid questions and I’m happy to share my view on those points.

      Regarding the issue of having to reset the data layer array in order to track the current selection of the filter, I’d suggest you to use the eventInfo property.
      By doing so, the values are not persisted in the state of the ACDL and only available within the respective event.
      This means, you don’t have to explicitly clear the array before pushing to it.
      In Launch you can then simply access this array and track the applied filters.

      window.adobeDataLayer.push({
          "event": "click",
          "eventInfo": {
              "items": [
                  'item1',
                  'item2',
                  'item3'
              ]
          }
      });
      
      window.adobeDataLayer.push({
          "event": "click",
          "eventInfo": {
              "items": [
                  'item2',
                  'item4'
              ]
          }
      });
      

      Regarding the issue of appending an element to an existing array, this is indeed not possible in the way you’ve illustrated it. I guess the easiest way of maintaining a consistent data layer would be to use eventInfo like mentioned above.
      For example, you’d then have to push only one product in case of an add-to-basket event or multiple ones in case of a basket.

      If this information should be persisted into the state for potential other tracking requests or services, I’m afraid you would either have to first read the data layer and then update the array, or reset it with null and then push the desired list of products into it.

      I hope this gave you some more insights.
      Let me know if you have any further questions.

      Best regards & have a nice day
      Ben

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.