20
12

这两三天一直在苏州,参加2020年中国.NET开发者大会,我第一次以讲师的身份参加技术大会,以前都是作为听众,这一次也是代表公司出去做技术交流,为此做了不少准备工作。

我从大学就是用C#和.NET,一直默默关注C#开源社区,2010年玩过Mono,2013年采用Xamarin开发Android应用,从2019年起开始带来团队实践.NET Core项目,公司核心的产品已经迁移到.NET Core,今年以来带领团队完成国产化系统下的.NET Core项目改造。

我的演讲主题是国产化系统下的.NET Core实践经验和心得,这是我们过去一年多以来的实践和心得,主要包括以下几个重点:

1、国产系统现状和要求,包括CPU、操作系统和中间件的要求,如何突破目前中间件对Java的特殊保护,到底是用java重写还是迁移.NET  Core,给出实用的迁移建议;

我的建议是“尽量不要重复造轮子,坚持使用DotNet”,而解决方案是如下:

file

2、国产化各种准备工作,重点技术预研,特别是现有系统的各个类库对.NET  Core的支持情况,最终做出技术评估;

file

3、项目迁移工作,除了按照官方要求迁移之外,还需要考虑系统在各个平台的可移植情况,我们迁移过程中遇到的问题和解决办法;

1)、现有系统的各个类库对.NET Core的支持情况:官方API和类库基本没有问题;重点注意第三方类库,比如Lucence.NET,Zip解析等
2)、客户迁移的要求情况:如果服务器CPU架构必须是申威,要重视;如果是龙芯可以拖一拖,但是其实可以和客户提系统支持的要求。

4、项目测试,模拟各种国产CPU搭建测试环境,并对系统进行测试;

file

5、国产专用服务器上的系统部署实践,部署过程中的问题和解决方案。

file

结语:开源,自由、开放的社区是创新的力量之源,这也是互联网诞生的初心。今天主持人说到会90后超过一半,还是很希望90后、00后和未来者们都知道什么是网站URL地址,都知道怎么使用搜索引擎,不要只是被困在算法和移动应用之中。

27
9

这两个月来带领团队实践安可国产化系统下的DotNet Core,我们积累了一些经验,这里做一下记录。

1、DotNet Core可以在安可下面运行吗?

首先,答案是肯定的。目前除了龙芯架构的服务器不支持DotNet Core(龙芯团队已经在努力,他们预计会尽快支持DotNet Core的下一个版本),其他所有平台架构的都支持,目前我们实际客户的操作系统有:ARM64(包括其扩展Aarch64)、X64,这些服务器包括飞腾、华为鲲鹏等国产服务器。

2、迁移麻烦吗?工作量有多大?

非常靠谱。DotNet兼容性做得非常好,只要是它支持的架构的服务器,都可以很好地运行。
file
唯一需要考虑的是,如果项目用到的第三方库比较多,需要逐个验证迁移工作,大部分流行的第三方库都已经支持DotNet Core。

3、Java重写还是迁移?

除非你们团队已经没有DotNet开发人员了,否则千万不要用Java重写,一方面是工作量大,另一方面是java的项目换到安可环境也是需要做很多适配工作。
而且我可以很负责任地说:DotNet Core的运行效率超级棒!几乎可以用飞快来形容。

4、迁移过程中有哪些经验?

代码经验

我们的代码几乎无缝转移过去,唯一的地方就是全文检索lucene.net,现在这个项目已经有更新,发了十几个bata版本,我认为可以用,所以升级过来了。
另外,安可要求只能用国产达梦数据库,需要做适配,还好这些数据库基本都是开源改过来的,适配不是问题,就是一些日期等细节需要主义。

服务器经验

我们第一个项目是迁移到政务系统,要求非常严格,有专用的服务器。我们先是在通用测试服务器下(Aarch64架构CPU,中标麒麟操作系统和银河麒麟操作系统),运行起来没有任何问题。后来在专用服务器下,架构和CPU都不变,但是专用服务器涉密,所以不能随意使用命令安装库,也不能更新系统库,只需要找到合适的包用专用服务器工具安装就可以,如果是系统库文件,就让厂商安装。

5、最大的问题是什么?

最大的问题是,安可要求服务器必须通过中间件安装系统,这些中间件其实就是web服务器,比如金蝶之类的,这里很崩溃,因为这些国产中间件只支持java,其实就是另外一个tomcat,我也认为他们基于开源改造。
后来我们的解决办法是,前后端分离,通过金蝶发布前端web站点,后端启动5000端口提供DotNet Core服务,这个并不违法安可要求。DotNet Core基于最开放的MIT开源协议,比java开放多了。

24
8

关于IT国产化盛宴的几点感想

0
归档:2020年8月分类:C#和.NET

这三周来牺牲社交时间,我亲自出马、带着团队三名精干主力在“中标麒麟龙芯”下编译.Net Core 3.0、并调试程序,编译通过但Bug太多,以失败而告终,最后放弃这个平台,这不是我们的问题,这是龙芯团队的问题,他们的CPU对彻底开源的.NET支持台还很差(也许明年会好些),不过目前我们支持“中标麒麟兆芯”,这个平台也完全符合安可要求。

从我接触到的情况来看,目前最鸡血的是那些国字号相关的企业,不过如果不涉密,但大部分也只是买来应付检查,因为国产服务器真的是几乎不能用。还有一些比较聪明的单位,直接购买云国产操作系统,但是这些国产云操作系统就是一个幌子,因为它们也是跑在“洋芯片”、寄生在“洋软件”下面而已。

Java的码农请别吐槽C#,两门语言都是“美帝”发明的开发语言哦,要说彻底支持符合国产要求,别忘了Java的SDK还有专利费,而且Java的拥有者Oracle告谷歌的侵权诉讼案还没结束呢,哈哈。

另外,很想对那些摇着民族主义旗号大喊国产的人,有本事从应用软件到开发语言彻底国产,还有哦,wifi现在澳洲公司还在收专利费、互联网也是MIT的发明、Apache基金会的总部现在还设在美国的马里兰州……这只是一场分蛋糕大盛宴,曲终人必散,苦的是那些重复造轮子的码农,还好我们已经脱离苦海。

03
7

