使用DDD指导业务设计的总结思考
领域驱动设计(DDD) 是 Eric Evans 提出的一种软件设计方法和思想,主要解决业务系统的设计和建模。DDD 有大量难以理解的概念,尤其是翻译的原因,某些词汇非常生涩,例如:模型、限界上下文、聚合、实体、值对象等。
实际上 DDD 的概念和逻辑本身并不复杂,很多概念和名词是为了解决一些特定的问题才引入的,并和面向对象思想兼容,可以说 DDD 也是面向对象思想中的一个子集。如果遵从奥卡姆剃刀的原则,“如无必要,勿增实体”,我们先把 DDD 这些概念丢开,从一个案例出发,在必要的时候将这些概念引入。
从点餐系统出发
我们设计一个点餐系统,往往马上关注到数据库的设计上,想当然的设计出一些数据库表,然后着手于界面、网络请求、如何操作数据库上,业务逻辑被封装到一个叫做 Service 对象上,这个对象不承载任何状态,业务逻辑通过修改数据库实现。核心内容如下:
一般来说这种方法也没有大的问题,甚至工作的很好,Fowler 将这种方法称作为事务脚本(Transaction Script)。还有其他的设计模式,将用户界面、业务逻辑、数据存储作为一个“模块”,可以实现用户拖拽就可以实现简单的编程,.net、VF曾经提供过这种设计模式,这种设计模式叫做 SMART UI。
这种模式有一些好处。
- 非常直观,开发人员学习完编程基础知识和数据库 CRUD 操作之后就可以开发
- 效率高,能短时间完成应用开发
- 模块之间非常独立
麻烦在于,当业务复杂后,这种模式会带来一些问题。
虽然最终都是对数据库的修改,但是中间存在大量的业务逻辑,并没有得到良好的封装。客人退菜,并不是将订单中的菜品移除这么简单。需要将订单的总额重新计算,以及需要通知后厨尝试撤回正在制作中的菜。
不长眼的新手程序员擅自修改数据片段,整体业务逻辑被破坏。这是因为并没有真正的一个 “订单” 的对象负责执行相关的业务逻辑,Sevice 上的一个方法直接就对数据库修改了,保持业务逻辑的完整,完全凭程序员对系统的了解。
模型与领域模型
从上面的例子中,模型是能够表达系统业务逻辑和状态的对象。
模型是一个非常宽泛的概念,任何东西都可以是模型,我们尝试给模型下一个定义,并随后继续将领域模型的概念外延缩小。
模型,用来反映事物某部分特征的物件,无论是实物还是虚拟的古人用八个卦象作为世界运行规律的模型;地图用线条和颜色作为地理信息的模型;IT 系统用 E-R 作为对象或者数据库表关系的模型;
我们知道要想做好一个可持续维护的 IT 系统,实际上需要对业务进行充分的抽象,找出这些隐藏的模型,并搬到系统中来。如果发生在餐厅的所有事物,都要能在系统中找到对应的对象,那么这个系统的业务逻辑就非常完备。
现实世界中的业务逻辑,在 IT 系统业务分析时,适合某个行业和领域相关的,所以又叫做领域。
领域,指的特定行业或者场景下的业务逻辑。
DDD 中的模型是指反应 IT 系统的业务逻辑和状态的对象,是从具体业务(领域)中提取出来的,因此又叫做领域模型。
通过对实际业务出发,而非马上关注数据库、程序设计。通过识别出固定的模式,并将这些业务逻辑的承载者抽象到一个模型上。这个模型负责处理业务逻辑,并表达当前的系统状态。这个过程就是领域驱动设计。
我从这里面学到了什么呢?
我们做的计算机系统实际上,是替代了现实世界中的一些操作。按照面向对象设计的话,我们的系统是一个电子餐厅。现实餐厅中的实体,应该对应到我们的系统中去,用于承载业务,例如收银员、顾客、厨师、餐桌、菜品,这些虚拟的实体表达了系统的状态,在某种程度上就能指代系统,这就是模型,如果找到了这些元素,就很容易设计出软件。
后来,如果我什么业务逻辑想不清楚,我就会把电断掉,假装自己是服务员,用纸和笔走一边业务流程。
分析业务,设计领域模型,编写代码。这就是领域驱动设计的基本过程。随后会介绍,如何设计领域模型,当我们建立了领域模型后,我可以考虑使用领域模型指导开发工作。
- 指导数据库设计
- 指导模块分包和代码设计
- 指导 RESTful API 设计
- 指导事务策略
- 指导权限
- 指导微服务划分(有必要的情况)
我们进行业务系统开发时,大多数人都会认同一个观点:将业务和模型设计清楚之后,开发起来会容易很多。
但是实际开发过程中,我们既要分析业务,也要处理一些技术细节,例如:如何响应表单提交、如何存储到数据库、事务该怎么处理等。
使用领域驱动设计还有一个好处,我们可以通过隔离这些技术细节,先进行业务逻辑建模,然后再完成技术实现,因为业务模型已经建立,技术细节无非就是响应用户操作和持久化模型。
我们可以吧系统复杂的问题分为两类:
- 业务复杂度,软件设计中和业务逻辑相关的问题,例如为订单添加商品,需要计算订单总价,应用折扣规则等。
- 技术复杂度,软件设计中和技术实现相关的问题,例如处理用户输入,持久化模型,处理网络通信等。
当我们分析业务并建模时,过于关注技术实现,会带来极大的干扰。我学到最实用的思维方法,就是把技术复杂度中的用户交互想象成人工交谈,持久化想象成用纸和笔记录。
DDD 还强调,业务建模应该充分的和业务专家在一起,不应该只是实现软件的工程师自嗨。业务专家是一个虚拟的角色,有可能是一线业务人员、项目经理、或者软件工程师。
由于和业务专家一起完成建模,因此尽量不要选用非常专业的绘图的工具和使用技术语言。 DDD 只是一种建模思想,并没有规定使用的具体工具。我这里使用 PPT 的线条和形状,用 E-R 的方式表达领域模型,如果大家都很熟悉 UML 也是可以的。甚至实际工作中,我们大量使用便利贴和白板完成建模工作。
这个建模过程可以是技术人员和业务专家一起讨论出来,也可以是使用 ”事件风暴“ 这类工作坊的方式完成。
这个过程非常重要,DDD 把这个过程称作 协作设计。
通过这个过程,我们得到了领域模型。
上图使我们通过业务分析得到的一个非常基本的领域模型,我们的点餐系统中,会有座位、订单、菜品、评价 几个模型。一个座位可以由多个订单,每个订单可以有多个菜品和评价。
同时,菜品也会被不同的订单使用。
上下文、二义性、统一语言
我们用这个模型开发系统,使用领域模型驱动的方式开发,相对于事务脚本的方式,已经容易和清晰很多了,但还是有一些问题。
有一天,市场告诉我们,这个系统会有一个逻辑问题。就是系统中菜品被删除,订单也不能查看。在我们之前的认知里面,订单和菜品是一个多对多的关系,菜品都不存在了,这个订单还有什么用。
菜品,在这里存在了致命的二义性!!!这里的菜品实际上有两个含义:
- 在订单中,表达这个消费项的记录,也就是订单项。例如,5号桌消费的鱼香肉丝一份。
- 在菜品管理中,价格为30元的鱼香肉丝,包含菜单图片、文字描述,以及折扣信息。
菜品管理中的菜品下架后,不应该产生新的订单,同时也不应该对订单中的菜品造成任何影响。
这些问题是因为,技术专家和业务专家的语言没有统一, DDD 认识到了这个问题,统一语言是实现良好的领域模型的前提,因此应该 ”大声的建模“。我在参与这个过程目睹过大量有意义的争吵,正是这些争吵让领域模型变得原来越清晰。
这个过程叫做统一语言。
和现实生活中一样,产生二义性的原因是因为我们的对话发生在不同的上下文中,我们在谈一个概念必须在确定的上下文中才有意义。在不同的场景下,即使使用的词汇相同,但是业务逻辑本质都是不同的。想象一下,发生在《武林外传》中同福客栈的几段对话。
这段对话中实际上有三个上下文,这里的 ”菜“ 这个词出现了三次,但是实际上业务含义完全不同。
- 大嘴说去买菜,这里的菜被抽象出来应该是食材采购品,如果掌柜对这个菜进行管理,应该具有采购者、名称、采购商家、采购价等。
- 秀才说实习生把账单中的菜算错了价格,秀才需要对账单进行管理,这里的菜应该指的账单科目,现实中一般是会计科目。
- 老白说的客人点了一个酱鸭,这里老白关注的是订单下面的订单项,订单项包含的属性有价格、数量、小计、折扣等信息。
实际上,还有一个隐藏的模型——上架中商品。掌柜需要添加菜品到菜单中,客人才能点,这个商品就是我们平时一般概念上的商品。
我们把语言再次统一,得到新的模型。
4个被红色虚线框起来的区域中,我们都可以使用 ”菜品“ 这个词汇(尽量不要这么做),但大家都明确 ”菜品“ 具有不同的含义。这个区域被叫做上下文。当然上下文不只是由二义性决定的,还有可能是完全不相干的概念产生,例如订单和座位实际概念上并没有强烈的关联关系,我们在谈座位的时候完全在谈别的东西,所以座位也应该是单独的上下文。
识别上下文的边界是 DDD 中最难得一部分,同时上下文边界是由业务变化动态变化的,我们把识别出边界的上下文叫做限界上下文(Bounded Context)。限界上下文是一个非常有用的工具,限界上下文可以帮助我们识别出业务的边界,并做适当的拆分。
限界上下文的识别难以有一个明确的准则,上下文的边界非常模糊,需要有经验的工程师并充分讨论才能得到一个好的设计。同时需要注意,限界上下文的划分没有对错,只有是否合适。跨限界上下文之间模型的关联有本质的不同,我们用虚线标出,后面会聊到这种区别。
使用上下文之后,带来另外一个收获。模型之间本质上没有多对多关系,如果有,说明存在一个隐含的成员关系,这个关系没有被充分的分析出来,对后期的开发会造成非常大的困扰。
聚合根、实体、值对象
上面的模型,尤其是解决二义性这个问题之后,已经能在实际开发中很好地使用了。不过还是会有一些问题没有解决,实际开发中,每种模型的身份可能不太一样,订单项必须依赖订单的存在而存在,如果能在领域模型图中体现出来就更好了。
举个例子来说,当我们删除订单时候,订单项应该一起删除,订单项的存在必须依赖于订单的存在。这样业务逻辑是一致的和完整的,游离的订单项对我们来说没有意义,除非有特殊的业务需求存在。
为了解决这个问题,对待模型就不再是一视同仁了。我们将那相关性极强的领域模型放到一起考虑,数据的一致性必须解决,同时生命周期也需要保持同步,我们把这个集合叫做聚合。
聚合中需要选择一个代表负责和全局通信,类似于一个部门的接口人,这样就能确保数据保持一致。我们把这个模型叫做聚合根。当一个聚合业务足够简单时,聚合有可能只有一个模型组成,这个模型就是聚合根,常见的就是配置、日志相关的。
相对于非聚合根的模型,我们叫做实体。
我们把这个图完善一下,聚合之间也是用虚线链接,为聚合根标上橙色。识别聚合根需要一些技巧。
- 聚合根本质上也是实体,同属于领域模型,用于承载业务逻辑和系统状态。
- 实体的生命周期依附于聚合根,聚合根删除实体应该也需要被删除,保持系统一致性,避免游离的脏数据。
- 聚合根负责和其他聚合通信,因此聚合根往往具有一个全局唯一标识。例如,订单有订单 ID 和订单号,订单号为全局业务标识,订单 ID 为聚合内关联使用。聚合外使用订单号进行关联应用。
还有一类特殊的模型,这类模型只负责承载多个值的用处。在我们饭店的例子中,如果需要对账单支持多国货币,我们将纯数字的 price 字段修为 Price 类型。
public Clsss Price(){private String unit;private BigDecimal value;public Price(String unit,BigDecimal value){this.unit = unit;this.value = value;}
}
价格这个模型,没有自己的生命周期,一旦被创建出来就无须修改,因为修改就改变了这个值本身。所以我们会给这类的对象一个构造方法,然后去除掉所有的 setter 方法。
我们把没有自己生命周期的模型,仅用来呈现多个字段的值的模型和对象,称作为值对象。
值对象一开始不是特别好理解,但是理解之后会让系统设计非常清晰。”地址“是一个显著的值对象。当订单发货后,地址中的某一个属性不应该被单独修改,因为被修改之后这个”地址“就不再是刚刚那个”地址“,判断地址是否相同我们会使用它的具体值:省、市、地、街道等。
值对象是相对于实体而言的,对比如下:
另外值得一提的是,一个模型被作为值对象还是实体看待不是一成不变的,某些情况下需要作为实体设计,但是在另外的条件下却最好作为值对象设计。
地址,在一个大型系统充满了二义性。
- 作为订单中的收货地址时,无需进行管理,只需要表达街道、门牌号等信息,应该作为值对象设计。为了避免歧义,可以重新命名为收货地址。
- 作为系统地理位置信息管理的情况中具有自己的生命周期,应该作为实体设计,并重命名为系统地址。
- 作为用户添加的自定义地址,用户可以根据 ID 进行管理,应该作为实体,并重命名为用户地址。
我们使用蓝色区别实体和聚合根,更新后的模型图如下:
虽然我们使用 E-R 的方式描述模型和模型之间的关系,但是这个E-R图使用了颜色、虚线,已经和传统的 E-R 图大不相同,把这种图暂时叫做CE-R图(Classified Entity Relationship)。DDD没有规定如何画图,你可以使用其他任何画图的方法表达领域模型。
使用领域模型指导程序设计
在了解到 DDD 之前,到底该用一对多和多对多关系?RESTful API 设计时到底应该选哪一个对象作为资源地址,评价应该放到订单路径下还是单独出来?订单删除相关有多少对象应该纳入事务管理?
在没有领域模型之前,这些大概率凭借经验决定,当我们把领域模型设计出来之后,领域模型可以帮助我们做出这些指导。领域模型不只是为编写业务逻辑代码使用,这样对领域模型来说就太可惜了。
下面是领域模型指导软件开发的一些方面,具体细节后面会再逐个讨论。
指导数据库设计
通过 CE-R 图,我们明显可以设计出数据库了。不过还有一些细节需要注意。
首先,在之前的认知里面,多对多关系是非常正常的。但是通过对领域模型的分析后发现,传统处理多对多关系时,需要额外增加一张关联表,这张关联表本质上是一个”关系“的实体没有被发掘出来。否则,在实际开发中会造成系统耦合,以及使用 ORM 的时候产生困惑。
菜品和订单之间是多对多关系吗?
如果是,菜品和订单之间耦合了。实际上,菜品的管理处于系统操作的上游,菜品不依赖订单的任何操作,也就是说订单的任何变化菜品无需关心。
订单拥有多个订单项,每个订单项从菜品读入数据并拷贝,或者引用一个菜品的全局 ID (菜品在另外一个聚合)。这样在设计表结构时订单和订单项关联,订单项不关联菜品。订单项应该从程序读取菜品信息。看起来多对多的关系,被细致分析后,变成了一个一对多关系。
在使用 ORM 时,良好的领域模型尤其有用。不合适的关联关系不仅让 ORM 关联变得混乱,还会让 ORM 的性能变差。
使用领域模型建立数据库的要点:
- 留意多对多关系,并拆解成一对多关系
- 值对象和实体往往为一对一关系
- 使用 ORM 时,聚合根和实体可以配置为级联删除和更新
- 禁止聚合根之间进行关联
指导 API 设计
RESTful API 已经变成了主流 API 设计方式,当设计好领域对象后,设计 API 的难度大大降低。
使用聚合根作为 URI 的根路径,使用实体作为子路径。通过 ID 作为 Path 参数。
值对象没有 ID,应该只能依附于某个实体的路径下做更新操作。
另外根据这个关系,处理批量操作的时候应该在实体的上一级完成,例如批量添加订单的订单项,可以设计为:
POST /orders/{orderId}/items-batch
不要设计为:
POST /orders/{orderId}/items/batch
指导对象设计
在实践中过程中,像 Java、Typescript具有类型系统的语言,对象很容易被误用。如果 User 对象既被拿来当做数据库操作使用,又被拿来当做接口呈现使用,这个类最终变成了上帝类,存在大量可有可无的属性。
例如用户注册时候需要输入重复密码,如果在 User 对象中添加 confirmPassword 属性,存储时候确并不需要。
因此 DDD 中,数据库各种对象的使用应该针对不同的场景设计。回到我们上面说的技术复杂度和业务复杂度中来。领域模型解决业务复杂度的问题,领域模型只应该被用作处理业务逻辑,存储、业务表现都应该和领域模型无关。
简单来说,可以把这些 Plain Object 分为三类:
- DTO,和交互相关或者和后端、第三方服务对接
- Entity,数据库表映射
- Model,领域模型
另外,在使用领域模型使用上也需要额外注意
- 领域对象尽量使用组合的方式,而不是继承,现实业务逻辑中继承这种概念实际上很少。例如菜品的设计,有热菜、汤菜、凉菜,实际上这里面并不是菜的继承,而应该抽象出分类这个模型。
- 不要滥用领域模型,有些业务逻辑,实在找不出一个领域模型很正常,所以 DDD 中存在一个领域服务。例如,生成一个 UUID。有些业务逻辑不持有系统业务状态,Eric 的书中比喻为像加油站一样的业务逻辑。
指导代码组织
代码组织,通俗来说就是如何分包。一种狭义的对 DDD 的理解就是指按照 DDD 风格进行代码组织,虽然 DDD 的内容远不止于此。
在很长一段时间,我对 DDD 分包策略陷入困惑,后来我明白到,讨论 DDD 风格的分包,必须将单体引用和微服务应用分开考虑。
微服务应用在逻辑上和解耦良好的单体应用是一致的。
但是微服务是一种分布式架构,映射到单体应用中,各个包分布到不同的服务器中了。我们先以单体应用入手,最后再讨论如何将单体应用架构映射到到微服务中。
在事务脚本的模式中,我们一般将代码分为三层架构。DDD 特别的抽离出一层叫做 application。这一层是 DDD 的精华,领域模型关心业务逻辑,但是不关心业务场景。
application 用来隔离业务场景,显得非常重要。举个例子,用户被添加到系统中,领域模型处理的是:
- 用户被添加
- 授予基本权限
- 积分规则创建
- 账户创建(三户模型,客户、用户、账户往往分开)
- 客户资料录入
但是,用户被添加到系统中由多个应用场景触发。
- 用户被邀请注册
- 用户自己注册
- 管理员添加用户
application 需要隔离应用场景,并组织调配领域服务,才能使得领域服务真正被复用。因此 application 需要承担事务管理、权限控制、数据校验和转换等操作。当领域服务被调用时,应该是纯粹业务逻辑,并与场景无关。
如果我们将三层架构和 DDD 架构对比,DDD 架构如右图所示。
我们将 DDD 的代码架构展开,可以看到更为细节的内容。 DDD 代码实现上需要 Repository、Factory 等概念,但这些是可选的,我们在后面具体讲代码结构的部分再阐述。
我们再来看,DDD 的单体应用架构映射到微服务架构下会是怎么样的。
微服务必须考虑到不再是一个服务,Domain 层被抽离出来作为 Domain Server 存在,Domain Server 不关心业务场景,因此不需要 application 层。Application Server 需要 Application 层,Domain 层由后端的 Domain Server 提供。
另外补充,一些 DDD 代码组织的基本逻辑:
- 隔离业务复杂度和技术复杂度
- 使用接口隔离有必要的耦合和依赖倒置
相关文章:
使用DDD指导业务设计的总结思考
领域驱动设计(DDD) 是 Eric Evans 提出的一种软件设计方法和思想,主要解决业务系统的设计和建模。DDD 有大量难以理解的概念,尤其是翻译的原因,某些词汇非常生涩,例如:模型、限界上下文、聚合、…...
面试官问:如何确保缓存和数据库的一致性?
如果你对这个问题有过研究,应该可以发现这个问题其实很好回答,如果第一次听到或者第一次遇到这个问题,估计会有点懵,今天我们来聊聊这个话题。 1、问题分析 首先我们来看看为什么会有这个问题! 我们在日常开发中&am…...
16.数据库Redis
一、基本概念 Redis(Remote Dictionary Server)译为“远程字典服务”,它是一款基于内存实现的键值型 NoSQL 数据库, 通常也被称为数据结构服务器,这是因为它可以存储多种数据类型,比如 string(字…...
【Redis高级-集群分片】
单机安装Redis首先需要安装Redis所需要的依赖:yum install -y gcc tclRedis安装包上传到虚拟机的任意目录:我放到了/tmp目录:解压缩:tar -zxvf /tmp/redis-6.2.4.tar.gz -C /tmp解压后:进入redis目录:cd /t…...
CSDN - CSDN27题解
文章目录幸运数字题目描述解题思路AC代码投篮题目描述解题思路AC代码通货膨胀-x国货币题目描述解题思路AC代码最后一位题目描述解题思路AC代码CSDN编程竞赛报名地址:https://edu.csdn.net/contest/detail/41 这次题目描述刚开始好像有些问题,之后被修正了…...
docker拉取mysql
搜索mysql版本docker search mysql搜索获赞数(星星数量) 大于 1000 的镜像docker search --filterstars1000 mysql搜索官方发布的版本docker search --filter is-officialtrue mysql搜索版本号docker search mysql57拉取docker pull devbeta/mysql57查看下载镜像docker images启…...
在Linux上安装Python3
记录:373场景:在CentOS 7.9操作系统上,安装Python-3.8.9环境。版本:JDK 1.8 Python-3.8.9官网地址:https://www.python.org下载地址:https://www.python.org/ftp/python/1.安装基础依赖1.1安装gcc(1)安装命…...
23 种设计模式的通俗解释,看完秒懂
01 工厂方法 追 MM 少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是 MM 爱吃的东西,虽然口味有所不同,但不管你带 MM 去麦当劳或肯德基,只管向服务员说「来四个鸡翅」就行了。麦当劳和肯德基就是生产鸡翅的 Factory 工厂模式&…...
如何做好需求管理?经验方法、模型、工具
需求管理能力是衡量产品经理能力的一个重要指标。因为需求是产品的基石,只有选取恰当的方法进行需求分析及管理,才能更好的构建产品方案,从而输出精准的产品定义。结合本人学习和自身经验,打算将需求管理分”需求挖掘”、”需求分…...
怎么用期货做风险对冲(如何利用期货对冲风险)
不同期货市场的同一期货品种的对冲交易怎么做 不同 期货市场 的同一期货品种的 对冲交易 。 因为地域和 制度环境 不同,同一种期货品种在不同市场的同一时间的价格很可能是不一样的,并且也是在不断变化的。 这样在一个市场做多头买进࿰…...
C++标准模板库type_traits源码剖析
一、type_traits源码介绍 1、type_traits是C11提供的模板元基础库。 2、type_traits可实现在编译期计算。包括添加修饰、萃取、判断查询、类型推导等等功能。 3、type_traits提供了编译期的true和false。 二、type_traits的作用 1、根据不同类型,模板匹配不同版本…...
Python获取公众号(pc客户端)数据,使用Fiddler抓包工具
前言 嗨喽~大家好呀,这里是魔王呐 ❤ ~! 今天来教大家如何使用Fiddler抓包工具,获取公众号(PC客户端)的数据。 Fiddler是一个http协议调试代理工具,它能够记录并检查所有你的电脑和互联网之间的http通讯,…...
Maven进阶
这里写目录标题1.分模块开发1.1 模块更新后,会造成的影响2.依赖管理2.1 依赖传递2.2 可选依赖(隐藏自己的依赖,不让别人用)2.3 排除依赖(用别人的资源,把不用的去了)3.聚合与继承3.1 为什么要使用聚合工程?3.2 聚合工程开发2.1 聚合工程三级目录1.分模块开发 我们之前做的项目…...
AXI实战(一)-为AXI总线搭建简单的仿真测试环境
AXI实战(一)-搭建简单仿真环境 看完在本文后,你将可能拥有: 一个可以仿真AXI/AXI_Lite总线的完美主端(Master)或从端(Slave)一个使用SystemVerilog仿真模块的船信体验小何的AXI实战系列开更了,以下是初定的大纲安排: 欢迎感兴趣的朋友关注并支持,以下为正文部分 文章目录…...
数据库管理-第五十六期 监控(20230210)
数据库管理 2023-02-10第五十六期 监控1 怎么监控2 直观3 历史分析4 另一个BUG总结第五十六期 监控 春节后的7天班过后就来到了2月份,本周对之前发现X8M上的那个bug进行补丁修复和协助从12.2迁移了一套PDB到这个一体机上面,2次割接。这周还和原厂老大哥…...
测试开发,测试架构师为什么能拿50 60k呢需要掌握哪些技能呢
这篇文章是软件工程系列知识总结的第五篇,同样我会以自己的理解来阐述软件工程中关于架构设计相关的知识。相比于我们常见的研发架构师,测试架构师是近几年才出现的一个岗位,当然岗位title其实没有特殊的含义,在我看来测试架构师其…...
Miniblink 入门
miniblink官网:入门之前强烈建议将Miniblink介绍仔细看一遍。 MB内核组件标准版接口文档:这里列举了所有的api以及简单的说明,但是本人建议还是看wke.h更方便,里面都是宏实现的,直接搜相关函数即可。 mb demo下载和参…...
[python入门㊷] - python存储数据
目录 ❤ json.dump()存储数据 ❤ json.laod()读取数据 ❤ 保存和读取用户生成的数据 ❤ 重构 JSON(JavaScript Object Notation)格式最初是为JavaScript开发的,但随后成了一种常见格式,被包括Python在内的众多语言采用 ❤ json.dump()存储数据…...
Little Fighter:旺角——NFT 系列来袭!
《小朋友齐打交 2 (LF2) 》是一款流行的格斗游戏,由 Marti Wong 和 Starsky Wong 于 1999 年创作。这是一款非常容易上瘾的游戏,具有多种游戏模式、横向卷轴格斗系统以及 24 个具有复杂动作和连击的不同角色。这款游戏在世界范围内非常受欢迎,…...
基础篇:01-微服务概述
1.单体应用与微服务架构区别 如上图左侧为单体应用架构。在传统单体应用中,所有功能模块都在一个工程中编码、部署,即使是集群部署,也只是单体应用的水平复制。 如上图右侧为微服务架构。在微服务架构的项目中,每个应用会按照领域…...
TC358775XBG替代方案|完美替代 TC358775XBG替代方案|低BOM成本DSI转LVDS方案CS5518
TC358775XBG替代方案|完美替代 TC358775XBG替代方案|低BOM成本DSI转LVDS方案CS5518 TC358775XBG芯片的主要功能是DSI到LVDS桥,通过DSI链路实现视频流输出,以驱动LVDS兼容的显示面板。该芯片支持单链路LVDS高达1366768 24位像素分辨率,双链路L…...
Android开发
前言:因为这学期选了手机APP开发这门课,所以还是写个博客记录一下学习过程,包括安卓开发和ios开发。用到的资料包括课程PPT,和我在网上找的一些视频和资料。 1.Andriod入门 XML:描绘应用界面 (决定APP长什…...
virtualbox虚拟机导入到vmware esxi虚拟机
virtualbox导出的ova文件转换为ovf文件导入到vmware esxi虚拟机 1、下载安装程序 链接:https://pan.baidu.com/s/1pRP8MQswDSDecMB5eJGNYA?pwdmv2q 提取码:mv2q 双击VMware-ovftool-3.0.1-801290-win.x86_64.msi默认安装 2、在cmd中进入到Vware OVF…...
如何使用命名空间管理C++代码
在编写 C 代码时,管理代码组织和消除名称冲突是一个重要问题。 为了解决这个问题,C 提供了一种叫做命名空间的机制。命名空间可以将代码组织在一起,并防止不同模块间的名称冲突。 定义命名空间 首先,你需要在代码中声明命名空间…...
海思3559:BT656调试笔记
前言 海思3559a的sdk例子是没有提供BT1120和BT656视频接入的,但实际上硬件是可以支持接入的。不过前提是只支持逐行方式输入,不支持隔行视频,如果想输入PAL制式的隔行视频,请先用芯片转成逐行再接入。不知道是官方手册有意无意的忽…...
reactor之hooks
Hooks 是一个工具类,它提供了一些方法,用来在 Reactor 的各个阶段添加回调函数,进行全局性的操作。总体来说分为三类: 本部分算是reactor中比较高级的部分,建议在开始上手用reactor做项目前,大概知道有这么…...
单片AR眼镜Monocle揭秘:基于反射棱镜,重15g续航1小时
提问:一个戴近视眼镜的人,会愿意再同时戴一副AR眼镜吗?这个问题对于VR来说并不难,通常VR头显为镜框留出了空间(一些Pancake VR自带屈光调节机制),因此二者并不冲突。然而AR眼镜体积更紧凑&#…...
计算机视觉框架OpenMMLab开源学习(五):目标检测实战
✨写在前面:强烈推荐给大家一个优秀的人工智能学习网站,内容包括人工智能基础、机器学习、深度学习神经网络等,详细介绍各部分概念及实战教程,通俗易懂,非常适合人工智能领域初学者及研究者学习。➡️点击跳转到网站。…...
SpringIOC推导IOC初步
了解准备 什么是Spring? Spring是一款轻量级的控制反转(IOC)和面向切面编程(AOP)的非入侵式开源框架 2002年Spring的前身interface21发布,随后在2004年3月24日正式更名发布Spring1.0版本Spring Frameword缔…...
Linux(centOS7)虚拟机中配置 vim
👏作者简介:大家好,我是小童,Java开发工程师,CSDN博客博主,Java领域新星创作者 📕系列专栏:前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶 Ǵ…...
帮别人做网站哪里可以接单/网站推广软件免费版
闲来无事,看到了拓扑排序就学习了一下。 拓扑排序: 算导上说是使用深搜来对有向无环图进行排序,得到一种线性次序。不过深搜的理解还理解不到。估计是深搜学的不怎么样吧! 说说我理解的这个线性次序吧。我认为算导上的例子就特别…...
做网站外包的公司好干嘛/企业网络营销顾问
我们看一个跨库事务一致性的问题,这是一个简单的场景:有新老两个系统,对应新老两套数据库,新数据库采用分库分表的设计,考虑到项目发布之后可能存在风险,采取了新老系统的并行方案。这个系统的业务比较简单…...
怎么样让百度收录网站/互联网公司排名
题目大意 智能手机九点屏幕滑动解锁,如果给出某些连接线段,求出经过所有给出线段的合法的滑动解锁手势的总数。题目链接: 滑动解锁 题目分析 首先,尝试求解没有给定线段情况下,所有合法的路径的总数。可以使用dfs进行搜…...
星海湾建设管理中心网站/沈阳seo关键词排名
ApplicationInspector是一款功能强大的软件源代码分析与审计工具,它可以帮助研究人员识别和发现目标应用程序中的公众周知的功能以及源代码中有意思的特性,并清楚目标应用的本质特征以及实现的功能。 ApplicationInspector跟传统静态分析工具不同的是&a…...
php可以做网站布局吗/我要登录百度
作者:指针不指南吗 专栏:蓝桥杯倒计时冲刺 🐾马上就要蓝桥杯了,最后的这几天尤为重要,不可懈怠哦🐾 文章目录1.Floyd求最短路2.Dijkstra求最短路(堆优化版)1.Floyd求最短路 题目 链接…...
离石古楼角网站建设/友情链接如何交换
为什么80%的码农都做不了架构师?>>> 各个数据库like写法: oracle数据库: SELECT * FROM user WHERE name like CONCAT(%,#{name},%) 或 : SELECT * FROM user WHERE name like %||#{name}||% SQLServer数据库: SELECT…...