跳转到主要内容
lurno
全部文章
博客

一家公司如何为十五家客户组织运营一个学院

十五家客户,一个学院,一支内容团队。什么必须共享,什么必须隔开,以及客户要求改课时会怎样。

The Lurno teamAugmental阅读约 1 分钟

一家为十五家客户组织运营学院的培训公司,运营的是十六个组织,不是一个。每家客户都想要自己的品牌、自己的域名、自己的管理员和自己的报表。而这家公司想要一个编写课程的地方,和一个横跨全部十五家的视图。这两种想要的东西方向相反,而整套设计最后归结为一句话:内容沿着这棵树向下流动,人和结果永远不横向穿过它。

把这条线画错地方,症状是可以预料的。要么每次法规一变,你就得手工把同一门课的十五份副本一一对齐;要么某个客户的管理员打开一份报表,在里面看到了另一家客户的员工。前者很贵。后者会终止合同。

结构很朴素:一个父组织——那家培训公司——里面装着十五个客户组织,每个客户组织又可以自由地包含自己的场地或部门。

Training company (parent)      authors everything, sees everything
├── Client 1                   learn.client1.com · own admins · own report
│   ├── Operations
│   └── Retail network
├── Client 2                   academy.client2.io · own admins · own report
├── …
└── Client 15                  academy.client15.co · own admins · own report

十六个节点,一套安装,一个升级周期。图是容易的那部分。谁来编辑消防安全课?谁看得到客户 7 的完成率数字?客户 3 想要一个不一样的及格分时,会发生什么?这三个问题就是全部的活。

B2B2B 学习交付

一个组织为其他组织、而不是为自己的员工运营学习。机构持有平台合同,编写内容,并给每家客户组织一个带自有品牌的学院、一批管理员和一套报表。学员是客户的员工。定义性的约束是:机构需要一个横跨每一家客户的视图,而每家客户只能看到自己。

什么是共享的,什么是隔开的

两份清单。这里几乎每一场运营上的争论,本质都是在争某样东西该进哪份清单。沿树向下共享,只有一份,由机构维护:

  • 课程体系、模块与课时,在父组织一次编写。
  • 题库,以及基于它构建的测评——一道题有十五份副本,就是十五次让答案跑偏的机会。
  • 能力框架,好让人工搬运达标在每一家客户组织里都表示同一件事。
  • 证书模板——版式、有效期规则、公开验证页。
  • 那些你永远不想让它有差别的东西:留存规则、审计日志、升级周期、无障碍方面的工作。

横向隔开,每家客户一份,绝不汇集:

  • 用户及其登录。一个人属于一家客户组织。
  • 报名、进度、作答次数、成绩、已签发的证书。
  • 品牌,以及客户员工在浏览器里输入的那个域名。
  • 管理员,以及赋予他们权限的那些角色。
  • 报表范围——客户看到的每一个数字,都以他们自己的节点为界。
  • 通知的发件地址,好让提醒从客户自己的域名抵达客户的员工。
  • 同意记录,以及任何要由客户法务团队负责的东西。

然后是尴尬的中间地带:那些看起来像内容、却带着客户身份的东西。证书是最清楚的例子。版式、有效期和验证页是你的;logo、签署人和证书上写的那个机构名属于客户。一个把证书当作不可分割的整体对象来处理的平台,会逼着你为了换一个 logo 而把模板分叉十五次。避开这一点的规则是:品牌是客户节点上的配置,绝不是内容的一份副本。白标是一个配置面,不是一个编写面。

一次编写,交付给十五家客户

父组织持有主内容库。课程在那里编写、在那里评审、在那里发布。接下来发生什么,才是平台之间的差别所在,而机制只有两种。

  1. 按引用。客户组织被授予访问父组织那门课的权限。副本仍然只有一份,所以你改掉的一个错字,在发布的那一刻就到达每一家客户。报名和成绩仍然坐在客户的节点里,因为那些本来就从不共享。
  2. 按复制。课程被深拷贝进客户的节点——模块、课时、题目、设置。客户现在有了一个独立的对象,可以独立变更,而你在上游做的任何事都再也到不了它那里。一个真实的课程体系是个很大的对象,所以这次复制是作为后台任务运行的。