数学基础、集合论和选择公理

0
归档:2020年7月分类:数海泛舟

这几天又重新学习复习了一下数学基础:逻辑主义、形式主义和直觉主义。我自己当然更倾向于基于公理化集合论的逻辑主义,这也是目前大部分数学家的选择。

一、数学基础

数学上,数学基础一词有时候用于数学的特定领域,例如数理逻辑,公理化集合论,证明论,模型论,和递归论(可计算性理论)。但是寻求数学的基础也是数学哲学的中心问题:在什么终极基础上命题可以称为真?
目前占统治地位的数学范式是基于公理化集合论和形式逻辑的。实际上,几乎所有现在的数学定理都可以表述为集合论下的定理。在这个观点下,所谓数学命题的真实性,不过就是该命题可以从集合论公理使用形式逻辑推导出来。

二、公理化集合论

基础集合论可以用非正式的、直觉的方式学习,在小学中就可以用文氏图说明。基础集合论直观地假设集合就是一群符合任意特定条件的对象的组合,但此假设会造成悖论。最简单及著名的是罗素悖论及布拉利-福尔蒂悖论。公理集合论的形成就是为了避免这些集合论的悖论。
许多数学家研究的公理集合论系统假设所有的集合形成累计层次。这类的系统可分为二类:
1、只由集合构成:这类系统包括最常用的公理集合论:含选择公理的策梅洛-弗兰克尔集合论(ZFC),由亚伯拉罕·弗兰克尔和陶拉尔夫·斯科伦扩展了策梅罗集合论所得。其他和ZFC有关的集合论有:
1)、策梅洛集合论是由德国数学家恩斯特·策梅洛创立,将分类公理代替替代公理。
2)、广义集合论,策梅洛集合论的一小部分,已足以处理皮亚诺公理及有限集合。
3)、克里普克-普拉特克集理论,省略了无穷公理、幂集公理和选择公理,削弱了分类公理和替代公理的公理架构。
2、由集合和真类构成:这类系统包括冯·诺伊曼-博内斯-哥德尔集合论,是设计生成同 ZFC同样结果的集合论公理系统,但只有有限数目的公理而不使用公理模式。单论只涉及集合的内容,此理论的强度和ZFC相当。另外比ZFC强的Morse-Kelley集合论及Tarski–Grothendieck集合论也属于这一类。

三、选择公理

选择公理:对于所有的集族,均存在选择函数。
罗素解释:假设有许多(甚至是无限)双鞋子,则我们可以选取每双鞋左边的鞋子构成一个具体的选择。然而,假设有无限双袜子(假设每双袜子都没有可区分的特征),在对于所有的集族,均存在选择函数。
哥德尔证明了选择公理与ZF的相对协调性。保罗·寇恩用力迫法证明了选择公理独立于ZF。也就是说:哥德尔和寇恩证明了,无论接受选择公理与否,都不会导致矛盾,只是身处不同的『数学世界』而已。
不过,除了一些研究集合论的数学家和逻辑学家以外,大部分数学家都选择接受选择公理,因为在含有选择公理的数学世界里,事情会简单一些。

16
6

你是否还在为微服务应该拆多小而争论不休?到底如何才能设计出收放自如的微服务?怎样才能保证业务领域模型与代码模型的一致性?或许本文能帮你找到答案。

本文是基于 DDD 的微服务设计和开发实战篇,通过借鉴领域驱动设计思想,指导微服务项目团队进行设计和开发(理论篇详见《当中台遇上 DDD,我们该如何设计微服务?》)。本文包括三部分内容:第一部分讲述领域驱动设计基本知识,包括:分层架构、服务视图、数据视图和领域事件发布和订阅等;第二部分讲述微服务设计方法、过程、模板、代码目录、设计原则等内容;最后部分以一个项目为例讲述基于 DDD 的微服务设计过程。

目标

本文采用 DDD(领域驱动设计)作为微服务设计指导思想,通过事件风暴建立领域模型,合理划分领域逻辑和物理边界,建立领域对象及服务矩阵和服务架构图,定义符合 DDD 分层架构思想的代码结构模型,保证业务模型与代码模型的一致性。通过上述设计思想、方法和过程,指导团队按照 DDD 设计思想完成微服务设计和开发。

通过领域模型和 DDD 的分层思想,屏蔽外部变化对领域逻辑的影响,确保交付的软件产品是边界清晰的微服务,而不是内部边界依然混乱的小单体。在需求和设计变化时,可以轻松的完成微服务的开发、拆分和组合,确保微服务不易受外部变化的影响,并稳定运行。

适用范围

本文适用于按照 DDD 设计方法进行微服务设计和开发的项目及相关人员。

以下情况不适用:

“我们的目标是按期盖出一栋大楼来,不要跟我提什么方法,有这啰嗦的时间,还不如抓紧点时间搬砖,把楼给我快点盖好!”。

“我的工作就是让软件运行起来,能工作一切就 OK!我不需要那么多约束,什么设计方法、扩展性、业务变化、领域模型、响应能力与我无关。别耽误工期啦!先上线再说!”。

“好的软件是自己演进出来的,我们不需要设计!”。

哈哈,开个玩笑啦!其实设计不会花太多时间的!

正文内容

不耽误大家时间了,言归正传。

DDD 分层架构视图
DDD 分层架构包括:展现层、应用层、领域层和基础层。

基于DDD的微服务设计和开发实战

DDD 分层架构各层职能如下:

展现层
展现层负责向用户显示信息和解释用户指令。

应用层
应用层是很薄的一层,主要面向用户用例操作,协调和指挥领域对象来完成业务逻辑。应用层也是与其他系统的应用层进行交互的必要渠道。应用层服务尽量简单,它不包含业务规则或知识,只为下一层的领域对象协调任务,使它们互相协作。应用层还可进行安全认证、权限校验、分布式和持久化事务控制或向外部应用发送基于事件的消息等。

领域层
领域层是软件的核心所在,它实现全部业务逻辑并且通过各种校验手段保证业务正确性。它包含业务所涉及的领域对象(实体、值对象)、领域服务以及它们之间的关系。它负责表达业务概念、业务状态以及业务规则,具体表现形式就是领域模型。

