Object-based Event Tracking
Intro #
Ok, so, I’m sure we’ve all had a go of many different analytics tools over the years but for me there is one thing that is lacking. Event tracking at the object level. Please do get in touch if you are familiar with an off-the-shelf solution that works in a similar way, but I have yet to find one that works how I would like it to.
This article will use pseudo-code to indicate various points, though you may notice that it is somewhat swift-esque.
The old way #
So the way most analytics tools work (I’ve used Mixpanel, AppsFlyer, Facebook Events, Crashylitics, Flurry and Google Analytics) tends to be that you have users and you have events.
Users: An instance of the app will typically identify as a user and then is able to story key-value style properties against the user. No history is stored on these, you can just see what the current value of a property is. This is typically useful for keeping count of how many times an action has been performed, keeping track of state or storing user related info that is unlikely to change, like their name, or phone number.
Events: Events tend to be associated with a user, then once the app has identified as a user it is able to track events that the user performs in the app, which then creates a timeline of events for the user. This allows us to login to some online portal and checkout our users’ journey’s and hopefully refine UX based on it. We are also usually allowed to specify a map of properties to be tracked to give more information about an event.
Sometimes these alone can provide very useful data, but since we (iOS developers at least) are building our apps in an object oriented manner, should not our analytics systems be following the same principle?
The Proposed Solution #
My proposed solution is to switch to an object-based event tracking system rather than just a user-based event tracking system. This would allow all the functionality of the traditional system but with so much more flexibility.
Example 1 #
Imagine an app that allows the users to make bookings, edit bookings, cancel bookings and review their booking once they have passed. Now we could quite simply have a system like this:
trackEvent("booking_made", properties: ["id": 12345])
trackEvent("booking_updated", properties: ["id": 12345])
trackEvent("booking_updated", properties: ["id": 12345])
trackEvent("booking_reviewed", properties: ["id": 12345, "stars": 4])
Which would give us the ability to filter for the booking related events and tie them up using the id to see the journey that a booking took. Would it not be preferable to go to a user and rather than having one long list of all their events and having to filter it down, if from the user you could navigate to a list of all of their bookings, and from there select an individual booking to see the journey it took? Of course, we could build an interface on top of the user-based event data so that we can access the data in this manner, but why not encapsulate our analytics data with the same vigour with which we do our data model?
Perhaps we could do something like:
let booking = createEventObject("booking", id: 12345)
booking.trackEvent("updated")
booking.trackEvent("updated")
booking.trackEvent("reviewed", properties: ["stars": 4])
It might not seem all that different, but it would allow the data to more easily mirror the model rather than tying together a flat list of events. Now rather than having to find all the relevant events and cross-reference the ids, we can just find the booking object and look at it’s timeline of events.
This example gives the general idea but is too simple to actually require event-based tracking, it could perfectly easily be handled with user-based events. Let’s have a look at something where it would be more useful.
Example 2 #
Suppose we have an app used by teachers and students, the teacher can set homework for a student which they can submit work for and the teacher can then assign a mark. Let’s look at how user-based event tracking might work for this:
// Teacher's device
trackEvent("homework_created", properties: ["id": 12345])
trackEvent("homework_issued", properties: ["id": 12345])
// Student's device
trackEvent("homework_received", properties: ["id": 12345])
trackEvent("homework_submitted", properties: ["id": 12345])
// Teacher's device
trackEvent("homework_received", properties: ["id": 12345])
trackEvent("homework_marked", properties: ["id": 12345])
Now we have a homework object that is being acted on by multiple users, meaning that if we want to know what happened to the homework, we have to look through two (or potentially more) user’s timelines of events to find all of the relevant events and coalesce them into a single timeline for the homework. Still possible, but starting to get quite messy.
The above example assumes that our data model has propagated the identifier of the homework between the users (as is likely required by the app) so we will assume the same in the event-based system, which will go a little something like this:
// Teacher's device
let homework = createEventObject("homework", id: 12345)
homework.trackEvent("issued")
// Student's device
let homework = getEventObject(id: 12345)
homework.trackEvent("received_by_student")
homework.trackEvent("submitted")
// Teacher's device
let homework = getEventObject(id: 12345)
homework.trackEvent("received_by_teacher")
homework.trackEvent("marked")
So the first thing you will probably notice is that there are actually more lines of code in this second approach. But doesn’t it just feel so much cleaner? Rather than tracking events for a user, which we then identify as being about a piece of homework by checking the event name and id, we simply find a user, navigate to a list of their homework objects and can see the full event log for a piece of homework. It now becomes very simple to track an object’s lifecycle through events performed on it by multiple users. We can also very easily see all the users who have acted on an object and what they each did. Previously we would have had to search through all the events for all the users to find any prefixed with homework_
and with the relevant id
to ensure that we had found all of the users who had acted on the object.
It just seems so much more logical to allow different objects to have their own timelines instead of limiting to just tracking events against a user. We all know users aren’t the only objects in our data models, and while they may be the only ones that can perform actions (although quite possibly not in some cases), they certainly aren’t the only ones that can have actions performed on them.
Taking this further #
Going forward I plan to put together a full specification on how this system could be implemented and even put together a simple version of it to have a play with. I’ll update this post with links to any further progress I make on this project.
Get Involved #
If you like the sound of this system and would like any more info about it or would perhaps like to get involved in the implementation stage, please feel free to get in touch with me via email. Also, if you have come across any similar systems I’d love to check them out!