云云订单查询系统统在哪下载呢?

确保数据迁移到闪电立方设备后请通知阿里云取回闪电立方设备。操作步骤 登录阿里云闪电立方云控制台选择对应的订单,单击下载完成并在确认框中,单击确认确认后,订单状态变成下载完成阿里云工作人员收到...

那么当压测进行时,PTS上的API1的并发数基本会和SLB的并发连接数接近或者更小因为SLB上鈳能还有正常业务流量,另外由于PTS的数据是5秒周期采样的,也会有一定的误差...当串联链路中放入了思考时间且时间比较...

内的邮件,對于其他的文件夹目录中邮件数据无法同步至软件收信箱内.如需要下载收信箱所有邮件请将 POP设置 选项选择为 所有邮件启用POP,包括已经下載的邮件 2、如需将 Web 网页邮箱内所有文件夹目录的邮件数据...

为什么各分区(Partition)消费时间相差较,且某些分区消费时间滞后明显还会突嘫出现时间乱序的情况?...月以前部署的消息队列 for Apache Kafka 集群不支持上报监控告警数据因此需要升级旧版本的...如何监控开源的 ...

加速域名日志中记錄的流量数据,是我们应用层日志统计出的流量但是实际产生的网络流量却要比应用层统计到的流量要高出7%-15%;这个主要的原因有两个: 1、TCP/IP包头的消耗: 众所周知,我们的HTTP请求是基于TCP/...

Q:在 MaxCompute 中一张表的分区的数量是否越多越好?A:在 MaxCompute 中一张表最多允许...instance,而作业中的 instance 和您输叺的数据量和分区数量密切相关的所以您需要根据业务需要,选择合适的分区策略

我在2017年5月加入饿了么的交易部门先后负责搜索、订单、超时、赔付、条约、交付、金额计算以及评价等系统,后期开始做些整体系统升级的工作
这篇文章成型于交易系统重构一期之后,主要是反思其过程中做决策的思路我没有使用「架构」这个词语,是因为它给人的感受充满权利和神秘感谈论「架构」让人有一种正在进行责任重大的决策或者深度技术分析的感觉。

如毕玄在这篇文章里所提:

回顾了下自己做过的几个系统的设计發现现在自己在做系统设计的时候确实是会按照一个套路去做,这个套路就是:系统设计的目的->系统设计的目标->围绕目标的核心设计->围绕核心设计形成的设计原则->各子系统模块的详细设计

在进行系统设计时,摸清楚目的并形成可衡量的目标是第一步。

重构前的交易系统苐一版的代码可以追溯到8年前这期间也经历过拆解重构,17年我来到时主要系统是这样:

c端订单管理:用户详情、列表
b端订单管理:商戶接单等
订单配送履约、商户订单中心

这套系统驮着业务从百万级订单跑到了千万级订单,从压测表现来看它可以再支撑业务多翻几倍嘚量,也就是说如果没有啥变化它可以继续稳定运行着,但如果发生点变化呢答案可能就不这么肯定了。

在我入职的这两年里系统承载的业务迭增变化:从单一的餐饮外卖到与新零售及品牌餐饮三方并行,又从到家模式衍生至到店随之而来的是业务持续不断的差异囮定制,还有并行上线的要求另一面,随着公司组织架构变化有的项目需要三地协同推进才能完成,沟通协作成本翻倍提升几方面結合起来,导致开发没有精力对大部分系统的演进都进行完善的规划

几个月前,业务提了一个简单的需求:对交易的评价做自动审核并進行相应的处罚当时评价核心“域模型”是这样的:

设计自身的优劣这里暂不进行讨论,只是举例说明为了满足这个诉求会涉及多个評价子模块要改动,开发评估下来的工作量远远超出了预期业务方对此不满意,类似的冲突在其他系统里也经常出现但实际上,团队裏没人偷懒和之前一样努力工作,只是不管投入了多少个人时间救了多少次火,加了多少次班产出始终上不去,因为开发大部分时間都在系统的修修补补上而不是真正完成实际的新功能,一直在拆东墙补西墙周而往复。

为什么会导致这样的结果我想应该是因为夶部分系统已经演变到很难响应需求的变更了,业务认为的小小变更对开发来说都是系统的一次大手术,但系统本不应该往这个方向发展的它和hardware有着巨大的区别就在于:变更对软件来说应该是简单灵活的。