基础层
基础层为各层提供通用的技术能力,包括:为应用层传递消息、提供 API 管理,为领域层提供数据库持久化机制等。它还能通过技术框架来支持各层之间的交互。

服务视图
微服务内的服务视图
微服务内有 Facade 接口、应用服务、领域服务和基础服务,各层服务协同配合,为外部提供服务。

基于DDD的微服务设计和开发实战

1、接口服务
接口服务位于用户接口层,用于处理用户发送的 Restful 请求和解析用户输入的配置文件等,并将信息传递给应用层。

2、应用服务
应用服务位于应用层。用来表述应用和用户行为,负责服务的组合、编排和转发,负责处理业务用例的执行顺序以及结果的拼装。

应用层的服务包括应用服务和领域事件相关服务。

应用服务可对微服务内的领域服务以及微服务外的应用服务进行组合和编排,或者对基础层如文件、缓存等数据直接操作形成应用服务,对外提供粗粒度的服务。

领域事件服务包括两类:领域事件的发布和订阅。通过事件总线和消息队列实现异步数据传输,实现微服务之间的解耦。

3、领域服务
领域服务位于领域层,为完成领域中跨实体或值对象的操作转换而封装的服务,领域服务以与实体和值对象相同的方式参与实施过程。

领域服务对同一个实体的一个或多个方法进行组合和封装,或对多个不同实体的操作进行组合或编排,对外暴露成领域服务。领域服务封装了核心的业务逻辑。实体自身的行为在实体类内部实现,向上封装成领域服务暴露。

为隐藏领域层的业务逻辑实现,所有领域方法和服务等均须通过领域服务对外暴露。

为实现微服务内聚合之间的解耦,原则上禁止跨聚合的领域服务调用和跨聚合的数据相互关联。

4、基础服务
基础服务位于基础层。为各层提供资源服务(如数据库、缓存等),实现各层的解耦,降低外部资源变化对业务逻辑的影响。

基础服务主要为仓储服务,通过依赖反转的方式为各层提供基础资源服务,领域服务和应用服务调用仓储服务接口,利用仓储实现持久化数据对象或直接访问基础资源。

微服务外的服务视图

  1. 前端应用与微服务
    微服务中的应用服务通过用户接口层组装和数据转换后,发布在 API 网关,为前端应用提供数据展示服务。

  2. 微服务与外部应用
    跨微服务数据处理时,对实时性要求高的场景,可选择直接调用应用服务的方式(新增和修改类型操作需关注事务一致性)。对实时性要求不高的场景,可选择异步化的领域事件驱动机制(最终数据一致性)。

    数据视图
    DDD 分层架构中数据对象转换的过程如下图。

基于DDD的微服务设计和开发实战

数据视图应用服务通过数据传输对象(DTO)完成外部数据交换。领域层通过领域对象(DO)作为领域实体和值对象的数据和行为载体。基础层利用持久化对象(PO)完成数据库的交换。

DTO 与 VO 通过 Restful 协议实现 JSON 格式和对象转换。

前端应用与应用层之间 DTO 与 DO 的转换发生在用户接口层。如微服务内应用服务需调用外部微服务的应用服务,则 DTO 的组装和 DTO 与 DO 的转换发生在应用层。

领域层 DO 与 PO 的转换发生在基础层。

领域事件和事件总线
领域事件是领域模型中非常重要的部分,用来表示领域中发生的事件。一个领域事件将导致进一步的业务操作,有助于形成完整的业务闭环。领域事件主要用于解耦微服务,各个微服务之间不再是强一致性,而是基于事件的最终一致性。

基于DDD的微服务设计和开发实战

微服务内的领域事件
微服务内的领域事件可以通过事件总线或利用应用服务实现不同聚合之间的业务协同。当微服务内发生领域事件时,由于大部分事件的集成发生在同一个线程内,不一定需要引入消息中间件。但一个事件如果同时更新多个聚合数据,按照 DDD“一个事务只更新一个聚合根”的原则,可以考虑引入消息中间件,通过异步化的方式,对微服务内不同的聚合根采用不同的事务。

微服务之间的领域事件
微服务之间的数据交互方式通常有两种:应用服务调用和领域事件驱动机制。

领域事件驱动机制更多的用于不同微服务之间的集成,实现微服务之间的解耦。事件库(表)可以用于微服务之间的数据对账,在应用、网络等出现问题后,可以实现源和目的端的数据比对,在数据暂时不一致的情况下仍可根据这些数据完成后续业务处理流程,保证微服务之间数据的最终一致性。

应用服务调用方式通常应用于实时性要求高的业务场景,但一旦涉及到跨微服务的数据修改,将会增加分布式事务控制成本,影响系统性能,微服务之间的耦合度也会变高。

事件总线
事件总线位于基础层,为应用层和领域层服务提供事件消息接收和分发等服务。其大致流程如下:

1、服务触发并发布事件。

2、事件总线事件分发。

如果是微服务内的订阅者(微服务内的其它聚合),则直接分发到指定订阅者。

如果是微服务外的订阅者,则事件消息先保存到事件库(表)并异步发送到消息中间件。

如果同时存在微服务内和外订阅者,则分发到内部订阅者,并将事件消息保存到事件库(表)并异步发送到消息中间件。为了保证事务的一致性,事件表可以共享业务数据库。也可以采用多个微服务共享事件库的方式。当业务操作和事件发布操作跨数据库时,须保证业务操作和事件发布操作数据的强一致性。

事件数据持久化
事件数据的持久化存储可以有两种方案,在项目实施过程中根据具体场景选择最佳方案。

事件数据保存到微服务所在业务数据库的事件表中,利用本地事务保证业务操作和事件发布操作的强一致性。

事件数据保存到多个微服务共享的事件库中。需要注意的一点是:这时业务操作和事件发布操作会跨数据库操作,须保证事务的强一致性(如分布式事务机制)。

事件数据的持久化可以保证数据的完整性,基于这些数据可以完成跨微服务数据的一致性比对。

微服务设计方法
事件风暴
本阶段主要完成领域模型设计。

