MASA Framework - DDD设计(2)

Clean Architecture

国内对于Clean Architecture的翻译很多,干净/整洁/清晰。但无论哪一种都说明了它简洁、清晰的特性。

早期它长这样

31b1ffd0a5a87506fe8240223beb326d.png

看到这张图的同学可能会对另外一张图有印象

洋葱架构(Onion)

305796d3abf622044b102f34bff9387d.png

现在长这样

e01742dce8950045f1ba01a429b4286e.png

看起来好像是亲戚,它们的确也有着千丝万缕的关系

分析Clean Architecture

这部分主要是根据explicit architecture文章的理解整理的,有翻译也有自己理解消化的。如有错漏欢迎指正,谢谢

三大构建块

  • 用户界面

  • 基础设施

  • 应用核心

5f4ef84cb99a63b98a0f9a3b521ae544.png

控制流

  1. 用户界面

  2. 应用核心

  3. 基础设施

  4. 应用核心

  5. 用户界面

a227a155af7fed4f15e477d73deb7ff2.png

工具

左右两侧形成鲜明对比,动机不同

  • HTTP/CLI:告诉应用要做什么

  • SMS/Mailing Server/Search Engine...:应用告诉它们要做什么

595d0017f21f202280e504c0a3e1b037.png

链接工具和交付机制到应用核心

将工具连接到应用程序核心的代码单元称为适配器(端口和适配器架构)。

告诉我们的应用程序做某事的适配器称为主适配器或驱动适配器,而我们的应用程序告诉我们做某事的适配器称为辅助适配器或驱动适配器。

端口

这些适配器为了适应应用核心的一个非常特定的入口点,即端口。端口只不过是工具如何使用应用程序核心或应用核心如何使用它的规范。

你可以看作是接口和DTO

主适配器或主动适配器

主适配器或主动适配器围绕一个端口并使用它来告诉应用核心该做什么。

我们的主动适配器是Controller或Console Commands,它们在其构造函数中注入了一些对象,该对象的类实现了Controller或Console Commands所需的接口(端口)。

端口可以是控制器需要的服务接口或存储库接口,然后将 Service、Repository 或 Query 的具体实现注入并在 Controller 中使用。

或者,端口可以是Command Bus或Query Bus的接口。在这种情况下,将Command Bus或Query Bus的具体实现注入到Controller中,然后Controller构造Command或Query并将其传递给相关Bus。

注:这里其实提到了CQRS

d6f2793cb90d15c3a765c842647b2d27.png

从适配器或被动适配器

与围绕在端口周围的主动适配器不同,从适配器实现一个端口、一个接口,然后在需要端口的任何地方注入应用核心。

可以理解为是右侧是符合应用核心的需要的接口或者对象,而左侧则是包装用例的传达机制,如HTTP/CLI等

aca1bb6fe9127ac5f2afa3758d116b1c.png

假设我们有一个需要持久化数据的需求:

  1. 我们创建一个持久化接口(在左侧端口周围),有一个保存数据的方法和一个通过ID删除数据的方法

  2. 基础设施中提供一个实现类,通过IoC注入这个接口和对应的实现类

  3. 在需要持久化数据的类的构造函数中注入一个持久化接口

  4. 如果有一天我们需要从SQL Server换到MongoDb,只需要替换步骤2中的实现类和注入新的实现类即可

IoC

与上图相比,仅仅是多了一个蓝色的箭头从外部直插入应用核心内部。在上面例子中有提到通过IoC的作用这里就不再重复了。

至此,我们一致在讲解应用核心的外围,而应用核心是我们架构设计的重点。

0d2986bcd6db2ea0583d26f44509a7c2.png

应用核心组织

洋葱架构采用 DDD 分层并将它们合并到端口和适配器架构中。这些层旨在为业务逻辑带来一些组织,即端口和适配器“六边形”的内部,就像在端口和适配器中一样,依赖方向是朝向中心的。

应用层

用例(Use Case)是我们的应用中的一个或多个用户界面触发的流程(业务逻辑)。用户界面可以是终端用户界面也可以是管理界面,或者控制台界面和API。