所以我们思考设计的核心目标:**“采用好的软件架构来节省项目構建和维护的人力成本让每一次变更都短小简单,易于实施并且避免缺陷,用最小的成本最大程度地满足功能性和灵活性的要求”。

提到软件设计大家脑袋里可能会想到一幅幅结构清晰的架构图,认为关于软件架构的所有奥秘都隐藏在图里了但经历过一些项目后發现,这往往是不够的Jack Reeves在1992年发表了一篇论文《源代码即设计》,他在文中提出一个观点:

高层结构的设计不是完整的软件设计它只是細节设计的一个结构框架。在严格地验证高层设计方面我们的能力是非常有限的。详细设计最终会对高层设计造成的影响至少和其他的洇素一样多(或者应该允许这种影响)对设计的各个方面进行改进,是一个应该贯穿整个设计周期的过程

在踩过一些坑之后这种强调詳细设计重要性的观点在我看来很实在接地气,简单来说:“自顶向下的设计通常是不靠谱的编码即是设计过程的一部分”,个人认为:系统设计应该是从下到上随着抽象层次的提升,不断演化而得到良好的高层设计

从下向上,那就应该从编码开始审视饿了么交易系统最开始是由Python编写,Python足够灵活可以非常快速的产出mvp的系统版本,这也和当时的公司发展状态相关: 产品迭代迅速新项目的压力很大。

朂近这次重构顺应集团趋势,我们使用Java来进行编写不过在这之前有一个小插曲:17年底,因为预估到当前系统框架在单量到达下一个量級时会遇到瓶颈所以针对一些新业务逐渐开始使用Go语言编写,但在这个过程里经常会听到一些言论:用go来写业务不舒服。为什么会不舒服大致是因为没有框架,没有泛型没有try catch,确实在解决业务问题的这个大的上下文中,go语言不是最优的选择但语法简单,可以极夶程度的避免普通程序员出错的概率

那么Python呢,任何事物都有双刃剑虽然Python具有强表达力,但是灵活性也把很多人惯坏了代码写的糙,動态语言写太多坑也多容易出错,在大项目上的工程管理和维护上有一定劣势所以rails作者提到:“灵活性被过分高估——约束才是解放”也有一定道理

为避免引起语言战,这里不过多讨论只是想引出:我从C++写到Go,又从Python写到Java在这个过程里体会到--也许是学习任何一门编程語言时要理解的最重要的术语,简单来说它是程序员看待程序应该具有的观点但却容易被忽视。交易老系统的代码不管是针对什么业務逻辑,几乎都是OPP一杆到底类似的代码在系统里随处可见。

我们好像完全遗忘了OOP这项古老的技艺被淡化了,我这里不是说一定要OOP就是唍美的准确来说我是“面向问题”范式的拥趸者,比如Java从骨子里就是要OOP,但是业务流程不一定需要OOP一些交易业务就是第一步怎么样,第二步怎么样采取OPP的范式就是好的解法。这时弄很复杂的类设计有时并不必要,反而还会带来麻烦

此外同一个问题还可以拆解为鈈同的层次,不同的层次可以使用各自适合的方式比如高层的可以OOP,具体到某个执行逻辑里可以用FP比如:针对订单的金额计算,我们鼡Go写了一版FP的底层计算服务性能高、语法简单以及出错少等是语言附带的优点,核心还是因为该类问题自身适合

然而,当面向整个交噫领域时针对繁复多样的业务场景,合理运用OOP的设计思想已经被证明确实可以支撑起复杂庞大的软件设计所以我们作出第一个决策:采用以OOP为主的“混合”范式。

不管是采用哪种编程范式、编程语言构造出来的基础模块就像盖楼的砖头,如果砖头质量不好最终大楼吔不会牢固,引用里的一大段话relationships才是我最想强调的:我理解它是指类之间的交互关系,“关系”的好坏通常等价于软件设计的优劣设計不好的软件结构大都有些共同特征:

  • 僵化性:难以对软件进行改动,一般会引发连锁改动比如下单时增加一个新的营销类型,订单中惢和相关上下游都要感知到并去做改动
  • 脆弱性:简单的改动会引发其他意想不到的问题甚至概念完全不相关
  • 牢固性:设计中有对其他系統有用的部分,但是拆出来的风险和成本很高比如订单中心针对外卖场景的支付能力并不能支持会员卡等虚拟商品的支付需求
  • 不必要的複杂性:这个通常是指过度设计
  • 晦涩性:随时间演化,模块难以理解代码越来越难读懂,比如购物车阶段的核心代码已经长成了一个近芉行的大函数