基于 DDD 的微服务设计通常采用事件风暴方法。通过事件风暴完成领域模型设计,划分出微服务逻辑边界和物理边界,定义领域模型中的领域对象,指导微服务设计和开发。事件风暴通常包括产品愿景、场景分析、领域建模、微服务设计和拆分等过程。本文不对事件风暴详细方法做深入描述,如感兴趣可查阅相关资料。

1、产品愿景
产品愿景是对产品的顶层价值设计,对产品目标用户、核心价值、差异化竞争点等信息达成一致,避免产品偏离方向。建议参与角色:业务需求方、产品经理和开发组长。

2、场景分析
场景分析是从用户视角出发,探索业务领域中的典型场景,产出领域中需要支撑的场景分类、用例操作以及不同子域之间的依赖关系,用以支撑领域建模。

建议参与角色:产品经理、需求分析人员、架构师、开发组长和测试组长。

3、领域建模
领域建模是通过对业务和问题域进行分析,建立领域模型,向上通过限界上下文指导微服务边界设计,向下通过聚合指导实体的对象设计。

建议参与角色:领域专家、产品经理、需求分析人员、架构师、开发组长和测试组长。

4、微服务拆分和设计
结合业务限界上下文与技术因素,对服务的粒度、分层、边界划分、依赖关系和集成关系进行梳理,完成微服务拆分和设计。

微服务设计应综合考虑业务职责单一、敏态与稳态业务分离、非功能性需求(如弹性伸缩要求、安全性等要求)、团队组织和沟通效率、软件包大小以及技术异构等因素。

建议参与角色:产品经理、需求分析人员、架构师、开发组长和测试组长。

领域对象及服务矩阵和代码模型设计
本阶段完成领域对象及服务矩阵文档以及微服务代码模型设计。

1、领域对象及服务矩阵
根据事件风暴过程领域对象和关系,对产出的限界上下文、聚合、实体、值对象、仓储、事件、应用服务、领域服务等领域对象以及各对象之间的依赖关系进行梳理,确定各对象在分层架构中的位置和依赖关系,建立领域对象分层架构视图,为每个领域对象建立与代码模型对象的一一映射。

建议参与角色:架构师和开发组长。

2、微服务代码模型
根据领域对象在 DDD 分层架构中所在的层、领域类型、与代码对象的映射关系,定义领域对象在微服务代码模型中的包、类和方法名称等,设计微服务工程的代码层级和代码结构,明确各层间的调用关系。

建议参与角色:架构师和开发组长。

领域对象及服务矩阵样例说明
领域对象及服务矩阵主要用来记录事件风暴和微服务设计过程中产出的领域对象属性,如:各领域对象在 DDD 分层架构中的位置、属性、依赖关系以及与代码对象的映射关系等。通过建立领域对象与代码对象的映射关系,可指导软件开发人员准确无误的按照设计文档完成微服务开发。

以下为领域对象及服务矩阵样例(部分数据,仅供参考)。

基于DDD的微服务设计和开发实战

各栏说明如下:

层:定义领域对象位于 DDD 分层架构中的哪一层。如:接口层、应用层、领域层以及基础层等。

聚合:在事件风暴过程中将关联紧密的实体和值对象等组合形成聚合。本栏说明聚合名称。

领域对象名称:领域模型中领域对象的具体名称。如:“请假审批已通过”是类型为“事件”的领域对象;“请假单”是领域类型为“实体”的领域对象。

领域类型:在领域模型中根据 DDD 知识域定义的领域对象的类型,如:限界上下文、聚合、聚合根(实体)、实体、值对象、事件、命令、应用服务、领域服务和仓储服务等。

依赖对象名称:根据业务对象依赖或分层调用依赖关系建立的领域对象的依赖关系(如服务调用依赖、关联对象聚合等)。本栏说明领域对象需依赖的其他领域对象,如上层服务在组合和编排过程中对下层服务的调用依赖、实体之间或者实体与值对象在聚合内的依赖等。

包名:代码模型中的包名,本栏说明领域对象所在的软件包。

类名:代码模型中的类名,本栏说明领域对象的类名。

方法名:代码模型中的方法名,本栏说明领域对象实现或操作的方法名。

微服务代码结构模型
微服务代码模型最终结果来源于领域对象及服务矩阵。在代码模型设计时须建立领域对象和代码对象的一一映射,保证业务模型与代码模型的一致性,即使不熟悉业务的开发人员或者不熟悉代码的业务人员也可以很快定位到代码位置。

微服务代码总目录 基于 DDD 的代码模型包括 interfaces、application、domain 和 infrastructure 四个目录。

基于DDD的微服务设计和开发实战

Interfaces(用户接口层):本目录主要存放用户接口层代码。前端应用通过本层向应用服务获取展现所需的数据。本层主要用于处理用户发送的 Restful 请求和解析用户输入的配置文件等,并将信息传递给 Application 层。主要代码形态是数据组装以及 Facade 接口等。

Application(应用层):本目录主要存放应用层代码。应用服务代码基于微服务内的领域服务或微服务外的应用服务完成服务编排和组合。为用户接口层提供各种应用数据展现支持。主要代码形态是应用服务和领域事件等。

Domain(领域层):本目录主要存放领域层代码。本层代码主要实现核心领域逻辑,其主要代码形态是实体类方法和领域服务等。

Infrastructure(基础层):本目录存放基础层代码,为其它各层提供通用技术能力、三方软件包、配置和基础资源服务等。

用户接口层代码模型
用户接口层代码模型目录包括:assembler、dto 和 facade。

基于DDD的微服务设计和开发实战

Assembler:实现 DTO 与领域对象之间的相互转换和数据交换。理论上 Assembler 总是与 DTO 一同被使用。

Dto:数据传输的载体,内部不存在任何业务逻辑,通过 DTO 把内部的领域对象与外界隔离。

Facade:提供较粗粒度的调用接口,将用户请求委派给一个或多个应用服务进行处理。

应用层代码模型
应用层代码模型目录包括:event 和 service。

基于DDD的微服务设计和开发实战

Event(事件):事件目录包括两个子目录:publish 和 subscribe。publish 目录主要存放微服务内领域事件发布相关代码。subscribe 目录主要存放微服务内聚合之间或外部微服务领域事件订阅处理相关代码。为了实现领域事件的统一管理,微服务内所有领域事件(包括应用层和领域层事件)的发布和订阅处理都统一放在应用层。