用例在应用层定义,由DDD和洋葱架构提供,它可以包含端口,ORM接口,搜索引擎接口,消息接口等,也可以是CQRS处理Handlers的地方,发送邮件,调用第三方API等。

应用服务/Command Handler包含用例的业务逻辑,作用是:

  1. 使用Repository查找一个或多个实体

  2. 告诉这些实体做一些领域逻辑

  3. 使用Repository持久化这些实体,保存数据更改

Command Handler可以有两种不同的使用方式:

  1. 包含执行用例的真实业务逻辑

  2. 作为架构中的中间件,接收Command并触发应用服务中的逻辑

71b2859740fb430ff777311abfffc57f.png

领域层

领域内的对象除了有对象本身的属性外,还可以操作该对象内部的属性,这是特定于域本身的,并且独立于触发该逻辑的业务流程,它们是独立的,完全不知道应用层。

a2f138ab61cf54fcaa8a61511e07b9d6.png

领域服务

有时我们会遇到一些涉及不同实体的领域逻辑,无论是否相同,该领域逻辑不属于实体本身,它没有直接责任。

那我们可以使用领域服务来承载这部分逻辑,可能有人会觉得那可以放应用层,但领域逻辑在其他用例中就不能重用了。领域逻辑应该在领域内部,不要上升到应用层。

领域服务可以使用其他领域服务,或者其他领域对象

领域模型

在最中心,依赖于它之外的任何东西,是领域模型,它包含代表领域中某些事物的业务对象。至于如何定义领域模型可以参考第一篇。

组件

组件与应用核心内所有的层交叉,从外贯穿到内部。例如身份验证、授权、计费、用户、评论或账户,但它们依然与领域有关。

像授权和身份验证这样的限界上下文应该被视为隐藏在某种端口后面的外部工具。

fe3705ae9a59a95f6d737866520c3dc6.png

解耦组件

具有完全解耦的组件意味着一个组件不直接了解任何另一个组件。换句话说,它可能没有接口,所以我们需要一些新的架构结构。

比如事件、最终一致性、服务发现等。当你往这条路上走的时候,你就开始脱离单体了。

这里Dapr或许是个不错的选择,它包含了这些功能,对Dapr感兴趣的可以看之前的手把手教你学Dapr系列

MASA Framework解决方案

结合DDD和Clean Architecture以及MASA Framework的特性,我们将在MASA.BuildingBlocks中以接口的形式定义规范,在MASA.Contrib中对接口进行实现。

这意味着你可以只关心BuildingBlocks中的接口定义来编写你的代码,也可以基于接口重新实现在DDD落地中你自己的业务特性来调整或扩展我们提供的默认行为。比如,你有自己的UoW、仓储层等都可以随意换掉。

应用层

应用服务:

实现应用程序的用例,衔接表示层(接口层)与领域层

除此之外,基于MASA EShop的示例中的MASA.EShop.Services.Catalog的CQRS架构演示,应用层也可以承载CQRS的Command Hanlder。除了可以继续使用领域层来解决Command业务外,你也可以选择在此中止,在Command Handler里简化架构直接对Command进行处理。

示例代码:https://github.com/masalabs/MASA.EShop/tree/develop/src/Services/MASA.EShop.Services.Catalog/Application/CatalogBrands

工作单元:

默认事件是在应用服务中首次开启,所以UoW也会在应用层被激活(实际上底层会根据仓储的操作,只有首次增删改才会自动激活,这个功能可以关闭,改为手动控制)

中间件:

对于使用Event Bus开发来说,应用层还可以作为统一的AOP出入口。

例如统一的事件参数验证:

首先需要启用统一验证中间件 https://github.com/masalabs/MASA.EShop/blob/develop/src/Services/MASA.EShop.Services.Catalog/Application/Middleware/ValidatorMiddleware.cs

然后为对应的Event/Command编写验证逻辑 https://github.com/masalabs/MASA.EShop/blob/develop/src/Services/MASA.EShop.Services.Catalog/Application/Catalogs/Commands/DeleteProductCommandValidator.cs

领域层

对于实体、聚合、值对象等概念就不再介绍了,可以参考上一章的内容。

贫血模型VS充血模型