引用是默认,离开它应该让人觉得不舒服。一旦一门课有了十五份副本,明年的法规更新就是十五次修改、十五次评审,以及十五次漏掉其中一份的机会。

共享课程该链接给每家客户,还是复制一份?

除非内容本身必须不同,否则就按引用链接。引用保留唯一的主副本,所以一处更正在发布的那一刻就到达每一家客户,而报名和成绩留在各家客户自己的组织里。只有当客户有真实的分叉理由时才复制:不同的监管机构、写进合同的及格分、必须出现在材料里的某项制度。一份副本是一项永久的维护义务——它不再接收上游的变更,而且日后不会有任何东西告诉你它已经落后了。

当客户要求改动一门共享课程

正是这类请求,决定了第三年有多贵。它来得很随意——能不能把我们的事故上报流程加进模块 3——而对它有四种诚实的答案。

  1. 这是一次更正。内容对所有人来说都是错的,或者过时了。改主副本,每家客户就都有了。悄悄属于这一类的请求,比机构预想的要多,而发现问题的那家客户通常是对的。
  2. 这是呈现。他们想要自己的 logo、自己的颜色、自己的术语。这些都不是内容。它们是他们节点上的配置;如果平台逼着你为了换一个 logo 而分叉一门课,那是平台的问题,不是这个请求的问题。
  3. 这是增补。他们要的是多一样东西,而不是换一样东西——这是多数机构会错过的那个答案。把一个只属于该客户的模块放进他们自己的组织里,挨着那个共享的课程体系。共享课程照旧共享,而额外的材料往往可以由客户自己维护。
  4. 这是真正的分歧。他们的监管机构把及格分定在 80,而你的是 70。这时候你才分叉——而这个分叉要有一个日期、一位负责人,以及登记册上的一行记录,因为十八个月后会有人问,客户 9 的版本为什么不一样,而我记得是某个客户要求的不算一个答案。
Lurno

一次分叉是一个有日期、有负责人、有理由的决定,而且总数一只手数得过来。

Not this

同一门课有十五份略有差别的副本,而没人确定哪一份是当前版本。

访问权:他们的管理员、他们的人,别人的一概不给

客户管理员的活很窄:增删自己的员工、给他们报名、催那些没完成的人,以及导出一份他们董事会认可的报表。全部都不必给你发邮件,也全部都不越出他们自己的组织。

让这件事变便宜的,是会级联的范围:在一个节点上授予一个角色,它就适用于它下面的一切。客户的培训总监是在该客户组织上的一次授权,他们的各个部门自动包含在内。场地经理是在该场地上的一次授权。某个部门关停时,你删掉那个节点,而不是去搜捕那些活得比它更久的授权。

更难的一半是你自己的员工,不是他们的。你的内容团队跨每一家客户的内容工作,却没有任何理由去读学员记录。负责客户 1 到 5 的客户经理需要这几家的成绩,而客户 6 到 15 的一概不需要。在父组织上设一个统一的管理员角色,等于把所有东西交给所有人,而这正是一家机构最后会有十来个人都能导出任意客户员工名单的原因。用权限目录搭出自定义角色、在正确的节点上授予,代价是一个下午。

客户管理员能看到其他客户的学员吗?

如果这条边界是在应用层之下强制执行的,就看不到。问厂商:这道检查住在哪里。如果客户之间是靠应用代码记得施加的一个过滤条件隔开的,那么这份隔离只成立到有人上线了一个没加它的导出接口为止。如果是在数据库里用行级安全强制执行的,那么一个漏掉过滤条件的查询返回的是空,而不是另一家客户的员工。每一场客户安全评审都会以某种形式问到这一点,所以先把答案落成文字。

报表,从两边看

客户看到的

只有他们自己的人,别人的一概没有:谁报了名、谁完成了、谁逾期了、谁一项测评连挂两次——按他们自己的部门拆分,可导出,带他们自己的品牌。这份报表的范围,就是保护他们的那条节点边界,所以是同一套机制在干两件事,而不是一个安全模型和一个报表模型必须互相对得上。

