整理和总结我自己的软件架构知识体系

最近开始整理自己实际项目中用到的和可能用到的软件架构体系,我打算每种架构都好好写文章,并且根据自己的实际情况定制出相应的架构,我还希望把他编写成一个codesmith模板,可以实现自动化代码。前面7种是具体落地的软件架构,最后1种是架构设计思想。 1、极简数据库访问架构,这种架构非常适合编写一些小软件,里面只有一个SQLHelper文件,并且模仿三层架构,拥有DAL类和BLL类,最后加上一个程序入口Program。 2、简单三层架构,这种架构非常适合开发小型项目,web层就是MVC或者API,并且拥有BLL层和DAL层。 3、N层架构,这是一种适合大型项目的开发架构,但是更多是基于数据库构建的系统,没有基于DDD或者其他好的软件设计模式。 4、基于AutoFac、依赖注入和模板模式的多层架构。 5、基于ABP框架的架构,完全按照DDD设计模式开发,可能有些臃肿,但是非常适合大型软件和系统。 6、基于整洁架构的框架,完全按照DDD并且结合各种软件技术。 7、垂直切片架构,这是一种和传统分成架构完全不一样的架构,适合已经熟悉DDD和各种架构模式的成熟团队使用,并且适合开发微服务架构下的子系统。 8、基于清晰架构的微服务软件架构设计思想,这不是具体的实现,而只是一种架构大局观,用于指导大型的软件架构设计。

April 3, 2023

关于软件的架构:微服务架构、清晰架构、整洁架构、洋葱架构、六边形架构、垂直切片架构、基于DDD分层架构、三层架构

这段时间一直在重构公司的项目代码,所以非常关注软件架构的设计,回顾我的架构接触史:第一次接触C#和.NET的时候就知道了三层架构和多层架构,当时被DAL和BLL等分层震惊了,原来软件设计还有那么多门道;毕业之后工作一直在使用多层架构,接着是MVC架构的出现,这种快速开发web站点的架构横扫整个行业;后来进入大厂工作,接触到了DDD;最近几年微服务架构又风声水起。 整体下来我最开始用了三层架构、然后是多层架构,接着是MVC架构配合多层架构,最后到了微服务架构,现在深刻理解那句话:没有最好的架构,只有最合适的架构。 网上看到有个人总结传统的架构演变之路: 三层架构:这是最简单、同时也是最成熟的软件应用程序架构,它将应用程序组织到三个逻辑和物理计算层中,包括表示层或用户界面、用于处理数据的应用(业务逻辑)程序层和用于存储和管理应用程序关联数据的数据层。大型项目中很少用,但是我还是会在一些小工具和小应用里运用。 多层架构:这种架构是三层架构的升级版,就是增加更多层对系统进行隔离,提高可扩展性和复用性,当然也增加了系统的复杂度。这是我刚毕业那家年用得最多的架构。 基于DDD的分层架构:采用领域驱动设计的思想设计的多层架构,以领域模型为核心、使用依赖注入和控制反转等技术来实现软件,对系统进行解耦,获得最大限度地可维护性和可扩展性。这是我现在在项目中用得最多的架构。 领域模型准确反映了业务语言,而传统数据对象除了简单setter/getter方法外,没有任何业务方法,即失血模型,那么DDD领域模型就是充血模型(业务方法定义在实体对象中)。首次清晰描述了领域驱动的分层实现并统一了业务语言。单一职责、低耦合、高内聚、业务内核沉淀。 六边形架构:让用户、程序、自动化测试和批处理脚本可以平等地驱动应用,让应用的开发和测试可以独立于其最终运行的设备和数据库。 左侧: 代表 UI 的适配器被称为主适配器,它们发起了对应用的一些操作,端口(应用层API)和它的具体实现(controller实现)都在应用内部。右侧: 表示和后端工具链接的适配器,被称为从适配器,它们只会对主适配器的操作作出响应,端口在应用内部(业务接口),具体实现(impl)在应用之外。 **洋葱架构:**在端口和适配器架构的基础上贯彻了将领域放在应用中心,将传达机制(UI)和系统使用的基础设施(ORM、搜索引擎、第三方 API…)放在外围的思路。洋葱架构在业务逻辑中加入了一些在“领域驱动设计”中被识别出来的层次。 围绕独立的对象模型构建应用。内层定义接口,外层实现接口。依赖的方向指向圆心。所有的应用代码可以独立于基础设施编译和运行。职责分离更彻底,高内聚低耦合。更好的可测试性和可维护性。 **整洁架构:**这套架构是站在巨人的肩膀上,把MVC、EBI、端口适配器、洋葱架构、DDD融会贯通,形成了一套落地实践方案。 清晰架构:融合 DDD、洋葱架构、整洁架构、CQRS等一系列架构的信息,这种架构很复杂,你可以根据实际情况来选择。 微服务架构:微服务架构的诞生是因为docker的兴起,因为可以更好地管理各种部署,所以可以将一个大型系统拆分部署到docker之中,每个小服务都能实现集群部署。我个人认为并非一开始就要上微服务架构,架构是不断演化的过程,哪怕是一开始就定位很大系统,可以先划分出基本的服务就行(比如登录系统、支付中心、内容服务、接口开放平台和后台系统),更细的划分可以随着业务的发展不断演化——当然前提是要一直保证代码的整洁和简洁、遵循基本的面向对象代码原则。 垂直切片架构:我是最近才接触到了这种架构,我们的项目最近用到了MediatR和AutoMapper,这两个开源项目的作者Bogard JIMMY BOGARD在自己的项目中实现了这种架构,通过反思洋葱架构和整洁架构等各种分层和抽象得出来的一种架构,作者不建议做太多的分层,因为各种以来会导致大量的问题,作者认为在微服务架构里面特别实用,当然代码也会准从DDD的设计原则。作者认为在所谓传统正常的“N层”或六边形或任何架构中,通过垂直切片移除这些层障碍,并沿着变化轴聚合在一起:

March 24, 2023

垂直切片架构 - Jimmy Bogard

许多年前,我们开始了一个新的长期项目,首先,我们基于洋葱架构构建了它的架构。在几个月内,这种风格开始显示出裂缝,我们从这种架构转向CQRS。随着转向CQRS,我们开始围绕垂直切片而不是层(无论是平面还是同心,它仍然是层)构建我们的架构。从那以后,在过去7到8年左右的时间里,围绕垂直切片架构构建应用程序和系统的所有方式一直是我们独有的方法,我无法想象回到分层架构方法的限制。 传统的分层/洋葱/清洁架构在其目标是单体的: 这种架构方式问题是实际上只适用于系统中的少数典型请求。此外,我倾向于看到这些架构严重拟合,严格遵守依赖关系管理规则。在实践中,我发现这些规则很少有用,并且你开始得到很多关于真正不应该被抽象的抽象(控制器必须与必须使用存储库的服务进行对话)。 相反,我想对我的系统采用量身定制的方法,我将每个请求视为如何处理其代码的独特用例。因为我的系统整齐地分解为“命令”请求和“查询”请求(HTTP-land中的GET与POST / PUT / DELETE),所以向垂直切片架构的移动使我使用了CQRS。 什么是“垂直切片架构”?在这种风格中,我的架构是围绕不同的具体请求功能而构建的,通过这种方法,我们的每个垂直切片都可以自行决定如何最好地满足请求: (对于获取订单,直接使用ORM转换为DTO,对于订单细节使用原生SQL转换为DTO,对于发票,使用基于聚合根的事件溯源,取消订单使用存储过程,这是一种微服务风格) 在所谓正常的“n层”或六边形或任何架构中,通过垂直切片移除这些层障碍,并沿着变化轴聚合在一起: 在应用程序中添加或更改功能时,通常会在应用程序中涉及到许多不同的“层”。现在改为沿着切片垂直将这些功能聚合在一起。 最小化切片之间的耦合,并最大化切片内的聚合。 通过这种方法,大多数抽象都消失了,我们不需要任何类型的“共享”层抽象,如存储库,服务,控制器。有时我们仍需要这些工具,但我们将交叉切片逻辑共享保持在最低限度。 通过这种方法,我们的每个垂直切片都可以自行决定如何最好地满足请求。 “企业架构模式”一书中的旧域逻辑模式不再需要成为应用程序范围内的选择。相反,我们可以从简单的(事务脚本)开始,并简单地重构从我们在业务逻辑中看到的代码气味中出现的模式。新功能只添加代码,您不会更改共享代码并担心副作用。非常自由! 但是,这种方法有一些缺点,因为它确实假设您的团队了解代码气味和重构。如果您的团队不理解“服务”在将逻辑推送到领域时自己却做得太多相关业务逻辑事情,那么这种模式可能不适合您。(服务类似餐厅服务员,服务员不应该做决定,只是协调者,当然如果你想退菜,服务员会决定说:不能退,菜已经烧了。) 如果您的团队确实理解了重构,并且能够识别何时将复杂的逻辑推入域,进入DDD服务应该是什么,并且熟悉其他Fowler/Kerievsky重构技术,那么您会发现这种架构风格能够远远超过传统的分层/同心架构。 来源:https://www.jdon.com/53095.html

March 24, 2023

关于android手机刷机的各类问题总结

