领域驱动设计(六) - 架构设计浅谈

单用一篇文章很难把这个主题描述的清楚,但为了系列的完整性,笔者会围绕DDD中所介绍的内容做下初步总结,使读者有一个连续性。

一、概述

现在不是局部解决问题的时代了要运用新的技术创造新的效率提升,需要整个商业链条一起前进。需要以系统或生态的角度来考虑问题,而不能回到点到点的突破层面。

基于以上背景慢慢的会衍生出新的岗位和角色。技术层面也驱于两级化发展,初级开发越来越倾向于工具的使用,高级研发越来越需要更全面,进而对架构和架构师提出了额外的要求。

1.1、对架构师的要求

  • 全局观:顶层设计+物理架构+应用架构,在顶层设计时充分考虑成本、效率、稳定、性能等因素;
  • 基本要求:前瞻性和解决复杂问题的能力和思路;
  • 能力要求:发现问题、定义问题、解决问题,问题是指一类问题;

1.2、一个良好的架构设计要包含

除了传统意义上所讲的CAP等两高或三高等,笔者认为更应该往下落一点,架构师不能只关心顶层设计,更多的要关心到细节或编码层面,这样才不至于出现实现偏差的问题。以下是笔者总结的几点:

  • 清除单点:系统的每一层级都有不同的集群策略,同时还要考虑容器自动增删功能;
  • 数据一致性:复杂业务可以采用消息中间件再配合不同级别的对帐机制保证;
  • 强弱依赖:在成本可接受的情况下,还可以实现做一套备份流程来临时支撑系统运行,但会导致数据不一致,即一定时间窗口内数据不一致;
  • 热点和极限值处理:a、隔离享用专有资源,适用于大客户的场景与普通客户分离;b、提前预热也可分为大小客户之分;c、简单逻辑、限流、排队等;d、IM这样的场景可以从业务上区分用户极别做到分极处理,在体验上有所侧重;
  • 资损流程:这块的程序对于数据一致性的要求比较高,所以在设计时要额外增加对帐、总额控制、异常、风控等设计;
  • 离线数据流:规模大、来源广、生产链路长,在整体生产和传输链路中,很容易出现数据少量丢失、部分环节失败、数据生产延迟等情况,最终消费在线系统也很难感知少量数据错误进而导致故障。针对离线数据流,要增加不同传输环节数据完整性校验、不同生产环节数据正确性校验、数据延迟监控、数据生产失败监控、端到端数据正确性规则校验、数据错误或延迟兜底预案、数据回滚重刷工具等机制;
  • 异常流程设计:详细可参考笔者的另一篇文章 ​​https://blog.51cto.com/arch/5295170​​ ;

二、SOLID

这个原则,是笔者认为比较好的一种指导思想,这里简单罗列一下概念,有兴趣的读者可深入研究一下,总结如下:

  • 单一职责:单一原则的划分依据并不是根据实体来聚合的,这里是按职责或相互关联的功能来聚合的,职责可以从变化原因的角度来考虑;在落地时可以通过拆分来达到职责的单一;
  • 开闭原则:在实际落地时一般采用抽象的理念,通过继承的方式来扩展功能,同时保证原代码的固化。但一个最重要的前提要考虑到多态的实现,这会直接影响函数的执行顺序;
  • 里氏替换:落地时也是用了抽象和继承思想。它强调的是在不遵循开闭原则的前提下达到一种功能的增强和替换。如果子类不能完整的实现父类的方法,就应该采用依赖、聚合、组合等关系代替继承;
  • 接口隔离:客户端不应该强迫依赖它不需要的接口,类间的依赖关系应该建立在最小的接口上。这个原则多少和单一职责有些关系,我们可以从安全或场景的角度来隔离接口;
  • 依赖倒置:高层模块不依赖低层模块,它们共同依赖同一个抽象,这个抽象接口通常是由高层模块定义,低层模块实现。同时抽象不要依赖具体实现细节,具体实现细节依赖抽象。这个设计原则分了三层:高层-抽象-底层,可以被复用的是高层+抽象。而不是抽象;

三、常用的架构设计