你看到的

同时看到每一个节点。你用来经营这门生意的数字是跨客户的,从任何单独一家客户内部都看不见:哪些客户已经六周没有人报名,哪些快到合同约定的席位数了,哪门课在所有地方都在挂科——因而是内容的问题,不是客户的问题。最后这一条,是选一个租户而不是十五套独立安装的最有力理由;在十五套安装里,同样的规律是一张有人每季度重建一次的表格,如果真有人建的话。

下一个诱人的功能是对标:告诉客户 4 他们排在前四分之一。先把两件事定下来。合同上,每家客户是否同意其汇总结果可以与其他组织比较。技术上,设一个最小分组规模,好让几家小客户之间的比较,不能被反推成关于其中某一家的结论。

客户组织在自己的报表里该看到什么?

关于他们自己的人的一切,关于别人的一概没有:报名、完成、逾期学员、测评结果和已签发的证书,按他们自己的部门拆分,并且可导出。跨客户对标是另一个决定,需要每家客户的同意和一个最小分组规模,因为几家小组织之间的比较,可以被反推成关于其中某一家的结论。

在 B2B2B 学院里,学员数据归谁所有?

通常归客户组织,培训机构按其指示行事,平台厂商在两者之下。在第一家客户签约之前就定下来:谁是控制者,谁是处理者,谁来回应数据主体访问请求,以及客户离场时那些结果会怎么处理。最后这个问题回答得最差。如果一家客户是一棵子树,移除他们就是移除这棵子树;如果一家客户是散落各处、打了客户 id 标签的一堆行,那么离场就是一份有人指望它完整的清单。

Lurno 站在哪里,以及结构解决不了什么

Lurno 就是为这个形状而建的。一个组织包含若干子组织,每个子组织有自己的品牌和自己的自定义域名,并自动配置 TLS,所以客户的员工是在客户自己的地址上登录的。组织内部还有第二棵分支树,用来表示该客户的场地和部门,在某个分支上授予的角色会沿着它向下级联。内容在父组织一次编写,然后共享给某个客户组织,或者深拷贝进去。

租户边界是在 Postgres 里强制执行的,而不是在应用代码里:676 次迁移中共 868 条行级安全策略。一个漏掉过滤条件的接口返回的是空,而不是别人的员工——这是值得向任何厂商去验证的一条说法,包括我们的。已经在这上面跑起来的一种形态,不点名:一家企业学院在一个租户里为 15 家客户企业做培训。

有四件事,把树画对了也解决不了。这里说的是 Lurno 的情况;对其他任何人,也照样问这四件。

  • 向客户收费。支付和结账在开发中。今天客户是在平台之外开票的,平台记录的是他们享有什么权益。这和多数机构现有的资金流转方式是吻合的,但报名当下的刷卡付费,是一次需要谈的对话,而不是一个功能。
  • 把人弄进来。账号是通过邀请创建的;自助注册在开发中。实际可行的答案是:让客户管理员自己导入他们的人,而不是给你发一张表格。
  • 认证。合作方登录(静默 SSO)可用:客户自己的系统用一份签名断言把用户交接过来。SAML 和 OIDC 在路线图上,尚未交付。如果客户的 IT 部门已经决定了他们的员工要怎么认证,那就在第一次会上问,而不是第五次。
  • 标准。SCORM、xAPI 和 LTI 在产品里已有数据模型,但运行时在开发中。坚持要带自己的 SCORM 包进来的客户,会卡在这一点上,而这需要早说。

这套模型在机构一侧的版本,写在培训机构上。内部版本——子组织是你自己的各个大区,而不是另一家公司——写在企业上。

简短版

内容沿树向下共享。人、结果、品牌和报表横向隔开。共享课程用引用,而不是复制,并把每一次分叉都当成一个有人签字的决定。给每家客户一个范围限于他们自己节点的管理员,并弄清楚这个范围是在哪里强制执行的。然后为自己建一个横跨全部十六个节点的视图:那些让生意活下去的问题——哪家客户安静下来了、哪门课在所有地方都挂——只能从顶上问。