这两周来频繁刷机,更新系统又还原系统,弄懂了很多事情,写一篇日志总结,以便以后查询使用,也希望能够看到的人能避免踩坑,也许第三部分更有价值,可以直接跳过去看。 一、关于刷机 刷机有风险,可能会变砖。不过因为很多手机都留了最后一手:FASTBOOT,所以总能把手机就回来 。 刷机的有不少的方法:卡刷、线刷、OTA。卡刷:一般来说卡刷简单的能够理解为在内存卡中刷机,就是进入Recovery中刷入系统;线刷:线刷事实上能够简单的理解为用USB线刷机;OTA:仅仅要在系统中在线升级就能够成功刷机。 刷机之前需要解锁,一般开放点的厂商都会给相应的解锁工具和权限,只要愿意折腾都可以完成解锁。其实解锁和刷机就是为了获得root权限,这样可以对系统做更大的定制,当然也降低手机的安全性。 刷机要根据自己的手机型号找到相应的刷机包(也就是ROM),要注意是卡刷还是线刷,卡刷就是拷贝到手机卡里刷机,线刷就是用电脑的工具通过手机数据线刷机。 小米的MIUI系统有大量的刷机包,我一直都喜欢用欧版,地址是:https://xiaomi.eu 内网地址是:https://sourceforge.net/projects/xiaomi-eu-multilang-miui-roms/ 二、获得root权限 我用的是小米手机,有两种方法,这里从别的地方搬过来,我只把重要的步骤留下,并且指出可能的坑点。 1、通过替换boot.img方式安装Magisk获取Root权限指南 来源原文:https://miuiver.com/install-magisk-for-xiaomi/ 现在获取手机 Root 主要通过安装 Magisk 实现,这篇文章将以新手视角介绍如何安装使用 Magisk。 准备工作:安装 Magisk 过程中需要用到 fastboot 命令,需要刷写手机 boot 或 Recovery 分区,请确保手机已完成 BL 解锁,不然无法进行 。正常情况下不会丢数据,但是建议都先外置备份手机数据再操作。 下载Magisk 地址 https://github.com/topjohnwu/Magisk/releases (选择下载最新版,然后安装到手机)。 提取相应文件:查看手机上运行的系统版本是多少,下载对应系统版本刷机包,从里面提取相应文件(请见下面说明)。如果之前 Ramdisk 查询结果为“是”,请提取 boot.img 文件如果之前 Ramdisk 查询结果为“否”,请提取 recovery.img 文件。 文件提取方法:如果系统版本有线刷包,可以直接解压提取;如果系统版本只有卡刷包,需要从解压的 payload.bin 文件里提取(老机型卡刷包可以直接提取),将提取到的文件复制到手机上。 生成修补文件:手机打开 Magisk 软件,点击 Magisk 卡片中的“安装”按钮。点击“选择并修补一个文件”,选择之前提取到的 boot.img 或 recovery.img 文件,点击“开始”,然后等待生成修补文件。据 Magisk 文档指出,小米有个别机型 Ramdisk 结果可能不能准确检测。如果修补 recovery.img 文件失败,可以尝试用 boot.img 修补,后面安装也遵循 Ramdisk 结果为“是”的做法。将生成的修补文件复制到电脑上(修补文件默认保存在手机内部存储 Download 目录)。 刷写修补文件:将手机关机,长按音量下键+电源键进入 FASTBOOT 模式,用数据线连接到电脑。电脑打开存放修补文件的文件夹,按住键盘 Shift 键,同时鼠标右键点击文件夹空白处,在右键菜单点击“在此处打开 Powershell 窗口”,然后运行下面刷写命令(命令中的文件名请先自行修改)。如果之前修补 boot.img 文件请用这个命令:.\fastboot flash boot magisk_patched-25200_pU6ZV.img;如果之前修补 recovery.img。文件请用这个命令:.\fastboot flash recovery magisk_patched-25200_pU6ZV.img。刷写完成后用下面命令重启手机:.\fastboot reboot ...

February 15, 2023

.NET7下string的改进

string是开发过程中,使用频度最高的类型之一,所以在构建类型时作了很多处理,如“不可变性”,“保留性”等特点。string的常量是在"“引号中进行赋值的。 var str1 = "这是一段文字"; Console.WriteLine(str1); 为了字符串的格式化,引入了$““定义方式,这样就可以在字符串中用{}来标注格式化的内容了。 var str2 = $"时间:{DateTime.Now}"; Console.WriteLine(str2); //输出结果是:时间:1/6/2023 15:37:13 var str2_1 = $"时间:{DateTime.Now:yyyy-MM-dd}"; Console.WriteLine(str2_1); //输出结果是:时间:2023-01-06 为了解决字符串内容的换行,引定入@"",来定义有换行的字符串,比如下面的一条SQL查询,可以按格式化后的样式来定义。$和@可以混用,不分先后。 var str3 = @"SELECT ID ,Question ,Score ,QuestionTypeID ,SubjectTypeID FROM Questions"; Console.WriteLine(str3); var str3_1 = @$"SELECT ID ,Question ,Score ,QuestionTypeID ,SubjectTypeID FROM Questions WHERE Score>{10}"; Console.WriteLine(str3_1); 其实原始字符串还解决了一个问题,就是字符串中有"的问题,以前需要有转义字符来实现,现在原始字符串都搞定了。 Console.WriteLine("\"a\" 是小写的");//通过\来转义 Console.WriteLine(@"""a"" 是小写的");//前缀是@时,通过"转义 最佳demo是json字符串的定义,用原始字符串的方式定义json字符串,最合适不过了。 var jsonString = """ { "irstName": "John", "astName": "Smith", "ex": "male", "ge": 25, "ddress": { "treetAddress": "21 2nd Street", "ity": "New York", "tate": "NY", "ostalCode": "10021" } } """; Console.WriteLine(jsonString) 通过下图,看到的json原始字符串,一目了然: ...