Service(应用服务):这里的服务是应用服务。应用服务对多个领域服务或外部应用服务进行封装、编排和组合,对外提供粗粒度的服务。

领域层代码模型
微服务领域层包括一个或多个聚合代码包。标准的聚合代码模型包括:entity、repository 和 service 三个子目录。

基于DDD的微服务设计和开发实战

Aggregate(聚合):聚合代码包的根目录,实际项目中以实际业务属性的名称来命名。聚合定义了领域对象之间的关系和边界,实现领域模型的内聚。

Entity(实体):存放实体(含聚合根、实体和值对象)相关代码。同一实体所有相关的代码(含对同一实体类多个对象操作的方法,如对多个对象的 count 等)都放在一个实体类中。

Service(领域服务):存放对多个不同实体对象操作的领域服务代码。这部分代码以领域服务的形式存在,在设计时一个领域服务对应一个类。

Repository(仓储):存放聚合对应的查询或持久化领域对象的代码,通常包括仓储接口和仓储实现方法。为了方便聚合的拆分和组合,我们设定一个原则:一个聚合对应一个仓储。

特别说明:按照 DDD 分层原则,仓储实现本应属于基础层代码,但为了微服务代码拆分和重组的便利性,我们把聚合的仓储实现代码放到了领域层对应的聚合代码包内。如果需求或者设计发生变化导致聚合需要拆分或重新组合时,我们可以聚合代码包为单位,轻松实现微服务聚合的拆分和组合。

基础层代码模型
基础层代码模型包括:config 和 util 两个子目录。

Config:主要存放配置相关代码。

Util:主要存放平台、开发框架、消息、数据库、缓存、文件、总线、网关、第三方类库、通用算法等基础代码,可为不同的资源类别建立不同的子目录。

基于DDD的微服务设计和开发实战

微服务总目录结构
微服务总目录结构如下:

基于DDD的微服务设计和开发实战

微服务设计原则
微服务设计原则中如高内聚低耦合、复用、单一职责等原则在此就不赘述了,这里主要强调以下几条:

第一条:“要领域驱动设计,而不是数据驱动设计,也不是界面驱动设计”。
微服务设计首先应建立领域模型,确定逻辑和物理边界后,然后才进行微服务边界拆分,而不是一上来就定义数据库表结构,也不是界面需要什么,就去调整领域逻辑代码。

领域模型和领域服务应具有高度通用性,通过接口层和应用层屏蔽外部变化对业务逻辑的影响,保证核心业务功能的稳定性。

第二条:“要边界清晰的微服务,而不是泥球小单体”。
微服务完成开发后其功能和代码也不是一成不变的。随着需求或设计变化,微服务内的代码也会分分合合。逻辑边界清晰的微服务,可快速实现微服务代码的拆分和组合。DDD 思想中的逻辑边界和分层设计也是为微服务各种可能的分分合合做准备的。

微服务内聚合与聚合之间的领域服务以及数据原则上禁止相互产生依赖。如有必要可通过上层的应用服务编排或者事件驱动机制实现聚合之间的解耦,以利于聚合之间的组合和拆分。

第三条:“要职能清晰的分层,而不是什么都放的大箩筐”。
分层架构中各层职能定位清晰,且都只能与其下方的层发生依赖,也就是说只能从外层调用内层服务,内层服务通过封装、组合或编排对外逐层暴露,服务粒度由细到粗。

应用层负责服务的编排和组合,领域层负责领域业务逻辑的实现,基础层为各层提供资源服务。

第四条:“要做自己能 hold 住的微服务,而不是过度拆分的微服务”。
微服务的过度拆分必然会带来软件维护成本的上升,如:集成成本、运维成本以及监控和定位问题的成本。企业转型过程中很难短时间内提升这些能力,如果项目团队不具备这些能力,将很难 hold 住这些过细的微服务。而如果我们在微服务设计之初就已经定义好了微服务内的逻辑边界,项目初期我们可以尽可能少的拆分出过细的微服务,随着技术的积累和时间的推移,当我们具有这些能力后,由于微服务内有清晰的逻辑边界,这时就可以随时根据需要轻松的拆分或组合出新的微服务。

不同场景的微服务设计
微服务的设计先从领域建模开始,领域模型是微服务设计的核心,微服务是领域建模的结果。在微服务设计之前,请先判断你的业务是否聚焦在领域和领域逻辑。

实际在做系统设计时我们可能面临各种不同的情形,如从传统单体拆分为多个微服务,也可能是一个全新领域的微服务设计(如创业中的应用),抑或是将一个单体中面临问题或性能瓶颈的模块拆分为微服务而其余功能仍为单体的情况。

下面分几类不同场景说明如何进行微服务和领域模型设计。

新建系统的微服务设计
新建系统会遇到复杂和简单领域两种场景,两者的领域建模过程也会有所差别。

1、简单领域的建模
对于简单的业务领域,一个领域可能就是一个小的子域。领域建模过程相对简单,根据事件风暴可以分解出事件、命令、实体、聚合和限界上下文等,根据领域模型和微服务拆分原则设计出微服务即可。

2、复杂领域的建模
对于复杂的业务领域,领域可能还需要拆分为子域,甚至子域还会进一步拆分,如:保险领域可以拆分为承保、理赔、收付费和再保等子域,承保子域还可以再拆分为投保、保单管理等子子域。对于这种复杂的领域模型,是无法通过一个事件风暴完成领域建模的,即使能完成,其工程量也是非常浩大,效果也不一定好。

对于这种复杂的领域,我们可以分三阶段来完成领域模型和微服务设计。

拆分子域建立领域模型:根据业务特点考虑流程节点或功能模块等边界因素(微服务最终的拆分结果很多时候跟这些边界因素有一定的相关性),按领域逐级分解为大小合适的子域,针对子域进行事件风暴,记录领域对象、聚合和限界上下文,初步确定各级子域的领域模型。

领域模型微调:梳理领域内所有子域的领域模型,对各子域模型进行微调,这个过程重点考虑不同限界上下文内聚合的重新组合,同步需要考虑子域、限界上下文以及聚合之间的边界、服务以及事件之间的依赖关系,确定最终的领域模型。