领域中需要限定领域内的业务逻辑,加上EF Core对充血模型的支持,充血模型更适合用作领域模型的开发。

将数据与行为封装,表现出现实业务对象完整行为,每个领域具备明确的职责划分,将逻辑分散到领域对象中。这也是应用层与领域层的一个比较明显的区别。

对于实体相关对象,我们提供了对应的类,当然也包括审计和值对象可能需要用到的枚举类。

枚举类:我们提供了Enumeration,参考自:https://docs.microsoft.com/zh-cn/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/enumeration-classes-over-enum-types

53f85932b7b4b2d8db41ddc7a11e7365.png

领域服务:

领域服务中可以调用其他的领域服务(包括进程内或跨进程),所以我们提供了IDomainService,它的功能包括:

  • 自动协调进程内和跨进程的事件传递

  • 支持被调用方是CQRS

  • 默认支持事件压栈,在UoW Commit后统一触发。也支持实时发送事件(如后续业务可被降级,但跨进程的事件为主业务逻辑不可被降级)

  • 跨进程事件支持最终一致性和Saga

9d78662c21971986a51406bad41e4e21.png

仓储:

领域中定义的仓储为接口,代表在当前领域内关心的业务。比如用户,在用户管理和名片两种业务中,对于IRepository<User>的定义是不同的。但在基础设施中BaseRepository<User>可以是同一个,因为BaseRepository<User>可以是最完整的实现,但领域内仓储服务只认其中一部分

d9eca1004da9cddf397307fb3d10bc5f.png

基础设施层

给接口提供实现,如仓储接口的实现、Event Bus中MQ或中介者的实现(MASA Framework已实现,所以我们的示例中目前只有仓储接口的实现)等。

MASA Framework模板架构

在MASA Framework模板中提供了自由组合的方式,你可以根据你的需求随意调整如是否包含Blazor、Dapr、DDD、CQRS等。

我们的MASA.EShop推荐采用了4种架构方式,从简到繁,本篇介绍最复杂的一个

Minimal APIS + CQRS + Dapr actor

95519b48791557072e7aae8dd4d6eee7.png

MASA.EShop中的Ordering服务就是采用这种架构分层,其实分层解释上面也有,只是之前解释的是站在MASA BuildingBlocks的角度,而接下来将是站在开发者角度。

  • User Interface Layer:它负责提供用户接口,完整前端逻辑。用户也可以是计算机系统,不特指是人。所以这里既可以是API,也可以是Blazor、MVC等。

  • Application Layer:它可以很薄也可以很厚(在当前分层下推荐薄)。负责协调User Interface和Domain,包括服务的编排和转发,AOP,发送事件等。

    如果你有Domain Layer可以把Command做的很薄调用Domaiin。如果你要精简CQRS,也可以不用Domain,在这一层直接做应用服务。当然Query也一样,但Query即便使用Domain也推荐把查询放在应用服务里,这样可以把Query和Command分离来获得CQRS的优势。

  • Domain Layer:业务核心,包括了领域对象和领域服务以及适配器的接口。建议采用充血模型将行为留在领域内,跨领域且需要被复用的可以使用领域服务。仓储接口则限定领域内的仓储行为,与物理仓储不同的是更聚焦业务本身,而不是实体的完整仓储能力。

  • Infrastructure Layer:给接口提供实现,如仓储接口的实现、Event Bus中MQ或中介者的实现(MASA Framework已实现,所以我们的示例中目前只有仓储接口的实现)等。

总结

至此,我们不仅实现了对单体架构的支持,还通过Event Bus对微服务架构提供了支持。

如果你对DDD或者MASA Framework感兴趣,不妨把MASA.EShop跑起来看一下,它提供了4种架构方式参考,可以满足大部分业务场景对架构的要求。

学以致用,学无止境。

参考:

DDD, Hexagonal, Onion, Clean, CQRS, … How I put it all together:https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/

开源地址

MASA.BuildingBlocks:https://github.com/masastack/MASA.BuildingBlocks

MASA.Contrib:https://github.com/masastack/MASA.Contrib

MASA.Utils:https://github.com/masastack/MASA.Utils

MASA.EShop:https://github.com/masalabs/MASA.EShop