January 12, 2023

武鸣要通高铁了:南宁北站封顶

这两天桂南高铁终点站南宁北站封顶,大概率武鸣今年要通高铁了,很难想象这是县城通的第一条铁路,之前一直没有任何列车通过武鸣。看新闻说以后从南宁北站始发的有:南宁北到广州、南宁北到深圳,南宁北到福州、厦门,南宁北到凭祥,那么无论以后我们在北边还是去了南边,回家都会方便很多。 [caption id=“attachment_1824” align=“aligncenter” width=“1417”] 南宁北站:汉语标题的左边是壮语发音,右边是英语。[/caption] 这条贵阳到南宁的高铁是广西第一条时速350公里的高铁,广西的交通从来都不是优先出海,而是往山区延伸。想起小时候经常听说轰轰烈烈的南昆铁路,长大了才知道这是为他人做嫁衣,这次也差不多。 改开几十年,西南几个“难兄难弟”,一直都只能给珠三角和东部输送廉价劳力,现在经济下行后才开始说要共同富裕,但是只有把社会资源扩容,把饼做得更大,才有可能解决发展不均衡。

January 12, 2023

当你在浏览器地址栏输入一个网址的时候,会发生什么?

作为一个软件开发者,你一定会对网络应用如何工作有一个完整的层次化的认知,同样这里也包括这些应用所用到的技术:像浏览器,HTTP,HTML,网络服务器,需求处理等等。 本文将更深入的研究当你输入一个网址的时候,后台到底发生了一件件什么样的事 1 首先,你得在浏览器里输入要网址: 2 浏览器查找域名的IP地址 导航的第一步是通过访问的域名找出其IP地址。DNS查找过程如下: 浏览器缓存 – 浏览器会缓存DNS记录一段时间。 有趣的是,操作系统没有告诉浏览器储存DNS记录的时间,这样不同浏览器会储存个自固定的一个时间(2分钟到30分钟不等)。 系统缓存 – 如果在浏览器缓存里没有找到需要的记录,浏览器会做一个系统调用(windows里是gethostbyname)。这样便可获得系统缓存中的记录。 路由器缓存 – 接着,前面的查询请求发向路由器,它一般会有自己的DNS缓存。 ISP DNS 缓存 – 接下来要check的就是ISP缓存DNS的服务器。在这一般都能找到相应的缓存记录。 递归搜索 – 你的ISP的DNS服务器从跟域名服务器开始进行递归搜索,从.com顶级域名服务器到Facebook的域名服务器。一般DNS服务器的缓存中会有.com域名服务器中的域名,所以到顶级服务器的匹配过程不是那么必要了。DNS递归查找如下图所示: DNS有一点令人担忧,这就是像wikipedia.org 或者 facebook.com这样的整个域名看上去只是对应一个单独的IP地址。还好,有几种方法可以消除这个瓶颈: 循环 DNS 是DNS查找时返回多个IP时的解决方案。举例来说,Facebook.com实际上就对应了四个IP地址。 负载平衡器 是以一个特定IP地址进行侦听并将网络请求转发到集群服务器上的硬件设备。 一些大型的站点一般都会使用这种昂贵的高性能负载平衡器。 地理 DNS 根据用户所处的地理位置,通过把域名映射到多个不同的IP地址提高可扩展性。这样不同的服务器不能够更新同步状态,但映射静态内容的话非常好。 Anycast 是一个IP地址映射多个物理主机的路由技术。 美中不足,Anycast与TCP协议适应的不是很好,所以很少应用在那些方案中。大多数DNS服务器使用Anycast来获得高效低延迟的DNS查找。 3 浏览器给web服务器发送一个HTTP请求 因为像Facebook主页这样的动态页面,打开后在浏览器缓存中很快甚至马上就会过期,毫无疑问他们不能从中读取。 所以,浏览器将把一下请求发送到Facebook所在的服务器: GET http://facebook.com/ HTTP/1.1 Accept: application/x-ms-application, image/jpeg, application/xaml+xml, [...] User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; [...] Accept-Encoding: gzip, deflate Connection: Keep-Alive Host: facebook.com Cookie: datr=1265876274-[...]; locale=en_US; lsd=WW[...]; c_user=2101[...] GET 这个请求定义了要读取的URL: “http://facebook.com/”。 浏览器自身定义 (User-Agent 头), 和它希望接受什么类型的相应 (Accept and Accept-Encoding 头). Connection头要求服务器为了后边的请求不要关闭TCP连接。 ...