微服务设计和拆分:根据领域模型的限界上下文和微服务的拆分原则,完成微服务的拆分和设计。

单体遗留系统的微服务设计
如果一个单体遗留系统,只是将面临问题或性能瓶颈的模块拆分为微服务,而其余功能仍为单体。我们只需要将这些特定功能领域理解为一个简单的子领域,按照简单领域建模方式进行领域模型的设计即可。但在新微服务设计中需要考虑新老系统之间的服务协议,必要时引入防腐层。

特别说明
虽然有些业务领域在事件风暴后发现无法建立领域模型,如数据处理或分析类场景,但本文所述的分层架构模型、服务之间规约和代码目录结构在微服务设计和开发中仍然是通用的。

基于 DDD 的微服务设计和开发实例
为了更好的理解 DDD 的设计思想和过程,我们用一个场景简单但基本涵盖 DDD 设计思想的项目来说明微服务设计和开发过程。

项目基本信息
项目主要目标是实现在线请假和考勤管理。基本功能包括:请假、考勤以及人员管理等。

请假:请假人填写请假单提交审批,根据请假人身份和请假天数进行校验,根据审批规则逐级递交审批,核批通过则完成审批。

考勤:根据考勤规则,剔除请假数据后,对员工考勤数据进行校验,输出考勤统计表。

人员管理:维护人员基本信息和上下级关系。

设计和实施步骤
步骤一:事件风暴
由于项目目标基本明确,我们在事件风暴过程中裁剪了产品愿景,直接从用户旅程和场景分析开始。

1、场景分析:场景分析是一个发散的过程。根据不同角色的旅程和场景分析,尽可能全面的梳理从前端操作到后端业务逻辑发生的所有操作、命令、领域事件以及外部依赖关系等信息(如下图),如:请假人员会执行创建请假信息操作命令,审批人员会执行审批操作,请假审批通过后会产生领域事件,通知邮件系统反馈请假人员结果,并将请假数据发送到考勤以便核销等。在记录这些领域对象的同时,我们也会标记各对象在 DDD 中的层和对象类型等属性,如:应用服务、领域服务、事件和命令等类型。

基于DDD的微服务设计和开发实战

2、领域建模:领域建模是一个收敛的过程。这个收敛过程分三步:第一步根据场景分析中的操作集合定义领域实体;第二步根据领域实体业务关联性,定义聚合;第三步根据业务及语义边界等因素,定义限界上下文。

定义领域实体:在场景分析过程中梳理完操作、命令、领域事件以及外部依赖关系等领域对象后。分析这些操作应由什么实体发起或产生,从而定义领域实体对象,并将这些操作与实体进行关联。

在请假场景中,经分析需要有请假单实体对象,请假单实体有创建请假信息以及修改请假信息等操作。

定义聚合:将业务紧密相关的实体进行组合形成聚合,同时确定聚合中的聚合根、值对象和实体。经分析项目最终形成三个聚合:人员管理、请假和考勤。在请假聚合中有请假单、审批轨迹、审批规则等实体,其中请假单是聚合根,审批轨迹是请假单的值对象,审批规则是辅助实体。

聚合内须保证业务操作的事务性,高度内聚的实体对象可自包含完成本领域功能。聚合是可拆分为微服务的最小单元。在同一限界上下文内多个聚合可以组合为一个微服务。如有必要,也可以将某一个聚合独立为微服务。

定义限界上下文:根据领域及语义边界等因素确定限界上下文,将同一个语义环境下的一个或者多个聚合放在一个限界上下文内。由于人员管理与请假聚合两者业务关联紧密,共同完成人员请假功能,两者一起构成请假限界上下文,考勤聚合则单独形成考勤限界上下文。

3、微服务设计和拆分:理论上一个限界上下文可以设计为一个微服务,但还需要综合考虑多种外部因素,如:职责单一性、性能差异、版本发布频率、团队沟通效率和技术异构等要素。

由于本项目微服务设计受技术以及团队等因素影响相对较小,主要考虑职责单一性,因此根据限界上下文直接拆分为请假和考勤两个微服务。其中请假微服务包含人员和请假两个聚合,考勤微服务只包含考勤聚合。

步骤二、领域对象及服务矩阵
将事件风暴中产出的领域对象按照各自所在的微服务进行分类,定义每个领域对象在微服务中的层、领域类型和依赖的领域对象等。

这个步骤最关键的工作是确定实体、方法、服务等领域对象在微服务分层架构中的位置以及各对象之间的依赖关系,形成服务矩阵(如下表)。这个过程也将在事件风暴数据的基础上,进一步细化领域对象以及它们之间关系,并补充事件风暴中可能遗漏的细节。

确定完各领域对象的属性后,按照代码模型设计各个领域对象在代码模型中的代码对象(包括代码对象所在的:包名、类名和方法名),建立领域对象与代码对象的一一映射关系。根据这种映射关系,相关人员可快速定位到业务逻辑所在的代码位置。

基于DDD的微服务设计和开发实战

步骤三:领域模型及服务架构
根据领域模型中领域对象属性以及服务矩阵,画出领域对象及服务架构视图(如下图)。这个视图可以作为标准的 DDD 分层领域服务架构视图模型,应用在不同的领域模型中。这个模型可以清晰的体现微服务内实体、聚合之间的关系,各层服务之间的依赖关系以及应用层服务组合和编排的关系,微服务之间的服务调用以及事件驱动的前后处理逻辑关系。

在这个阶段,前端的设计也可以同步进行,在这里我们用到了微前端的设计理念,为请假和考勤微服务分别设计了请假和考勤微前端,基于微前端和微服务,形成从前端到后端的业务逻辑自包含组件。两个微前端之上有一个集成主页面,可根据页面流动态加载请假和考勤的微前端页面。(微前端技术详见:《中台微服务了,那前端呢?》)。

基于DDD的微服务设计和开发实战

步骤四:代码模型设计
根据 DDD 的代码结构模型和各领域对象在所在的包、类和方法,定义出请假微服务的代码结构模型。应用层代码结构包括:应用服务以及事件发布相关代码(如下图)。

基于DDD的微服务设计和开发实战