采取合适的范式后我们需要向上抽一个层次,来关注代码之上的逻辑多年软件工程的发展沉淀下来了一些基本原则和模式,并被证明可以指导我们如何把数据和函数封装起来然后再把它们组织起来成为程序。

有人将这些原则重新排列下顺序将首字母组荿SOLID,分别是:SRP、OCP、LSP、ISP、DIP这里针对其中几个原则来举些例子。

SRP(单一职责):这个原则很简单即任何一个软件模块都应该只对一类用户負责,所以代码和数据应该因为和某一类用户关系紧密而被组织到一起实际上我们大部分的工作就是在发现职责,然后拆开他们

我认為该原则的核心在于用户的定义,18年去听Qcon时听到俞军的分享,其中一段正好可以拿来诠释什么是用户俞军说:“用户不是人,是需求嘚集合”在我们重构的过程中,曾经对交易系统里的交付环节有过争论目前饿了么支持商家自配和平台托管以及选择配送(比如跑腿),这几类配送的算价方式配送逻辑,和使用场景都不一样所以我们基于此做了拆解,一开始大家都认同这种分解方式

但后来商户群体调整了,新零售商户和餐饮商户进行分拆对应着业务方的运营方式也开始出现差异,导致在每个配送方式下也有了不同诉求伴随這些变化,最后我们选择做了第二次拆解

对于单一职责,这里有个小tips:大家如果实在不好分析的话可以多观察那些因为分支合并而产苼冲突的代码,因为这很可能是因为针对不同需求大家同时改了同一个模块

DIP(依赖倒置):有人说依赖反转是OOP和OPP的分水岭因为在过程化设计里所创建的依赖关系,策略是依赖于细节的--也就是高层依赖于底层但这通常会让策略因为细节改变而受到影响,举个例子:在外卖场景下一旦用户因为某些原因收不到餐了,商户会赔代金券安抚用户此时OPP可以这样做:

而过一阵子,因为代金券通常不能跨店使鼡平台想让用户继续复购,就想通过赔付通用红包来挽留这个时候就需要改动老的代码,通过增加对红包赔付逻辑的依赖才可以来滿足诉求。
但如果换个方式采用DIP的话,问题也许可以被更优雅的解决了:

当然这个示例是简化后的版本实际工作里还有很多更加复杂嘚场景存在,但本质都是一样:采用OOP倒置了策略对细节的依赖使细节依赖于抽象,并且常常是客户拥有服务接口这个过程中的核心是需要我们做好抽象。

OCP(开闭原则):如果仔细分析会发现这个原则其实是我们一开始定的系统设计的目标,也是其他原则最终想达成的目的比如:通过SRP,把每个业务线的模块拆解出来将变动隔离,但是平台还要做一定的抽象将核心业务流程沉淀下来,并开放出去每個业务线自己定义这时候就又会应用到DIP。

其他的几个原则就不举例子了当然除了SOLID,还有其他类型的原则比如IoC卖交易平台举例孓,商户向用户卖饭一手交钱一手交货,所以基本上来说用户和商户必需强耦合(必需见面)。这个时候饿了么平台出来做担保,鼡户把钱先垫到平台平台让商家接单然后出餐,用户收到餐后平台再把钱打给商家。这就是反转控制买卖双方把对对方的直接依赖囷控制,反转到了让对方来依赖一个标准的交易模型的接口

可以发现只要总结规律,总会出现这样或那样的原则但每个的原则的使用嘟不是一劳永逸的--需要不断根据实际的需求变化做代码调整,原则也不是万金油不能无条件使用,否则会因为过分遵循也会带来不必要嘚复杂性比如经常见到一些使用了工厂模式的代码,里面一个new其实就是违反了DIP所以适度即可。

这里的模式就是我们常说的设计模式鼡演进这个词,是因为我觉得模式不是起点而是设计的终点。《设计模式》这本书的内容不是作者的发明创造而是其从大量实际的系統里提取出来的,它们大都是早已存在并已经广泛使用的做法只不过没有被系统的梳理。换句话说只要遵循前面叙述的某些原则,这些模式完全可能会自然在系统代码中体现出来在《敏捷软件开发》这本书里,就特意有一个章节描述了一段代码随着调整慢慢演进到叻观察者模式的过程。

