单向数据流之用户行为统计

这几年的开发中,最让人头疼的事情之一就是数据统计。这里就来看看以单向数据流的角度如何改进统计系统的设计。

面向切面埋点

我非常的反对使用面向切面埋点来处理用户行为,理由有三个:

  • 统计数据极度依赖视图结构,或者需要将每个数据绑定到视图上。
  • 不能完成复杂的交互统计,仅能实现简单的事件数据。
  • 视觉上的修改会影响统计结果。

以前在使用切面埋点的时候,就遇到很多的问题,虽然说每个数据点都不可能漏埋或者错埋,但是每次上线后数据分析都需要跑过来让开发给他们看看这些行为的埋点数据是怎么样的。这样也很难实现一个多期的版本对比。

用户行为统计

那么按照标准的用户行为统计又有哪些问题呢?

  • 每个数据深入业务底层。需要统计要么把事件层层代理到Controller,要么在底层这些看似不合理的地方埋点。
  • 复用问题。业务虽然一样,但是埋点信息并不能完全保持一致,而且有些场景下也无法保持一致,因为可能会有重复场景。
  • 埋点数据回归测试。由于是人工埋点,所以可能会漏埋错埋的情况发生。

目前

目前我们的埋点方案主要有3点:

  • 数据尽量保持统一。相同的业务埋相同的点,然后根据页面区分。这样就能够实现重用,缺点是有少部分需要特殊化的场景。
  • 代理到业务层,然后再埋。缺点是如果中间层次过多,会出现多级代理,而仅仅是为了埋点。
  • 子类化。专门子类化该页面的专有子类。缺点是子类的目的就是为了区分埋点,有点多余。

以上都没有一个很好的方案能够解决数据回归测试的问题。而回归测试也只能靠人工执行。

单向数据流方案

统计即是数据,那么当然也非常符合数据流模型,那么我们就用数据流模型来简化埋点方案,增加每个模块的独立性和复用性,同时也把埋点放到一个地方去做,减少埋点数据在整个应用内的散乱分布。

stat_flow.png

以上就是这套方案的大概结构。用户触发行为时,和之前直接统计行为不同,而是创建一个Action对象,将统计所需要的参数,或者自身包含数据包装在Action内,发送给Store。Store作为一个数据中心,负责接收和分发数据,他将收到的数据分发给订阅者Subscriber,最后由Subscriber完成统计数据,并上报服务器。

Store、Action是完全可复用的,同时这两者并不关联实际业务,所以完全可以模块化,同时只要行为足够完整,也不需要关系具体业务方统计数据的样式。这样就可以让其他模块完全的复用了。

那么如何提升复用性,我们来关联下之前讨论过的MVP。

stat_flow_with_mvp.png

这里,红色框内的部分都是逻辑性的,是完全可复用的;View也是独立与逻辑的,也是可复用的;只有Subscriber和Controller是和业务强相关的,是不可复用的。那么我们就可以知道需要把哪些东西放到不可复用的地方,哪些东西放到可以复用的地方了。

同时我们也需要考虑下测试的问题,来解决埋点数据的完整性和正确性。

stat_test_subscriber.png
stat_test_user_event.png

只要我们mock了Store部分,就可以轻易的检查发生的Action,或者向订阅者发送对应的Action,这样就可以比较简单的去回归测试数据了。只不过这样做的收益可能并不高。

实现

这里我们来看看实现的方式。

首先定义基础的Store和Action

class StatAction {
    var type: String?
    var params: [String: Any]?
}

protocol StatSubscriber {
    func newStatAction(action: StatAction)
}

class StatStore {
    func dispatch(_ action: StatAction) {}
    func subscribe(_ subscriber: StatSubscriber) {}
    func subscribe(_ subscriber: (StatAction)->Void) {}
}

那么在ViewController里就可以这样配置。

func viewDidLoad() {
    super.viewDidLoad()

    self.store = StatStore()
    self.store?.subscribe({ action in
        // ... switch case action.type.
        // Track
    });

    self.submodule.store = self.store
}

而子模块中只需要使用store来分发行为就可以了。

let action = StatStore(type: "star", params: ["id": "1234"])
self.store?.dispatch(action)

这里订阅者甚至可以自己创建独立的类来处理这些情况,这样就更加的分离了行为统计这种不能划分为任何模块的内容了。

最后

这个方案将行为统计从整个app中剥离出一个单独的模块,同时实现了高度可复用性,而且使得统计也成为可以单元测试的了。唯一的缺点是在具体统计的时候需要大量switch...case...来区分不同的行为。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容