领域层代码结构包括一个或多个聚合的实体类以及领域服务相关代码(如下图)。在本项目中请假微服务领域层包含了请假和人员两个聚合。

基于DDD的微服务设计和开发实战

领域模型中的一个聚合对应一个聚合代码包,如:人员和请假领域逻辑代码都放在各自的聚合代码包中,如随着业务发展,人员管理功能需要从请假微服务中拆分出来,我们只需要将人员聚合代码包稍加改造并独立部署即可快速发布为人员管理微服务。

步骤五:详细设计
在完成领域模型和代码模型设计后,我们就可以开始详细设计了,详细设计主要结合具体的业务功能来开展,主要工作包括:系统界面、数据库表以及字段、服务参数规约及功能等。

步骤六:代码开发
软件开发人员只需要按照设计文档和功能要求,找到业务功能对应的代码位置,完成代码开发和服务编排即可。

步骤七:测试和发布
完成代码开发后,由开发人员编写单元测试用例,基于挡板模拟依赖对象完成跨服务的测试。单元测试完成后,在团队内可进一步完成微服务与相应微前端的集成和测试,形成请假和考勤两个业务组件。前端主页面完成请假和考勤微前端页面集成和页面流及组件基础数据配置,主页面可以按照页面流程动态加载请假和考勤微前端页面。最终部署的软件包包括:请假和考勤两个微服务,请假和考勤两个微前端,一个主页面共计五个。这五个部署包独立开发、独立运行和独立部署。

技术组件说明
主页面和微前端采用:Vue(前端框架),ElementUI(UI 框架 -PC),VUX(UI 框架 - 移动端) 和 MPVUE(UI 框架 - 小程序) 等。微服务开发采用:Spring Cloud、Kafka、Redis 等。数据库采用:PostgreSQL。

附录一:DDD 名词和术语
Event Storming(事件风暴):事件风暴是一项团队活动,旨在通过领域事件识别出聚合根,进而划分微服务的限界上下文。在活动中,团队先通过头脑风暴的形式罗列出领域中所有的领域事件,整合之后形成最终的领域事件集合,然后对于每一个事件,标注出导致该事件的命令(Command),再然后为每个事件标注出命令发起方的角色,命令可以是用户发起,也可以是第三方系统调用或者是定时器触发等。最后对事件进行分类整理出聚合根以及限界上下文。

Entity(实体):每个实体是唯一的,并且可以相当长的一段时间内持续地变化。我们可以对实体做多次修改,故一个实体对象可能和它先前的状态大不相同。但是,由于它们拥有相同的身份标识,他们依然是同一个实体。例如一件商品在电商商品上下文中是一个实体,通过商品中台唯一的商品 id 来标示这个实体。

ValueObject(值对象):值对象用于度量和描述事物,当你只关心某个对象的属性时,该对象便可作为一个值对象。实体与值对象的区别在于唯一的身份标识和可变性。当一个对象用于描述一个事物,但是又没有唯一标示,那么它就是一个值对象。例如商品中的商品类别,类别就没有一个唯一标识,通过图书、服装等这些值就能明确表示这个商品类别。

Aggregate(聚合):聚合是实体的升级,是由一组与生俱来就密切相关实体和值对象组合而成的,整个组合的最上层实体就是聚合。

Bounded Context(限界上下文):用来封装通用语言和领域对象,为领域提供上下文语境,保证在领域之内的一些术语、业务相关对象等(通用语言)有一个确切的含义,没有二义性。使团队所有成员能够明确地知道什么必须保持一致,什么必须独立开发。

04
6

今天邮箱收到github的issue更新提醒,就在昨天,龙芯(loongson)的码农们已经让.NET Core 3.1的FlightFinder例子在MIPS64跑起来了,但是更复杂的程序还得继续改,预计在今年年底加入.NET 5.x(谨慎乐观)。

.NET 本身是完全开源,比Java还要开放(想想现在Google和Oracle关于Java的专利案还没结束呢),但是MS官方已经明确.NET Core和未来的版本不会支持MIPS的CPU指令集架构(龙芯就是用这个架构级),因为太小众了,要实现就得社区自己去写。

龙芯的团队从去年11月份跑Hello World到现在做了不少工作,看社区里的几位工程师也很努力,最终应该会支持(大概率版本会落后),这个值得肯定。但是,对于龙芯来说,社区和生态太难了,要不是去年“安可”要求,估计也不会刺激他们去支持.NET Core。好的结局是“安可”推广成功,gov和army里无关紧要的系统都用(核心系统因为复杂度,怕是很难用上)。

最最要命的是:技术发展太快了,龙芯所支持的服务器市场现在已经完全靠云计算了,这些云计算不可能采用龙芯等国产芯片,这也是为什么阿里要造出概念化的飞天国产云系统的原因——为了政策需要啊!

对于科学技术,我坚信市场化和资本化是最有效的驱动力,最近的SpaceX是又一个有力佐证。努力的人很多,最后大都做了前浪。

02
6

数学最重要的公式

0
归档:2020年6月分类:数海泛舟

一、费马大定理

$$ x^n+y^n=z^n \mbox{(n=2,为毕达哥拉斯定理)} $$

二、欧拉公式

$$e^{i\pi} + 1 = 0$$

三、牛顿-莱布尼茨公式

$$ \int_a^bf(x){\rm d}x=F(b)-F(a) $$

四、黎曼zeta函数

整数形式

$$\zeta(p) = \sum_{n=1}^{\infty}{\frac{1}{n^p}}$$

复数形式

$$\zeta(s) = \sum_{n=1}^{\infty}{\frac{1}{n^s}} \mbox{ (其中,} s \in C \mbox{,且 } Re(z) > 1 \mbox{)}$$

02
6

【转】插画版Kubernetes指南

0
归档:2020年6月分类:架构之路

插画版Kubernetes指南(小孩子也能看懂的kubernetes教程)
是根据一个视频翻译过来的,比较形象

编者按:Matt Butcher 是 Deis 的平台架构师,热爱哲学,咖啡和精雕细琢的代码。有一天女儿走进书房问他什么是 Kubernetes,于是就有了这本插画版的 Kubernetes 指南,讲述了勇敢的 Phippy(一个 PHP 应用),在 Kubernetes 的冒险故事,满满的父爱有木有!