January 10, 2023

NPM和webpack的关系

what is webpack? Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。即WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。 webpack的核心作用 模块化开发中,我们会编写大量模块,如果不打包就上线,那么页面加载或交互时,将会发起大量请求。为了性能优化,需要使用webpack这样的打包器对模块进行打包整合,以减少请求数。就像简单的vue项目,所有组件最终都将被打包到一个app.js中。 相较于无差别打包依赖模块的传统打包器,webpack的核心优势在于它从入口文件出发,递归构建依赖关系图。通过这样的依赖梳理,webpack打包出的bundle不会包含重复或未使用的模块,实现了按需打包,极大的减少了冗余。 webpack是一个工具,这个工具可以帮你处理好各个包/模块之间的依赖关系(modules with dependencies),并将这些复杂依赖关系的静态文件打包成一个或很少的静态文件,提供给浏览器访问使用;除此之外,webpack因为可以提高兼容性,可以将一些浏览器尚不支持的新特性转换为可以支持格式,进而减少由新特性带来的浏览器的兼容性问题。 好,我们通过介绍,我们有个概念,webpack是一个打包工具,可以帮你把你的项目这里的项目其实就是指通过模块化开发的项目 打包为简洁版的浏览器可识别的静态资源。 what is npm? 介绍了webpack,我们可能会疑问,我的JS,CSS,HTML文件分开写,挺好的呀,为什么要使用webpack工具,进行复杂的各项配置。在传统前端开发模式下,我们确实是按照JS/CSS/HTML文件分开写的模式就可以,但是随着前端的发展,社区的壮大,各种前端的库和框架层出不穷,我们项目中可能会使用很多额外的库,如何有效管理这些引入的库文件是一个大问题,而且我们知道基于在HTML中使用script引入的方式,有两个弊端,一个是会重复引入,二是当库文件数量很多时管理成为一个大难题。面对这样的局面,为了简化开发的复杂度,前端社区涌现了很多实践方法。模块化就是其中一项成功实践,而npm就是这样在社区 其实就是node社区中产生的。 npm 由三个独立的部分组成:网站、注册表(registry)、命令行工具 (CLI)。 网站 是开发者查找包(package)、设置参数以及管理 npm 使用体验的主要途径。 注册表 是一个巨大的数据库,保存了每个包(package)的信息。CLI 通过命令行或终端运行。开发者通过 CLI 与 npm 打交道。 一般来说提起npm有两个含义,一个是说npm官方网站,一个就是说npm包管理工具。npm社区或官网是一个巨大的Node生态系统,社区成员可以随意发布和安装npm生态中的包,也就是不用在重复造轮子了,别人造好了,你直接安装到你的项目中就可以使用,但是因为前面说了,当包引入数量很多时管理就成为了一个问题,这个就是npm为开发者行了方便之处,npm已经为你做好了依赖和版本的控制,也就是说使用npm可以让你从繁杂的依赖安装和版本冲突中解脱出来,进而关注你的业务而不是库的管理。 webpack就是将你从npm中安装的包打包成更小的浏览器可读的静态资源,这里需要注意的是,webpack只是一个前端的打包工具,打包的是静态资源,和后台没有关系,虽然webpack依赖于node环境。 what is node or nodejs? 其实node和nodejs两个概念没有太大差别,我个人认为唯一的区别就是,人们说起node的时候语境更多的是再说node环境,而说nodejs时更多的是在说node是一门可以提供后端能力的技术。本质上来说,node就是nodejs,nodejs就是node 简单的说 Node.js 就是运行在服务端的 JavaScript。Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。 node环境基于V8引擎提供了一种可以让JS代码跑在后端的能力,这就是node。其实这里的node本身和我们这篇讲的前端模块化没啥关系。但是因为npm是产生与node社区,node中也是通过npm来加载模块的,所以有必要说一下他们之间的关系。 npm 是 Node.js 官方提供的包管理工具,他已经成了 Node.js 包的标准发布平台,用于 Node.js 包的发布、传播、依赖控制。 webpack npm node之间关系? webpack是npm生态中的一个模块,我们可以通过全局安装webpack来使用webpack对项目进行打包; webpack的运行依赖于node的环境,没有node是不能打包的,但是webpack打包后的项目本身只是前端静态资源和后台没有关系,也就是说不依赖与node,只要有后台能力的都可以部署项目。 npm是于Node社区中产生的,是nodejs的官方包管理工具,当你下载安装好node的时候,npm cli 就自动安装好了。 正是因为npm的包管理,使得项目可以模块化的开发,而模块化的开发带来的这些改进确实大大的提高了我们的开发效率,但是利用它们开发的文件往往需要进行额外的处理才能让浏览器识别,而手动处理又是非常繁琐的,这就是webpack工具存在的意义