MASA.Blazor:https://github.com/BlazorComponent/MASA.Blazor

如果你对我们的 MASA Framework 感兴趣,无论是代码贡献、使用、提 Issue,欢迎联系我们

efc4f1f861cfa9832d1b56309bb9a650.png

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringMVC核心分发器DispatcherServlet分析[附带源码分析]

目录 前言DispatcherServlet初始化过程DispatcherServlet处理请求过程总结参考资料前言 SpringMVC是目前主流的Web MVC框架之一。 如果有同学对它不熟悉&#xff0c;那么请参考它的入门blog&#xff1a;http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html 本…

WPF 展示视频修改为WriteableBitmap

WPF开发者QQ群&#xff1a;340500857由于微信群人数太多入群请添加小编微信号yanjinhuawechat 或 W_Feng_aiQ 邀请入群需备注WPF开发者 PS&#xff1a;有更好的方式欢迎推荐。接着上一篇&#xff0c;进行WriteableBitmap性能优化修改后运行对比如下&#xff1a;前&#xff08;C…

linux之类似Windows的资源管理器gnome-system-monitor(可用这个杀死进程)

1、使用 直接运行下面命令gnome-system-monitor 如果没有安装用下面命令安装sudo apt-get install gnome-system-monitor 2、结果 可以点击右键然后杀死相关进程&#xff0c;这也是杀死进程的办法。

HttpClient异常处理手册

HttpClient异常处理手册 开源中国 发表于 2014-08-26 19:44:06异常处理 HttpClient的使用者在执行HTPP方法&#xff08;GET,PUT,DELETE等&#xff09;&#xff0c;可能遇到会两种主要类型的异常&#xff1a; 传输异常协议异常并不是所有的异常都会传播给HttpClient的用户。Htt…

再读《精通css》02:选择器

2019独角兽企业重金招聘Python工程师标准>>> 1.2 为样式找到目标1、类型选择器用来选择特定类型的原素。比如p&#xff0c;a&#xff0c;h1等等。也叫元素选择器或简单选择器。2、后代选择器用来寻找特定元素或元素组的后代。后代选择器由两个选择器之间的空格表示。…

余弦欧式距离matlab,余弦相似度和欧几里得距离

1.余弦相似度同过两个向量的夹角的余弦值来判断两个向量的相似度。余弦值取值[-1,1],越接近1&#xff0c;两向量夹角越小&#xff0c;越相似。图片.png二维公式&#xff1a;图片.pngn维公式&#xff1a;图片.png存在的问题[1]&#xff1a;余弦相似度更多的是从方向上区分差异&a…

App Store 排名获取。

为什么80%的码农都做不了架构师&#xff1f;>>> https://affiliate.itunes.apple.com/resources/documentation/genre-mapping/ app榜示例 &#xff0c; 取中国免费榜前10条&#xff1a; 首先访问 https://itunes.apple.com/WebObjects/MZStoreServices.woa…

使用 Playwright 对 ASP.NET Core 应用执行功能测试

前言在前面的文章中&#xff0c;我们已经介绍过 Playwright for .NET&#xff0c;它常用于自动化测试已经部署好的 Web 应用。其实&#xff0c;开发人员也可以使用它在 ASP.NET Core 应用程序中进行功能测试。功能测试功能测试是从用户角度编写&#xff0c;用于基于其要求验证系…

PHP自动查找指定文件夹下所有文件BOM和删除所有文件