某一天

160704211922444

有一天,女儿走进书房问我:『亲爱的爸爸,什么是 Kubernetes 呢?』

160704211922441

我回答她:『Kubernetes 是一个开源的 Docker 容器编排系统,它可以调度计算集群的节点,动态管理上面的作业,保证它们按用户期望的状态运行。通过使用「labels」和「pods」的概念,Kubernetes 将应用按逻辑单元进行分组,方便管理和服务发现。』

女儿更疑惑了……于是就有了这个故事。

给孩子的插画版 Kubernetes 指南

160704211922442

很久很久以前,有一个叫 Phippy 的 PHP 应用,她很单纯,只有一个页面。她住在一个托管服务里,周围还有很多可怕的应用,她都不认识,也不想去认识,但是他们却要共享这里的环境。所以,她一直都希能有一个属于自己的环境:一个可以称作 home 的 webserver。

160704211922446

每个应用的运行都要依赖一个环境,对于一个 PHP 应用来说,这个环境包括了一个 webserver,一个可读的文件系统和 PHP 的 engine。

160704211922443

有一天,一只可爱的鲸鱼拜访了 Phippy,他建议 Phippy 住在容器里。Phippy 听从了鲸鱼的建议搬家了,虽然这个容器看起来很好,但是……怎么说呢,就像是漂浮在海上的一个小房间一样,还是没有家的感觉。

160704211922445

不过容器倒是为应用提供了隔离的环境,在这个环境里应用就能运行起来。但是这些相互隔离的容器需要管理,也需要跟外面的世界沟通。共享的文件系统,网络,调度,负载均衡和资源分配都是挑战。

160704211922447

『抱歉……孩子……』鲸鱼耸耸肩,一摇尾消失在了海平面下…… Phippy 还没有来得及失望,就看到远方驶来一艘巨轮,掌舵的老船长非常威风。这艘船乍一看就是大了点,等到船走近了,Phippy 才发现船体两边挂满了皮筏。

老船长用充满智慧的语气对 Phippy 说:『你好,我是 Kube 船长』。

160704211922449

『Kubernetes』是希腊语中的船长,后来的『Cybernetic』和『Gubernatorial』这两个词就是从 Kubernetes 衍生来的。Kubernetes 项目由 Google 发起,旨在为生产环境中成千上万的容器,构建一个健壮的平台。

『您好,我是 Phippy。』

『很高兴认识你。』船长边说,边在 Phippy 身上放了一个 name tag。

1607042119224410

Kubernetes 使用 label 作为『nametag』来区分事物,还可以根据 label 来查询。label 是开放式的:可以根据角色,稳定性或其它重要的特性来指定。

1607042119224412

Kube 船长建议 Phippy 可以把她的容器搬到船上的 pod 里,Phippy 很高兴地接受了这个提议,把容器搬到了 Kube 的大船上。Phippy 感觉自己终于有家了。

1607042119224411

在 Kubernetes 中,pod 代表着一个运行着的工作单元。通常,每个 pod 中只有一个容器,但有些情况下,如果几个容器是紧耦合的,这几个容器就会运行在同一个 pod 中。Kubernetes 承担了 pod 与外界环境通信的工作。

1607042119224413

Phippy 对这一切都感到很新奇,同时她也有很多与众不同的关注点:『如果我想要复制自己该怎么做呢?按需的……任意次数的可以吗?』

『很简单。』船长说道,接着就给 Phippy 介绍起了 replication controller。

1607042119224414

  Replication controller 提供了一种管理任意数量 pod 的方式。一个 replication controller 包含了一个 pod 模板,这个模板可以被不限次数地复制。通过 replication controller,Kubernetes 可以管理 pod 的生命周期,包括扩/缩容,滚动部署和监控等功能。

1607042119224418

Phippy 就这样在船上和自己的副本愉快地生活了好多天。但是每天只能面对自己的副本,这样的生活也太孤单了。

Kube 船长慷慨地笑道:『我有好东西给你。』

说着,Kube 船长就在 Phippy 的 replication controller 和船上其它地方之间建了一个隧道:『就算你们四处移动,这个隧道也会一直待在这里,它可以帮你找到其它 pod,其它 pod 也可以找到你。』

1607042119224415

service 可以和 Kubernetes 环境中其它部分(包括其它 pod 和 replication controller)进行通信,告诉它们你的应用提供什么服务。Pod 可以四处移动,但是 service 的 IP 地址和端口号是不变的。而且其它应用可以通过 Kubernetes 的服务发现找到你的 service。

1607042119224416

有了 service,Phippy 终于敢去船上其它地方去玩了,她很快就有了新朋友 Goldie。有一天,Goldie 送了 Phippy 一件礼物,没想到 Phippy 只看了一眼就哭了。

『你怎么哭了?』Goldie 问道。

『我太喜欢这个礼物了,可惜没地儿放……』Phippy 都开始抽泣了。Goldie 一听原来是这么回事,马上就告诉 Phippy:『为什么不放在一个 volume 里呢?』

1607042119224417

Volume 代表了一块容器可以访问和存储信息的空间,对于应用来说,volume 是一个本地的文件系统。实际上,除了本地存储,Ceph、Gluster、Elastic Block Storage 和很多其它后端存储都可以作为 volume。

1607042119224419

Phippy 渐渐地爱上了船上的生活,她很享受和新朋友的相处(Goldie 的每个 pod 副本也都很 nice)。但是回想起以前的生活,她又在想是不是可以有一点点私人空间呢?

Kube 船长很理解:『看起来你需要 namespace。』

1607042119224421

Namespace 是 Kubernetes 内的分组机制。Service,pod,replication controller 和 volume 可以很容易地和 namespace 配合工作,但是 namespace 为集群中的组件间提供了一定程度的隔离。

1607042119224420

于是,在 Kube 船长的船上,Phippy 和她的朋友们开始了海上的历险,最重要的是,Phippy 找到了自己的家。

从此,Phippy 过上了幸福的生活。
转载自:https://www.cnblogs.com/kouryoushine/articles/8007648.html

公告栏

欢迎大家来到我的博客,我是dodoro,希望我的博客能给你带来帮助。