架构师如何考虑软件的可持续迭代 段和尘 毕业于中科院,十二年从事移动端Android研发。百度→创业→魅族→创业→腾讯→字节。 目前负责头条的Android客户端架构。 又失败了咋办?《在岁月中远行》 1.你所了解的架构是什么? A.一个完美的设计,能够解决各种软件问题 B.没有完美的架构,需要不断对实现做重构 2.你觉得架构师们每天都在干什么? A.每天都根据用户的需求,讨论如何做软件设计 B.每天都在填坑,填不完的坑… 01架构面临的问题 02架构常见的手段 03架构演进的例子 04成为优秀架构师 01 架构面临的问题 稳定期 壮年期 不断重构优化落地标准规范 贵族期 青春期 适应业务变化应对代码膨胀 传说中的屎山多方跑马圈地 学步期 快速融合代码确定工程架构 流程没完没了各方拼命甩锅 官僚期 孕育期 婴儿期 如何抽象建模 代码已经腐朽 消亡期 我有一个想法 如何技术选型 躺平摆烂逃离 咱们重新来过 成长期:如何快速成长?衰退期:如何对抗衰老? 交叉问题 •跨端调用:JsBridge/JNI… •多端一致:统一接口层/统一实现层… •数据通信:IDL协议/压缩/安全… 前端 •语言:Js/Ts/Dart/H5/Css... •框架:RN/Angular/Vue/jQuery... •组件:ElementUI/AmazeUI… 公共问题 •编程思想:OOP/AOP/IoC… •问题分解:按业务/按技术… •领域建模:接口设计/DSL/… •服务治理:模块化/容器化… •流程机制:敏捷开发/单测手段… •架构标准:公约文档/数据监测… 客户端 •语言: Java/Kotlin/Swift/C#... •框架: GMS/Flutter/Cocos… •平台:Android/iOS… 服务端 •语言:Java/Python/Go... •框架:Spring/Flask/Beego.. •平台:CentOS/Windows/… 只要业务继续发展,越来越复杂就是必然趋势。 理解成本变高 •宏大的规模是不好理解的 •复杂的结构是不好理解的 预测难度变大 •业务变化不可预测 •技术变化不可预测 应用框架层 App开发者直接使用的接口层,UI的实现、数据的处 理、资源的使用,都是利用这一层的API BinderIPC 提供跨进程访问的能力,App可以高效的访问由系统进程暴露的能力。App进程与系统进程之间的通信是典型的C/S模型。 ndroidOS 系统服务 提供窗口管理、相机、音视频等重要的系统能力,包 含各种子系统,内部逻辑十分庞大,往下调用HAL层封装的硬件能力;往上通过Binder暴露可以远程调用的API 硬件抽象层 屏蔽底层不同驱动的差异,使得系统服务层可以快速 适配到不同的硬件设备 Linux内核 CPU、内存、唤醒服务等重要的驱动实现都是基于该 层操作系统的核心实现。 ViewController Activity iOS Android Service“BackgroundMode” IntentsSegues/ViewControllers ContentProviderCoreData LayoutsStoryboardsandscenes 应用框架层 EventKit、GameKit、MapKit、PushKit 核心服务层 Location、Motion、Health、GPS、 Telephony、Foundation… 图形图像层 ULKit、Animation、Graphics、Images 内核层 Bluetooth、Security、Accessories… UI框架层 提供不同样式的组件和动画,声明式UI。采 用了Dart作为编程语言,能够同时支持JIT和AOT,在开发调试和运行阶段都能有不错的效率提升 引擎层 将上层定义的UI树转换成屏幕像素,提供平 台调用接口和Dart虚拟机 嵌入层 Flutter引擎需嵌入不同的平台: Android/iOS/Windows/Linux等 小结 •架构设计是为了解决特定领域不同发展阶段的业务问题 •不同领域的架构有明显的技术差异,但也有很多相似性 •架构不仅面临技术挑战,还要应对组织业务膨胀的熵增 •移动端需要利用有限的设备资源设计符合小屏幕的架构 02 常见的架构手段 架构模型 优点 缺点 MVC •模块职责划分明确。主要划分层M,V,C三个模块,利于代码的维护 •View和Controller容易膨胀•View与Model没有完全分离 MVP•View与Model完全分离,可以修改视图而不影响模型,交互都发生Presenter •Presenter与View的交互是通过接口来进行的, 方便单元测试 •页面逻辑复杂的话,相应的接口也会变多,增加维护成本 MVVM•ViewModel与View的耦合更彻底,ViewModel 只负责处理和提供数据 •ViewModel里面只包含数据和业务逻辑,没有UI,方便单元测试 •数据绑定使得程序较难调试,因为数据都是自动更新到UI 户操 View 调用控制器 通知 Controller 更新数据 Model View 数据 定UI 通知数据更新 ViewModel Model 用户操作 绑 更新数据 Binding 单向绑定:数据→视图双向绑定:数据←→视图 DataBinding单向自动更新双向自动更新Android使用特征典型场景 Static❌❌@[]界面模板,不同区域填充不同数据 Observable✅❌Observable,@BindAdapter不同状态显示不同界面,界面局部更新 Two-way✅✅@=[],@InverseBindingAdapter输入框,选择框 控制反转,是一种模块化的编程思想,有不同的实现方式。 •依赖注入(DenpendencyInjection) •ConstructorInjection(eg:Spring@Autowired,在构造的时候,将被依赖的对象注入) •SetterInjection(eg:Spring通过setter方法将依赖的对象注入) •ParameterInjection(eg:Retorfit@Header,@Query, @Path) •Interface/Methodinjection(eg:Callback) •依赖查询(DependencyLookup) •ServiceLocator(eg:ServiceProvideInterface) •ContextualizedLookup(eg:AndroidContext) •TemplateMethod(eg:ViewDataBinding...) •Strategy(eg:Scheduler,Event-Driven) •不同架构手段的共同目标是高内聚低耦合 •找到适合业务场景的架构而不是炫技滥用 •一个复杂的系统是多种架构模型的组合体 03 架构演进的例子 孕育期 做一个信息流产品,可以无限刷的列表 •首先,需要实现一个列表,支持上下滑动 •然后,每次滑动,都需要请求服务端数据 •接着,列表的每一项都需要响应点击操作 •… 婴儿期 产品功能开始变多,需要拆分模块 •首先,需要支持图文内容的组合混排显示 •接着,需要引入账号体系,用户注册登录 •然后,用户可以收藏感兴趣的内容并分享 •… •继续,场景越来越多,拆分网络和多线程 •… 业务场景变多,需要拆分业务 •首先,支持用户发布文章,并给予奖励 •接着,视频这个重要的内容也需要支持 •然后,不同业务之间越做越大需要拆分 •… •继续,拆分出视频业务,架构自成体系 •… •继续,拆分出中台业务,供多业务使用 •… PresentationLayer Data LogicBusinessLayer Insert/Update/Delete/Query PersistenceLayer RW Database File 表现层:接收用户输入,获取数据,呈现界面 业务层:处理业务数据,数据流转,安全检查持久层:提供数据的增删改查能力 存储层:按照特定的格式存储数据 优点 •结构简单清晰,易于理解和管控 •层级关系适合不同技能人员分工 缺点 •对层级管控要求严格,灵活性低 •为了解耦容易拆分出很多中间层 不同业务和模块混合,需要解耦 •首先,需要约定模块可以对外提供的能力 •接着,模块之间需要遵循相同的调用方式 •然后,旧的模块需要按照相同标准来改造 •… •继续,使用方不应该直接依赖于实现方 •… 优点 •适用广泛,容易扩展出不同变种 •性能较好,支持消息的异步处理 事件Event:一个消息,譬如触摸了屏幕、点击了按钮 事件处理器Processor:事件的实际消费者,对关注的消息进行订阅,消息处理的过程很灵活,可以在当前处理器“吞”掉一个消息,也可以继续将消息交由其他消息队 列处理(责任链) 事件队列EventChannel:消息队列,事件将以消息的形式发布到队列中,并设计特定的派发机制来处理队列中的消息 缺点 •缺少约束,消息处理器容器膨胀 •处理链路容易变长,理解成本高 1.事件发布:「YouMoved」事件被处理器 「Customer」接收处理 2.事件处理:「Customer」将事件转换为「ChangeAddress」,并发布到消息队列 3.事件处理:「Quote」和「Claims」两个事件处理器都订阅了「ChangeAddress」消息,并收到了从消息队列派发出的消息 4.事件处理:「Quote」将消息转换为「RecalcQuote」并将其派发给「Notification」这个处理器; 「Claims」将消息转换为「UpdateClaims」,并将其派发给「Notification」和「Adjustment」 实例 业务变得更加内聚,需要灵活插拔 •首先,扫一扫等能力不是所有场景都需要 •接着,视频的子能力可以拆解后按需使用 •然后,越来越多的业务想动态化发布产物 •… •继续,动态化发布引入很多问题需要调优 •… 优点 •高扩展性,需要什么就开发什么 •插件隔离,能够简化业务耦合度 宿主容器:CoreSystem,一般不包含具体业务逻辑, 而是从中抽象出模型,相当于一个运行环境,供不同业 务以插件的形式在其中运行 插件:Plug-inComponent,具体的业务实现,通过一定的技术手段挂载到宿主容器中,插件一般可以访问宿主的资源 缺点 •对宿主容器的要求很高,且宿主不易扩展 •插件的注册和通信机制通常比较复杂 稳定期 用户规模和团队稳定,历史包袱重 •首先,经过前期快速发展已经积累了大量历史包袱 •接着,需要深入了解业务才能设计出更合理的架构 •然后,很多改动牵一发动全局,代码被迫变得更差 •… •继续,新旧技术栈共存,版本依赖冲突,冗余代码 •… 稳定期–微服务架构 优点 •健壮性好,单个服务不影响全局 •服务隔离,服务之间互不相耦合 请求接入:服务使用方发起请求,请求以一定的方式(可以直接调用,也可以跨进程调用)发送到服务注册中心, 等待请求的处理 服务调度:Broker,将服务请求调度到对应的微服务节点上进行处理 微服务:ServiceComponent,一个高度内聚的模块集合,对外暴露服务接口。每一个微服务都是独立的,分别向服务注册中心注册自身所能提供的服务接口 缺点 •容器出现服务数量腹胀难以管控 •服务发现和通信需要额外的成本 贵族期 期望高 •良好的顶层设计,从上到下有统一的认知 •遵循共同的规范,写出让人舒适的代码 •有没有“一劳永逸”的设计,可保基业长青 •什么时候能从架构工作中找到成就感 责任大 •设计不合理:这谁写的,看小爷我推到重来 •使用不规范:这压根就不该这么用 •逻辑太晦涩:这尼玛谁看得懂 •编码坑太多:这特么是隐藏技能啊,悄无声息改代码 事情难 •业务历久弥新,历史包袱叠加新的场景 •随便动动刀子就拔出萝卜带出泥 •技