常见的架构一般有经典MVC分层、六边形(又称接口适配器)、CQRS、事件驱动、微服务等,在DDD中主推的是CQRS、六边形、事件驱动三种。从落地情况来看多数会采用分层和六边形架构。

然而现实情况是我们很难去说清我们的系统到底用的是哪种架构模式,多数是一种混合架构,比如任一架构都存在层次、任一架构都可以存在多种服务协议接口。更多的是看如何来表述,架构的表述最重要的一点就是是否能描述清楚层次的职责以及不同层次之间的依赖关系和依赖原则(依赖细节还是依赖抽象)。下面是DDD的经典层次结构:

  • UI表现层:显示信息、解释用户指令,用户指令解释;
  • 用户接口层:提供给UI表现层的数据API接口;
  • 应用接口层:定义软件要完成的任务,指挥领域层来解决问题,它不包含业务规则和状态,但需要管理安全和事务,但可以包含任务的进度状态;而只为下一层中的领域对象协调任务,分配工作,使它们互相协作,只提问;
  • 领域层:也称模型层,控制业务状态,业务状态信息和业务规则,借助基础设施层持久化信息;只回答,内部通过适配器的方式与不同领域交互;
  • 基础设施层:为上面各层提供通用的技术能力;相当于工具和持久层;持久化层的数据模型可以和领域模型完全不同,也可以相同。但要在概念上区分开;

3.1、MVC分层

3.1.1、设计原理

分层架构只有一个约束:上层只能与位于下方的层发生耦合。分为严格分层架构和松散分层架构,通常会采用松散分层架构,通常会遇到逆向调用的问题,一种解决方案是逻辑下沉,也可以采用以下两种方案实现逆向调用:

  • 观察者模式:底层发布事件,高层接收;
  • 调停者模式:底层定义接口,高层实现;同时可把实现做为参数向下传递;

  • 图1:经典松散分层架构,需注意逆向调用的问题;
  • 图2:主要解决领域层依赖基础设施的问题,它是把领域接口实现放在了应用层,由应用层去引用基础设施;
  • 图3:采用依赖倒置的设计,通俗来讲就是:高层模块不依赖低层模块两者都依赖于抽象(接口定义),抽象不依赖细节(接口实现)而细节依赖抽象;

3.1.2、分层架构特殊说明

DDD中应用层一般是很薄的一层,不应该包含业务逻辑,一般用于控制持久化事务和安全认证,或者向其它系统发送消息通知,它主要用于接收端指令,再通过资源库获取聚合实例,然后执行相应的领域服务。

当领域层用于发布领域事件时,应用层可以将订阅方注册到任意数量的事件上,这样的好处是可以对事件进行存储和转发。这时领域层只需关注自己的核心逻辑即可,领域事件发布器也适合做成适配的方式,与底层基础设施间解耦(无论怎么设计领域层就是核心业务不能依赖基础设施层);

3.2、Adapter六边形

3.2.1、设计原理

从分层到六边形需要一个概念上的转换,端口和适配器的功能就是将客户输入转化成能被系统API所理解的参数,实际的操作是委托给内六边形的。这种架构中存在两种适配器:

  • 处理输入:接收用户指令,向应用程序发起请求,调用port;
  • 处理输出:响应请求,输出到外部设备或工具中,实现port;

六边形架构的优点是:

  • 可以轻易开发用于测试的适配器;
  • 应用程序和领域模型可以单独开发;
  • 接入方也可以并行开发,通过适配器方式接入;

3.2.2、六边形架构特殊说明

适配器中的协议部分一般是由公共组件完成。在这种结构中,存储也被认为是一类客户。其存取服务在DDD是用资源库的方式实现。核心域也是通过适配器来与这个客户打交道的(即领域服务如果发布事件也要经过适配器来转发,即图上的颜色区域)。

六边形架构的边界是外六边形,在两个六边形之间应该有一个适配转发器的设计,来自动路由用户的请求到合适的适配器上,再由适配器请求应用程序。同时在适配器和应用程序之间存在一个职责交接的过程。我们也可以换一种图形来表述六边形架构:

3.3、CQRS事件驱动

3.3.1、设计原理

这种架构模式会带来系统的复杂性和基础依赖,但同时也能解决数据显示的复杂性问题,CQRS架构比较适合异构数据和需要读写分离的应用。概要设计如下图所示:

3.3.2、CQRS架构特殊说明

CQRS中存在两种模型,在代码实现时会把除了getById()以外的所有查询方法从原来的查询接口中分离出去;

  • 查询模型:它不是领域模型的一部分只用于显示,设计查询模型时需要注意的是视图的个数;通常会在处理器中内置一个查询过滤器的功能用于减少视图的个数;在查询 时不能直接或间接是修改对象的状态;
  • 命令处理器:专门负责更新对象的状态,当命令执行结束后,实例被更新同时必须发布一个领域事件更新查询模型。详细设计如下图所示:

四、工程设计

有了以上设计后,我们的源码目录一般也要做出相应调整,这一步非常重要因为会涉及到多人开发、打包、发布部署等工作,尤其在跨国时显的会尤为重要,本章是笔者在原来一家公司的落地经验可参考。     模块的设计体现在包路径的结构上,需要和具体的架构结合来设计,比如采用DDD设计,那么DDD从大的方面来讲就是三层:应用服务、领域服务、领域;这就是一层设计。而域内的服务、聚合、实体等可称为二级设计。如果和DDD结合那么建议模块由:

  • 规范:固定顶级  + 上下文名称 + 1级层次(domain|[application/port.adapter]|repository) + [模块] + 2级层次(根据架构来设计service|model|event)
  • 例子:com.xxx   + refund + domain + submodule + service ,com.xxx.refund.domain.service,com.xxx.refund.domain.flowlog.service;

注意事项

  • 不建议用通用的后缀名称,通用的后缀有可能也带来噪音和层次调用限制;而是用模块来区分;
  • 第三层比如domain,这里面可能不包含任何代码;

附、清晰架构完整设计

附一张其它大牛的一张设计图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/57534.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网络安全(黑客技术)自学

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高; 二、则是发展相对成熟…

Python 程序设计入门(001)—— 安装 Python(Windows 操作系统)

Python 程序设计入门(001)—— 安装 Python(Windows 操作系统) 目录 Python 程序设计入门(001)—— 安装 Python(Windows 操作系统)一、下载 Python 安装包二、安装 Python三、测试&…

【JavaEE初阶】Servlet(四) Cookie Session

文章目录 1. Cookie && Session1.1 Cookie && Session1.2 Servlet会话管理操作 1. Cookie && Session 1.1 Cookie && Session Cookie是什么? Cookie是浏览器提供的持久化存储数据的机制.Cookie从哪里来? Cookie从服务器返回给浏览器. 服务…

