Building a custom account tracking system for a scalable Piwik installation.
Piwik is an open-source web analytics platform that is used by an agency to track their client's blog traffic.
Application Development, Strategy, Web Analytics
A Three Part System
Tracking web analytics for over 1,000 clients required a system that is robust, reliable and responsive to changes in the clients' situation. From the creation of a client record in the agency's CRM system, through the launch of their blog or website, to the analysis of their web analytics in a client's dashboard, data integrity was a crucial factor.
The project goals required a system that could automate the activation and management of the tracking code on the client's blog or website, verify that activity was being recorded, and classify the client's account under a specified category in the agency CRM.
Model View Controller
A conventional MVC software pattern divides an application into three interconnected parts, each representing a state-transition point at which a user's information is stored, presented or updated.
The project goals required a system that could:
- automate the activation and management of the tracking code on a client's site,
- provide centralized administrative control over client tracking,
- verify tracking activation,
- classify the client's tracking account in the agency CRM.
Each of the requirements involved basic CRUD functionality and business logic typical of most MVC applications.
A logical balance
A passage from a book on software design patterns states:
A view queries the model in order to generate an appropriate user interface (for example, the view lists the shopping cart's contents). The view gets its own data from the model. The controller may (in some implementations) issue a general instruction to the view to render itself. In others, the view is automatically notified by the model of changes in state (Observer) which require a screen update.
Although the above statement is true in principle, providing the view with access to the model creates an interdependence between both. If the view is too closely coupled to the model and its business logic, the resulting dependency may 1. overcomplicate the view's logic and reusablility in order for it to consume an individual model, or 2. limit its flexibility by trading its unique logic for broader compatibility with multiple models.
In this scenario, more complex applications with multiple models may not be able to share a common, sustainable internal logic between views.
Finding the right balance between the complexity and compatibility of the view's logic means (re) examining the relationship between the view and the model, and the role that each plays in the system.
As explained above, associating the View with the Model also marries their concerns - logically requiring one to accommodate the needs of the other. This is further complicated by the scope of the Model which, in many cases, may incorrectly address both the domain (CRUD) and the MVC (business) logic together. An ideal MVC application consists of interconnected but modular components, providing (re)usability without sacrificing flexibility.
The Analytics tracking application uses a single model to service several views that handle the SCRUD based operations behind the administration, verification and automation of client tracking accounts. In addition, it required several helper classes to connect to the web services running the analytics engine, Piwik, and the client sites themselves.
With that configuration, the interaction between the Model, its helper classes and Views would make the code highly complex and difficult to manage.
But what if the interaction between View and Model were abstracted into a separate layer?
Separation of Concerns
My own thoughts were that Views could skip the middleman and instead use View Helpers to read data from Models more directly.
Enter the ViewModel...
The ViewModel contains the business logic neccessary to process input, update the Model, and inform the View of changes to the application's state. By communicating directly with the ViewModel (and not the Model), which in turn communicates with the Model, the View is no longer directly coupled to the Model's concerns. This simplifies both the application's overall process and allows each component greater logical independence.
Display Logic vs. Presentation
In the strictest sense, a Template component in addition to a View is a redundant abstraction. However, freeing the View of its dependency on the domain logic of the Model through the ViewModel still left a layer of complexity where the display logic was bound to the presentation code.
Using the Template as a vehicle for only presentation reduced the clutter in the View layer and allowed for an extra layer of refinement in which the application state could be further manipulated at the presentation level, i.e. the client side assets and refinements that power the display or output.
Decoupling Object Creation
"Dependency Injection" is a 25-dollar term for a 5-cent concept. [...] Dependency injection means giving an object its instance variables. [...]
Since it was first coined in 2004, "Dependency Injection" has become a commonplace term in application design. Essentially, Dependency Injection is a pattern in software configuration where an object and its dependencies, though logically connected, are structurally separate - meaning that an application's objects are configured by an external agent.
As described above, decoupling the application component logic increased its efficiency and simplicity. Similarly, separating object creation from dependencies further simplified the application's code. The application's Dependency Injector class silently ran behind the scenes, and was the key to the process connecting the application's component parts (Model, ViewModel, View, Template and Controller) to each other while preserving their state and that of any active data being processed.
The injector functioned as a "state carrier" in the application's process, providing instance variables to components that acted upon them, and then passing the variable in its modified state to the next component.
Scalable & Robust
There are many valid ways of implementing tried and true software design patterns in an applicaton. Choosing the right one requires a consideration of the application's requirements, scalability and lifecycle. In this project, the bulk of the application logic stayed at the ViewModel layer (the true MVC Model) and the presentation layer(s), giving it a pyramidal structure of increasing logical complexity from Model to Template.
Learning how to best utilize good practices and design patterns was a challenge worth taking on and the resulting application (which can be found on the github repository) was both scalable and robust and performed exactly as needed.