2019独角兽企业重金招聘Python工程师标准>>> <?php if (isset($_GET[dir])){ //设置文件目录 $basedir$_GET[dir]; }else{ $basedir .; } $auto 1; checkdir($basedir); function checkdir($basedir){ if ($dh opendir(…

php支持cs吗,关于composer、phpmd和phpcs于windows中的安装与使用方法

Composer项目地址 https://getcomposer.org中文 http://docs.phpcomposer.com/Composer是 PHP 的一个依赖管理工具。它允许你申明项目所依赖的代码库&#xff0c;它会在你的项目中为你安装他们。一、安装Composer官网有详细介绍安装方法&#xff0c;包括windows和linux系统。以…

基于ASP.NET Core api 的服务器事件发送

现如今程序员对Web API的调用已经是轻车熟路。但是传统的api调用都是拉模式&#xff0c;也就是主动发起请求去调用一个api.但是程序员往往对另一种很有用的模式很陌生&#xff0c;即推模式。拉模式 - 主动调用并获取结果的模式。推模式 - 订阅并接受数据推送的模式。今天要介绍…

Android之解决java.lang.NoSuchMethodError:android.os.powerManager.isInteractive问题

1、问题 再三星平板(Android 4.2.2系统)我们代码powerManager调用了函数isInteractive方法,出现下面错误 java.lang.NoSuchMethodError:android.os.powerManager.isInteractive 2、解决办法 1、一开始想用try catch来解决,肯定不行,功能没实现,而且进程还是会挂 2…

DDD为何叫好不叫座?兼论DCI与业务分析的方法论

今天&#xff0c;仔细阅读了园子里面的一个朋友写的《一缕阳光&#xff1a;DDD&#xff08;领域驱动设计&#xff09;应对具体业务场景&#xff0c;如何聚焦 Domain Model&#xff08;领域模型&#xff09;&#xff1f;》(http://www.cnblogs.com/xishuai/p/3800656.html)这篇博…

php 实现的字典序排列算法,字典序的一个生成算法

字典序的一个生成算法。最近在LeetCode刷题&#xff0c;刷到一个题&#xff0c;链接&#xff1a;https://leetcode-cn.com/problems/permutation-sequence/这个题要求得长度为n的字典序列的第k个排列。我们知道&#xff0c;字典序列是一个长度为n(n>1)&#xff0c;元素为1~n…

BeetleX服务网关流量控制

为了保障后台服务应用更可靠地运行&#xff0c;网关提供了一些基础流量控制功能&#xff1b;通过这一功能可以限制流转到后台应用服务的处理量&#xff0c;从而让服务在可应对的并发范围内更可靠地运作。服务网关提供了流量控制有基础控制、IP、域名和请求路径。基础配置主要包…

【cocos2d-x】2.0升级为3.0一些常见变化纪录

1.去CC之前2.0的CC**,把CC都去掉&#xff0c;基本的元素都是保留的2.0CCSprite CCCallFunc CCNode ..3.0Sprite CallFunc Node ..2.cc***结构体改变2.0 ccp(x,y) ccpAdd(p1,p2)ccpSubccpMultccpLength(p)ccpDot(p1,p2);ccc3()ccc4()ccWHITECCPointZeroCCSizeZer…

Java Web开发——Servlet监听器

一、Servlet监听器的概念 Servlet监听器是Servlet规范中定义的一种特殊类&#xff0c;用于监听ServletContext、HttpSession和ServletRequest等域对象的创建与销毁事件&#xff0c;以及监听这些域对象中属性发生修改的事件。 监听对象&#xff1a; 1、ServletContext&#xff1…

通过Dapr实现一个简单的基于.net的微服务电商系统(十九)——分布式事务之Saga模式...

目录&#xff1a;一、通过Dapr实现一个简单的基于.net的微服务电商系统二、通过Dapr实现一个简单的基于.net的微服务电商系统(二)——通讯框架讲解三、通过Dapr实现一个简单的基于.net的微服务电商系统(三)——一步一步教你如何撸Dapr四、通过Dapr实现一个简单的基于.net的微服…

php怎么关闭oracle连接,PHP 连接 Oracle

起因由于项目的数据库需要用客户购买的Oracle数据库&#xff0c;所以需要php安装oci扩展。运行环境php : 7.2系统: windows10oracle: 11gR2安装相关环境由于php的oci8扩展还是需要使用到oracle的一些包&#xff0c;所以先下载这一些。下载完成后解压缩这个压缩包&#xff0c;并…

.NET 深度指南:Colors

作者 &#xff5c; Peter Huber译者 &#xff5c; 王强策划 &#xff5c; 丁晓昀我不知道你们是什么情况&#xff0c;但我自己在过去多年中都因为.NET 色彩&#xff08;Colors&#xff09;类中可用的色彩数量有限而头痛不已&#xff0c;为此我试图用 ColorPickers 获得匹配的色…