January 2, 2023

告别2022:被尘埃痛击,被时光血洗

原作者:刘原 2023-01-01 10:02 这是2022年的最后一缕斜阳,它穿越云翳和树梢,穿越山梁和旷野,呼啸而来,打在你的脸上,也打在我的屁股上。 都不许喊疼。 每到岁末,我都会在这一天和读者们共同回望即将远逝的这一年。谈不上新年献词,因为我的底色总是苍凉,从无昂扬喜庆,倘在先前的报馆,那都是要被毙的稿,除非那天恰好是我自己值班签版可以徇私。我把它归类为:民间记忆。 站在这个时间门槛上,我只望见了两个字:永逝。所有的故事都已终结,所有的脸孔都已凝固,悉数留在了2022,万物在这一年展露过它们应有的容颜,然后,被历史的书页重重覆盖。 那么,2022的记忆该从哪一截起始呢?我已记不清晰,可能是从丰县的一段锁链,也可能是从唐山的一家烧烤店。大家都记得那会满世界刷屏的场景。 我从未见过亿万网民如此齐心。 事后我思忖过,拐卖人口、伤害女性,固然是极其恶劣的,但它是多年来的沉疴,类似案例屡见不鲜,那么,为何这两桩恶行突然点爆了全民的怒火? 一是以短视频为主力的网络传播,与以往的文字图片传统形式相比,在视觉和情感上的爆破力是无与伦比的,再高超的文字记者,描述出的场景都远不如那几十秒视频有冲击力。 二是人民心中有块垒。经过几年疫情的折磨,大家对苦难有了更多的共情,谁无母亲姐妹妻女?这样的惨案已经不仅仅是女性群体的悲剧,它更是属于全体国民的悲剧。 三是每个人都会思索:我们生而为人,有哪些与生俱来的权利?如果被拘禁、被伤害、被殴打可以被漠视,被掩盖,谁敢保证下一个受害者不会是自己和家人? 在这两次刷屏之下,冬奥会并没有收到预期中的热度。当然也正常,中国的冰雪运动普及还很有限,即便我这样曾经狂热的体育爱好者,几十年看的惟一冰雪项目也只有短道速滑。我甚至不记得谷爱凌在哪些项目上拿了金牌,只知道她多年来一到暑假就飞回北京参加海淀区的奥数班。 冬奥会刚结束,俄乌战争开始了。 这是2022年里,全世界最刻骨铭心的一件事。 当我在2月24日中午醒来时,手机里有无数条朋友们发的信息。我一睁眼就吓得激灵一下,大意是说俄军几十万大军分几路攻入乌克兰,基辅陷落,乌三军已经悉数被摧毁…… 这样的消息,我只信了半天,是的,就开战之初兵荒马乱的那半天。然后,我再没信过那些打鸡血的简中自媒体,连看都不去看。 这是一场局部战争,但也是一场剧烈改变世界格局的战争。它的烈度和席卷范围,远不如20世纪的一战和二战,但它事实上起到了三战的效能,对1945之后、冷战落幕之后的国际秩序进行了重构。 一切都因为一个叫泽连斯基的人。 没有人在今年的早春二月看好他,包括他的国民,包括欧美国家。作为“95街区”(大概类似于乌克兰版的开心麻花)的创始人,《人民公仆》的主演,泽连斯基能唱会跳,演技出色,若说他是各国元首中表演造诣最深的人,恐不为过。即使考虑到罗纳德里根的存在,他至少也是TOP2。 但在大兵压境时,没人指望一个喜剧演员出身的总统能力挽狂澜,美国甚至为他设计了流亡路线,欧洲各国也打算旁观乌克兰亡国,然后象征性发个谴责声明算球。 但是,这个小个子演员爆发了。他告诉国民和世界:他就在基辅,哪里都不去,他的家人也同在基辅,他随时准备死在那里。 从国家被入侵的那一天起,这个男人就没刮干净过胡子,没穿过西装。他永远胡子拉碴,穿着浅绿的军用T恤,在烛光里的防空洞,在残垣断壁的街道,在被血洗过的村庄,不停地穿行,不停地拍着视频,告诉他的人民——他从未离开,他始终在抵抗。 这不是演技。出入于血与火之间,一秒就可能被炮弹炸死。没人会拿自己的性命去演。他就是无畏,他就是骨头硬。 说来也是滑稽,世上多少所谓强人,平素大喇喇上山打虎下海捉鳖,装旷世伟人,真碰上事了就大小便失禁。真正泰山崩于前而色不变的,却往往是人们嘴里嘲讽的所谓戏子。 80年代,刚遇刺不久的里根,在西柏林的一次公开集会上演讲,现场的一个氢气球爆炸,声音像极了枪响。正在演讲的他头都不抬,只说了一句:Miss me(没打中)。连缩起头的条件反射都没有,这才叫无畏。 里根以其机敏、睿智、勇气,以一己之力拖垮了苏联帝国。撒切尔夫人曾评价他:一枪不开就赢了冷战。 而泽连斯基以视死如归的气概,正在让俄罗斯帝国失血。俄罗斯这个国家,大概是命犯演艺圈,注定要被两个演员终结国运。 起初欧洲是混沌而绥靖的,除了鲜明而锋利的金毛,以及迟缓但坚定的老拜,并没有太多国家伸出援手,天知道你是不是亡国之君。但当乌克兰挺过最艰难的前几天,当泽连斯基一次次向世界发布他和他的人民誓死抵抗的宣言之后,情势逆转了。人类的勇气、良知和同情被瞬间唤醒,冬夜里微弱的摇曳之烛迅速变成巨大火把,几乎所有民选国家的民众都在督促本国政要支援乌克兰,这成了最大的政治正确。 为了挺乌克兰,波兰德国等接纳了大批乌克兰难民,东欧尤其波罗的海一众小国毫无畏惧地拿出了压箱底的军火,从北美到亚洲甚至非洲,无数国家从军援到经援,都在慷慨支援乌克兰。整个欧洲甚至愿意忍受缺气的寒冬,也要制裁发动战争的国家。 不要污蔑乌克兰在打什么代理人战争。联大每次投票,大部分国家都支持他们,他们又是哪一国的代理人?自己的家园被夷为废墟,自己的亲人死于非命流离失所,而自己拿起枪加入卫国战争,这是最堂堂正正、最天经地义的抵抗。你若连这都能抹杀,那你一定也会抹杀我们祖辈浴血奋战的伟大的抗日战争,那叫汉奸。 在冬残奥会上,特别感人的一幕是:许多国家的运动员在临冲线时,默契地放缓了脚步,让乌克兰运动员夺金。那几天,乌克兰国旗一次次在冬残奥会赛场升起,甚至一次升起三面。人们想让战火中的乌克兰人民知道,世界的道义和慈悲站在他们这边,他们不是孤独的。 在这场战争中,除了泽连斯基,我还记住了三张脸: 一个11岁的男孩哈桑,十年前,在俄军的空袭下,寡母抱着襁褓中的他逃离了叙利亚回到乌克兰,十年后,俄军炮击扎波罗热核电站,住在这个城市的母亲要照顾年迈的外婆,他自己背着一个包,拿着一个塑料袋和一本护照,独自坐了近千公里的火车,去斯洛伐克投奔哥哥姐姐。他的手臂上写着电话号码。 这孩子和我家流氓兔一样大。他羞怯,胆小,很快就吃完了身上带的一点食物,平素连夏令营都不敢去的他,不安地趴在车窗边,向着未知的异国孤身逃难。乌克兰的知名女狙击手埃梅拉尔德,在深秋的哈尔科夫前线阵地,与同为军人的新郎举行了婚礼。乌军的一名将军是他们的证婚人。 当这场战争结束时,或许他们已经殉国,或许已经伤残。但他们只想告诉这个世界:他们来过,爱过,对得起这片向日葵盛开的大地,对得起2022年的秋天。 而最令我动容的,是下面这张照片,我特地选它作为本文的题图。 马里乌波尔钢铁厂的一名守军,在日复一日绞肉机式的战斗间隙,贪婪地沐浴着珍贵的阳光。 这是一群绝望的孤军,每天都在倚靠钢铁建筑进行巷战,缺乏补给,没有援军。像四行仓库的谢晋元,像衡阳保卫战的方先觉,一无所有,除了必死之心。 生活在黑暗里的人们,心里一定要有光。我们来到这世界,不是为了当一只鼹鼠。阳光与朝露,是造物主赐予我们的,我们配得上每一束光。 不是每个人都能望见2023年的霞光。英国女王、戈尔巴乔夫,这些曾在收音机里陪伴过我们童年的名字都远逝了,他们曾经参与的历史,也变成了书籍中的铅字。而未来的史,由现在活着的人继续书写。 9月8日深夜,我用手机看BBC的女王病危现场画面,镜头在伦敦白金汉宫和苏格兰巴尔莫勒尔城堡之间切换,像是一场死亡直播。我突然看到两辆轿车和一辆军用吉普驶出城堡大门,扬长而去,当时就发了个朋友圈说:女王似已殁,那几辆车应该是去操办后事了。 随后的讣告验证了我的直觉,女王正是那个时间点去世的。这几年,见的缟素太多,我对死亡的嗅觉也变得敏锐多了。 但11月26日,新疆阿勒泰的7名福建籍工人没能嗅到死亡的气息。那天傍晚,他们乘车去4公里外的小镇准备转车返乡,没想到车刚开出500米就陷入暴雪中,于是,他们决定下车,徒步走回工地。没想到,在能见度仅3米的暴风雪中,他们竟然迷路了,连500米外的工地都没能找到,活活冻死。 他们倒下的地方距工地300米,这是阳世与阴间的距离。 而今年三月,一架东航737客机在我曾经的故乡梧州上空一头栽下。8000米,这也是从阳世到阴间的距离。而我们,至今不知事故原因。 破碎的脸太多,我们无法记全。 当一个三岁的小男孩在某场悲剧中罹难后,我看到一句令人心碎的评论:他生于疫情,死于疫情,就这样过了一生,他很乖,只是从未被这人间爱过。 不说了,说多了难过。 在这个寒冬,世界杯,或许是2022年惟一能慰藉我们的幸福。 这届世界杯的精彩度,是几十年来罕见的。不仅仅因为这是梅西C罗莫德里奇内马尔莱万一众诸神的黄昏,也因为大疫三年,人类受够了,人类太需要一场血脉贲张的赛事来涤荡几年的悲伤和郁结。 结局亦完美。 曾经的侏儒症患者梅西,在经历了无数人生悲喜之后,登顶王座。命运终于给了那个最努力最有天赋的孩子最好的馈赠,这是生命的大和谐,哦不,大圆满。 他终于成了贝利、马拉多纳之后的第三位球王。 有人觉得梅西缺乏马拉多纳的霸气,配不上球王称号。在他们眼中,在那不勒斯带着全队去嫖娼、跟女理发师造个私生子、用气枪射击记者、服用麻黄素的迭戈才叫王者气概,而梅西太乖,不像定海神针的样子。 但我喜欢梅西。他刻苦、干净、忠诚、慈悲,从未忘记含辛茹苦培养他的天堂里的外婆,会给遥远中国一个热爱足球的穷孩子寄来礼物,这是典型的在欧洲长大并接受欧洲文明的孩子。而马拉多纳,虽然天赋异禀,但至死都是南美街头烂仔底色,跟不止一个独裁者勾肩搭背。 我当然更喜欢梅西。他走在人间正道上,代无数平凡人实现了一个卑微而伟大的梦想。 如今谈及梦想,近乎奢侈。活着就是最大的梦想。 一位老友的女儿,从海外留学归来,在早春的沪上,曾经匮乏得桌上只剩一根胡萝卜。解禁之后,她直接买了一张机票,再次留洋。 在2022的最后一个月,我应约给媒体写一篇盘点稿。我忽然好奇,在同样的这一年,不同国度的人们是如何度过的?于是,我和亚洲、欧洲、北美洲的朋友们,在冬夜里,漫无边际地聊天。 我刨根问底地询问他们,印象最深刻、灵魂最颤栗的事情有哪些。 身在清迈的朋友边跟我聊,边收拾行李准备翌日去柬埔寨旅游。他告诉我,泰国有几个地方让他最是感慨:国民极注重卫生,即便是贫困乡村的农户,厕所里都没有苍蝇和臭味;当地人从不吵架,更不会拳脚相见,世道恬静,人民温和;他丢了几次钱包手机,每次都能找回来,别的朋友亦如此,此地不偷不抢不骗,甚至不捡,真正的路不拾遗。 初到多伦多的朋友遇到大雪封门,坐在壁炉边和我聊枫叶国的教育。老师永远不会因成绩优劣歧视任何孩子,智障学生有专人辅导,残疾学生有专车接送。移民局官员主动帮她联系了女儿就读的学校,说有困难告诉我们,许多问题可以由政府出钱解决,因为有你们这些市民,才令我们政府的存在有意义;校长对她说,不要焦虑地去报课外培训班,所有学业上的事都归学校负责,你不用操心;老师给家长发调查问卷,标题特别温暖:“每一个孩子都重要”。朋友的女儿上小学六年级,稚嫩的她冒出的一句话,让我惊愕而震撼—— “在加拿大,孩子们才是这个国家最大的软肋。” ...

January 1, 2023

我们的第四个五年计划

今天是元旦,过去的一年经历了过山车式的体验,国内的疫情管制终于放开,虽然未来还有很多不确定,但是大方向完全正确。 我们全家去年也收获了很多,展望未来,小熊说要让我想想未来五年的计划,我总结了一下,所有的计划可以归为在AU:事业有成、学业有成、买房定居。 这也是我们对新年的愿望,我们必将实现,我们必须实现。

January 1, 2023