非线性弹簧摆的仿真(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

寻找旋转排序数组中的最小值——力扣153

文章目录 题目描述解法 二分法 题目描述 解法 二分法 int findMin(vector<int>& nums){int l0, rnums.size()-1;while(l<r){int mid (lr)/2;if(nums[mid]<nums[r]) rmid;else lmid1;}return nums[l];}

解决Win11右键菜单问题

✅作者简介&#xff1a;大家好&#xff0c;我是Cisyam&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Cisyam-Shark的博客 &#x1f49e;当前专栏&#xff1a; 程序日常 ✨特色专栏&…

某科技公司提前批测试岗

文章目录 题目 今天给大家带来一家提前批测试岗的真题&#xff0c;目前已经发offer 题目 1.自我介绍 2.登录页面测试用例设计 3.如何模拟多用户登录 可以使用Jmeter,loadRunner性能测试工具来模拟大量用户登录操作去观察一些参数变化 4.有使用过Jmeter,loadRunner做过性能压…

面试题总结

文章目录 第一阶段:网络1、osi七层模型、tcp\ip 五层模型2、三次握手四次挥手3、交换机路由器工作原理4、vlan的作用5、icmp协议Linux1、cpu、内存、io、磁盘容量、网络流量、load average2、lvm逻辑卷如何创建3、raid磁盘阵列4、开机引导过程5、软连接硬链接6、查找文件命令7…

iOS——Block循环引用

Capturing ‘self’ strongly in this block is likely to lead to a retain cycle 典型的循环引用 self持有了blockblock持有了self(self.name) 这样就形成了self -> block -> self的循环引用 解决办法 强弱共舞 使用 中介者模式 __weak typeof(self) weakSelf sel…

“Rust难学”只是一个谎言

近年来Rust的存在感日渐升高&#xff0c;但是其陡峭的学习曲线似乎总是令人望而生畏。不过谷歌的一项内部调查表明&#xff0c;关于Rust的“难学”或许只是一种谣传。 Rust到底难不难学&#xff1f;谷歌有了Go&#xff0c;为何还要支持Rust&#xff1f;频频陷入内斗的Rust领导…

力扣 C++|一题多解之动态规划专题(2)

动态规划 Dynamic Programming 简写为 DP&#xff0c;是运筹学的一个分支&#xff0c;是求解决策过程最优化的过程。20世纪50年代初&#xff0c;美国数学家贝尔曼&#xff08;R.Bellman&#xff09;等人在研究多阶段决策过程的优化问题时&#xff0c;提出了著名的最优化原理&…

【前端|Javascript第1篇】一文搞懂Javascript的基本语法

欢迎来到JavaScript的奇妙世界&#xff01;作为前端开发的基石&#xff0c;JavaScript为网页增色不少&#xff0c;赋予了静态页面活力与交互性。如果你是一名前端小白&#xff0c;对编程一无所知&#xff0c;或者只是听说过JavaScript却从未涉足过&#xff0c;那么你来对了地方…

MPAndroidChart学习及问题处理

1.添加依赖 项目目录->app->build.gradle dependencies {implementation com.github.PhilJay:MPAndroidChart:v3.0.3 }项目目录->app->setting.gradle dependencyResolutionManagement {repositories {maven { url https://jitpack.io }} }高版本的gradle添加依…

QGraphicsView实现简易地图1『加载离线瓦片地图』

最简单粗暴的加载方式&#xff0c;将每一层级的所有瓦片地图全部加载 注&#xff1a;该方式仅能够在瓦片地图层级较低时使用&#xff0c;否则卡顿&#xff01;&#xff01;&#xff01; 瓦片地图数据来源&#xff1a;水经注-高德地图-卫星地图 瓦片地图瓦片大小&#xff1a;25…

【高级程序设计语言C++】二叉搜索树

1. 二叉搜索树的概念2. 二叉搜索树的功能2.1. 二叉搜索树的简单模型2.2. 二叉搜索树的查找2.3. 二叉搜索树的插入2.4. 二叉搜索树的删除 3. 二叉搜索树的性能分析 1. 二叉搜索树的概念 二叉搜索树&#xff08;Binary Search Tree&#xff0c;简称BST&#xff09;是一种常见的二…

C# Onnx Paddle模型 OCR识别服务

效果 项目 可运行程序exe下载 Demo&#xff08;完整源码&#xff09;下载

03 制作Ubuntu启动盘

1 软碟通 我是用软碟通制作启动盘。安装软碟通时一定要把虚拟光驱给勾选上&#xff0c;其余两个可以看你心情。 2 镜像文件 我使用清华镜像网站找到的Ubuntu镜像文件。 Index of /ubuntu-releases/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 请自己选择镜像…

探索 GPTCache|GPT-4 将开启多模态 AI 时代,GPTCache + Milvus 带来省钱秘籍

世界正处于数字化的浪潮中&#xff0c;为了更好理解和分析大量数据&#xff0c;人们对于人工智能&#xff08;AI&#xff09;解决方案的需求呈爆炸式增长。 此前&#xff0c;OpenAI 推出基于 GPT-3.5 模型的智能对话机器人 ChatGPT&#xff0c;在自然语言处理&#xff08;NLP&a…

Word导出高清PDF

通过word导出pdf清晰度较高的方法_word如何导出高分辨率pdf_Perishell的博客-CSDN博客通过打印机属性设置&#xff0c;让word打印出比较高清的pdf_word如何导出高分辨率pdfhttps://blog.csdn.net/weixin_45390670/article/details/129228568?ops_request_misc%257B%2522reques…

卡片的点击事件通过点击进行路由传参

下面是详情页 通过 接收 <template><div class"detail"><img :src"row.imgUrl"><van-icon name"arrow-left" click"back" /></div> </template><script> export default {created() {let …