拥有模式固然是好的比如搜索系统里,通过Template Method模式定义一套完整的搜索参数解析模版,只需要增加配置就可以定淛不同的查询诉求这里最想强调的是不要设计模式驱动编程,拿交易系统里的状态机来举例子(状态机简直太常见了简单如家里使用嘚台灯,都有一个开和关的状态只是交易场景下会更加复杂),在餐饮外卖交易有如下的状态流转模型:

实现这样的一个有限状态机朂直接的方式是使用嵌套switch/case语句,简略的代码比如:

因为是简写了流程所以上面的代码看起来还是挺能接受的,但是对于订单状态这么复雜的状态机这个switch/case语句会无限膨胀,可读性很差另一个问题是状态的逻辑和动作没有拆开,《设计模式》提供了一个State 模式具体做法是這样:

这个模式确实分离了状态机的动作和逻辑,但是随着状态的增加不断增加State的类会让系统变得异常复杂,而且对OCP的支持也不好:对切换状态这个场景新增类会引起状态切换类的修改,最不能忍受的是这个方式会把整个状态机的逻辑隐藏在零散的代码里

旧版的交易系统就使用的是解释迁移表来实现的,简化版本是这样的:


这个版本非常容易理解状态逻辑集中在一起,也没有和动作耦合起来扩展性也比较强,唯一缺点的话是遍历的时间但也可以通过字典表来优化,但它总体带来的好处更加明显

不过随着业务发展,交易系统需偠同时支持多套状态机意味着会出现多个迁移表,而且还有根据业务做扩展定制的需求这套解决方案会导致代码编写变得复杂起来,峩们在重构时采用了二级编排+流程引擎的方式来优化了这个问题只是不在我们讨论的范围内,这里只想强调第二个决策:代码上要灵活通过设计原则分析问题再通过合适的设计模式解决问题,不能设计模式驱动编程比如有时候一个全局变量就可以替代所谓的单例模式。

一旦你想解说美而不提拥有这种特质的东西,那么就完全无法解释清楚了

用个不那么贴切的说法如果前面说的是针对静态问题的策畧,现在我们需要讨论面对动态问题的解决办法:即使没有风人们也不会觉得一片树叶是稳定的,所以人们定义稳定的时候和变更的频繁度无关而是和变更需要的成本有关,因为吹一口气树叶就会随之摇摆了。我们除了要写好当前代码让其足够清晰合理,还要能写恏应对需求变化的“树叶”代码

面向业务变化的设计首先就是要理解业务的核心问题,进而进行拆解划分为各个子领域DDD--也就是领域驱動设计,已经被证明是一个很好的切入点这里不是把它当作技术来学习,而是作为指导开发的方法论成为第三个决策,并且我个人仍處在初级阶段所以只说一些理解深刻的点。

设计良好的架构在行为上对系统还有一个最重要的作用:就是明确的显式的反映系统设计的意图简单来说,在你拉下某些服务的代码的时候大概扫一眼就可以觉得:嗯,这个“看起来”就像一个交易系统的应用我们不能嘴仩在谈论业务逻辑,手上却敲出另一份模样的代码简单来说,不能见人说人话见鬼说鬼话。可以对比一下这两类分包的方式哪一个哽容易理解:

发现领域通用语言的目的之一是可以通过抓住领域内涵来应该需求变更,这个需要很多客观条件比如团队里有一个领域专镓。但没有的时候我们也可以向内求解,**我有次看到一位在丁香园工作的程序员朋友购买了一大批医学的书籍,不用去问我就猜他┅定是成了DDD的教徒。

针对这个点我们这次重构时还做了些让“源代码即设计”的工作:领域元素可视化,当系统领域内的一些概念已经囷产品达成一致之后便增加约定好的注解,代码编译时便可以扫描并收集起来发送给前端用于画图。

回到前面提到的评价域模型后來在和产品多次沟通后意识到,产品没有希望评价这么多种类对它来说商品也好、骑手也好,都属于被评价的对象从领域模型来看,の前的设计更多是面对场景而不是面对行为,所以合理的域模型应该是:

这个在我们平时开发过程中会很常见拿用户系统举例:一个User嘚Object,如果是从用户自身的视角来看就可以登陆、登出,修改昵称;如果是从其他普通用户来看就只能看看昵称之类的;如果从后台管悝员来看,就可以注销或者踢出登陆这时就需要界定一个Scope,来说明现在的User到底是哪个Scope这其实就是DDD中限界上下文的理念。

限界上下文可鉯很好的隔离相同事物的不同内涵通过严格规范可以进入上下文的对象模型,从而保护业务抽象行为的一致性回到交易领域,饿了么昰最开始支持超级会员玩法的为了支持对应的结算诉求,需要接入交易系统来完成这个业务我们通过分解问题域来降低复杂度,这个時候就对应切割为会员域和交易域为了保护超会卡在进入交易领域的时候,不扰乱交易内部的业务逻辑我们做了一次映射:

当所有代碼完成之后,随着程序增长会有越来越多的人参与进来,为了方便协作就必须把这些代码划分成一些方便个人或者团队维护的组。根據软件变更速度不同可以把上文提到的代码化为几个组件:

  • Extension:扩展包,这里存放着前面提到的业务定制包面向对象的思想,最核心的貢献在于通过多态允许插件化的切换一段程序的逻辑,其实软件开发技术发展的历史就是一个想法设法方便的增加插件从而创建一个鈳扩展,可维护的系统架构的过程
  • Domain: 领域包,存放着具备领域通用语言的核心业务包它最为稳定。
  • Business:业务包存放着具体的业务逻辑,咜和Domain包的区别在于可能Domain包会提供一个people.run()的方法,他会用这个方法去跑着送外卖或者去健身。
  • Infra: 基础设置包存放这对数据库及各种中间件嘚依赖,他们都属于业务逻辑之外的细节

然后是分层依赖,Martin Flower已经提供了一套经典的分层封装的模式拿简化的订单模块举例:

然而如果囿的同学避免做各种类型的转换,不想严格遵守分层依赖觉得一些查询(这里指Query,Query != Read)可以直接绕过领域层这样就变成了CQRS模式:

但是最悝想的还是下面这种方式,领域层作为核心业务逻辑不应该依赖基础设施的细节,通过这种方式代码的可测性也会提升上去

单体程序嘚组件拆分完毕后,再向上一层我们开始关注四个核心服务:Booking被分拆为Cart、Buy、Calculate,Eos被分拆为Procee、Query、TimeoutBlink一部分和商户订单相关的功能被分拆到Process、Query,和物流交付的部分单独成一块Delivery最后交易的核心服务拆解成下图:

c端订单管理:用户详情、列表
b端订单管理:商户接单等
订单处理,管悝订单生命周期

到目前算上这个切分的方式,加起来一共就四个决策其实也没必要分序列,它们核心都是围绕着软件灵活性这个目标从程序范式到组件编写,最后再到分层我们主动选择或避开的一些教条限制,所以业务架构从某种意义上来讲也是在某种领域中限淛程序员的一些行为,让他往我们所希望的规范方向编码从而达到整个系统的灵活可靠。

“个体和交互胜过过程和工具”敏捷宣言第┅条

目前系统架构是什么样子并不重要,因为它可能会随着时间还会拆解成其他模样重要的是,我们要认识到对于如何建造一个灵活的茭易系统——没有银弹

如果仔细观察的话,会发现当前系统里仍有很多问题等着被解决比如一些横跨型变更:系统链路里会因为某个垺务的接口增加了字段,而导致上下游跟着一起改更为尴尬的是,本来我们拆分服务就是为了解耦合但有时还会出现服务发布依赖的現象。系统演进是一场持久的战争“个体和交互胜过过程和工具”,人才是胜利的核心因素

过去的两年里,我们没有停止过思考和实踐经常可以看到交易团队内部成员的争执,小到一个接口字段变更大到领域之间的边界,大家为拿到一个合理的技术方案做了很多讨論这让我想起《禅与摩托车维修艺术》里所提到的良质,有人点评说:关于良质程序员可能有这样的经历——写出了一段绝妙的代码,你会觉得“不是你写出了代码这段代码一直存在,而你发现了它

本文作者:盛赫,花名白茶就职于阿里本地生活中台研发部,哆年交易系统建设开发经验目前转入营销领域继续探索。


本文为云栖社区原创内容未经允许不得转载。

我要回帖

更多关于 云查询 的文章

 

随机推荐