asp.net core系列 71 Web架构分层指南
一.概述
本章Web架构分层指南,参考了“Microsoft应用程序体系结构指南”(该书是在2009年出版的,当时出版是为了帮助开发人员和架构师更快速,更低风险地使用Microsoft平台和.NET Framework设计和构建有效,高质量的应用程序)。虽然已过去十年了,技术架构已更新(如流行的DDD/CQRS模式,微服务,容器),但web分层思想还是一样可取,下面是一个“传统N分层设计”架构图,该架构在2010年左右是最流行的,包括了表现层presentation,服务层services,业务层business,数据访问层data,横切关注点cross,如下所示:

对比传统多层或三层.net web架构,下图是当前流行的.net web微服务架构,在web程序分层之上还包含了容器,web api网关,各服务对应的数据存储(sqlserver,redis,mongoDB),web程序有web api并结合应用了DDD\CQRS分层模式,以及系统各种中间件。

下图是一个订单微服务站点,包含了简化的cqrs分层,蓝色长方格是表示cqrs分层的职责,包括了查询 Queries viewModels和命令Command Domain-Model以及上层的应用服务层Application,如下所示

1.1 逻辑分层设计架构类型
(1) 最传统的分层是经典三层设计,包括表现层,业务层,数据层.
(2) 基于服务的解决方案SOA,公开应用程序业务功能的服务层,服务层在业务层之上。
(3) 基于领域驱动设计的DDD\CQRS分层模式
(4) 微服务架构。
这4种web分层架构是不断的演化改变 ,每一种分层架构并不是独立的思想,它包含了演化之前的架构分层思想。从以前三层架构到现在的微服务架构,是适应每个时代互联网业务实现的需求。
|
功能 |
SOA |
微服务 |
|
组件大小 |
大块业务逻辑 |
单独任务或小块业务逻辑 |
|
耦合 |
通常松耦合 |
总是松耦合 |
|
公司架构 |
任何类型 |
小型、专注于功能交叉团队 |
|
管理 |
着重中央管理 |
着重分散管理 |
|
目标 |
确保应用能够交互操作 |
执行新功能、快速拓展开发团队 |
二.Web分层设计步骤
1.分层策略
(1)分层粒度是确定分层策略的关键第一步.
(2)在逻辑分层中, 多层是在同一进程中运行,这可以利用更高性能的通信机制。例如通过组件接口直接调用。必须小心保持层之间的封装和松散耦合。
(3)在物理分层中,确定合适的通信机制,该机制考虑到通信延迟并保持之间的松散耦合。
(4)多层中,考虑它们之间如何相互影响,将确保性能和灵活性之间的良好平衡。
2.确定需要层
最常用的方法是将表现层,服务层,业务层和数据访问层功能分离到单独的层中。某些应用程序还引入了各种组件像缓存、日志、消息队列等。
3.决定如何分发各层和组件
对于web体系架构,一般都是在一个物理层,只有在必要时,才应在不同的物理层上分布层和组件。这是实现分布式部署的常见原因包括安全策略,物理约束,共享业务逻辑和可伸缩性。
4.确定是否需要折叠层
一般规则是您应始终将功能分组到层中。在某些情况下,一个层可以充当代理或传递层,提供封装和松散耦合,而不提供大量功能。但是,通过分离该功能,您可以稍后对其进行扩展,而对设计中的其他层几乎没有影响,如:应用服务层。
5.确定层之间引用的规则
在分层策略时,您必须定义层如何相互交互的规则(交互是指:各层引用的关系)。指定交互规则的主要原因是最小化依赖性并消除循环引用。因此应该遵循以下方法之一:
(1)自上而下的交互
较高级别的层可以与下面的层交互,但是较低级别的层不应该与上面的层交互。此规则将帮助您避免层之间的循环依赖关系,以及要降低层之间的依赖性。
(2)严格的互动
每个层必须仅与下面的层进行交互。此规则将强制严格分离关注点,其中每个层仅知道下面的层。此规则的好处是对层界面的修改只会影响上面的层。如果您正在设计一个将随着时间的推移进行修改以引入新功能并且您希望最大限度地减少这些更改的影响的应用程序,或者您正在设计可能分布在不同物理层上的应用程序,请考虑使用此方法。
(3)松散的互动
较高级别的层可以绕过层直接与较低级别的层交互。这可以提高性能,但也会增加依赖性。换句话说,对较低级别层的修改可以影响上面的多个层。
下图是一个示例:该web架构示例是使用了 cqrs 模式,涉及到了事件源es, 事件源实现本因该分离到命令域和查询域, 而项目中应用服务层直接引用了底层数据访问层Dapper(绕过了领域层), 这样底层Dapper接口方法的修改或换成EF将影响顶层应用服务层,这属于第三种"松散的互动"。 应该推荐使用第一种自上而下的交互。

6.确定跨领域问题
定义层后,必须标识跨越层的功能。此功能通常被描述为横切关注点,包括日志记录,缓存,验证,身份验证和异常管理。确定应用程序中的每个横切关注点非常重要,并设计单独的组件以尽可能地管理这些问题。此方法可帮助您实现更好的可重用性和可维护性。
如下图所示:NLog与Redis是具体实现组件,实现了Common层中的日志和缓存接口,Common层就是横切组件,是跨层可重用的。像Ioc也是横切组件。 下图层的名称没有标识跨越层的功能,如果是GFNetCore.Infra.CrossCutting.IoC 这样命名会更好。

7.定义层之间的接口
为层定义接口时,主要目标是强制层之间的松散耦合。这意味着层不应暴露另一层可能依赖的内部细节。相反,层的接口应设计为通过提供隐藏层内组件细节的公共接口来最小化依赖性。这种隐藏称为抽象,有许多不同的方法来实现它。以下设计方法可用于定义层的接口:
(1)抽象接口
通过定义抽象基类或接口类来实现,该类充当具体类的类型定义。该类型定义了一个公共接口,该层的所有使用者都使用该接口与该层进行交互。这是一种面向接口编程,例如:表现层调用应用服务层的接口。表现层在CustomerController控制器中如下所示(通过依赖注入后,构造方法来实例):
//表现层调用应用服务层ApplicationService
private readonly ICustomerAppService _customerAppService; public CustomerController(ICustomerAppService customerAppService,
INotificationHandler<DomainNotification> notifications) : base(notifications)
{
_customerAppService = customerAppService;
}
但在项目中,为了简化开发量,表现层调用应用服务层的实现类(违反了面向接口编程)。表现层在CustomerController控制器中如下所示:
//调用应用服务层ApplicationService
private readonly CustomerAppService _customerAppService = null; //日志对象
public readonly ILoggerEX _logger; public CustomerController(INotificationHandler<DomainNotification> notifications,
ILoggerEX logger,
CustomerAppService customerAppService)
: base(notifications)
{
_customerAppService = customerAppService;
_logger = logger;
}
(2)依赖倒置
这是一种编程风格,是面向接口编程的实现,依赖倒置的应用如:DDD\CQRS, 层依赖于层接口,而不是层依赖于另一个层的实现。依赖注入模式是依赖性反转的常见实现。依赖性反转方法提供了灵活性,可以帮助实现可插入设计,因为依赖性是通过配置而不是代码组成的。它还可以最大化可测试性,因为您可以轻松地将具体测试类注入到设计的不同层中。
依赖性是通过配置的,如下代码所示,由CommandRepository类来实现ICommandRepository接口:
services.AddScoped<ICommandRepository<CommandModels.Customer>, CommandRepository<CommandModels.Customer>>();
services.AddScoped<IQueryRepository<QueryModels.Customer>, QueryRepository<QueryModels.Customer>>();
(3) 基于消息
可以使用基于消息的通信来实现接口并提供层之间的交互,.net技术如:wcf, web services, msmq它们支持跨物理和进程边界的交互(以xml的soap格式传输),但这是对于09年流行的web架构。现在基于消息多数用web api技术,是面向微服务开发(以json的rest api)。
参考资料
asp.net core系列 71 Web架构分层指南的更多相关文章
- asp.net core系列 40 Web 应用MVC 介绍与详细示例
一. MVC介绍 MVC架构模式有助于实现关注点分离.视图和控制器均依赖于模型. 但是,模型既不依赖于视图,也不依赖于控制器. 这是分离的一个关键优势. 这种分离允许模型独立于可视化展示进行构建和测试 ...
- asp.net core系列 39 Web 应用Razor 介绍与详细示例
一. Razor介绍 在使用ASP.NET Core Web开发时, ASP.NET Core MVC 提供了一个新特性Razor. 这样开发Web包括了MVC框架和Razor框架.对于Razor来说 ...
- asp.net core 系列 18 web服务器实现
一. ASP.NET Core Module 在介绍ASP.NET Core Web实现之前,先来了解下ASP.NET Core Module.该模块是插入 IIS 管道的本机 IIS 模块(本机是指 ...
- asp.net core 系列 16 Web主机 IWebHostBuilder
一.概述 在asp.net core中,Host主机负责应用程序启动和生存期管理.host主机包括Web 主机(IWebHostBuilder)和通用主机(IHostBuilder).Web 主机是适 ...
- asp.net core系列 63 领域模型架构 eShopOnWeb项目分析 上
一.概述 本篇继续探讨web应用架构,讲基于DDD风格下最初的领域模型架构,不同于DDD风格下CQRS架构,二者架构主要区别是领域层的变化. 架构的演变是从领域模型到CQRS, 一开始DDD是用领域 ...
- asp.net core系列 62 CQRS架构下Equinox开源项目分析
一.DDD分层架构介绍 本篇分析CQRS架构下的Equinox开源项目.该项目在github上star占有2.4k.便决定分析Equinox项目来学习下CQRS架构.再讲CQRS架构时,先简述下DDD ...
- asp.net core系列 44 Web应用 布局
一.概述 MVC的视图与Razor页面经常共享视觉和程序元素,通过使用布局来完成,布局还可减少重复代码.本章演示了以下内容的操作方法:(1)使用通用布局,(2)自定义布局,(3) 共享指令,(4)在呈 ...
- asp.net core系列 43 Web应用 Session分布式存储(in memory与Redis)
一.概述 HTTP 是无状态的协议. 默认情况下,HTTP 请求是不保留用户值或应用状态的独立消息. 本文介绍了几种保留请求间用户数据和应用状态的方法.下面以表格形式列出这些存储方式,本篇专讲Sess ...
- asp.net core系列 41 Web 应用 MVC视图
一.MVC视图 在Web开发的MVC和Razor中,都有使用视图,在Razor中称为"页"..cshtml视图是嵌入了Razor标记的HTML模板. Razor 标记使用C#代码, ...
随机推荐
- DataGrid通过DataSet保存为xml文件,并导入
做了个小的DataGrid通过DataSet保存为xml_测试,DataGrid通过DataSet保存为xml_测试,通过dataSet.writeXML()和dataSet.readXML()方法完 ...
- Gitlab Runner实现NetCore自动化持续集成
目录 1.开发工具 2.GitLab服务器搭建 3.新建webapi 4.Dockerfile配置 5.配置docker-compose.yml 6.配置.gitlab-ci.yml 7.在GitLa ...
- 和朱晔一起复习Java并发(四):Atomic
本节我们来研究下并发包中的Atomic类型. AtomicXXX和XXXAdder以及XXXAccumulator性能测试 先来一把性能测试,对比一下AtomicLong(1.5出来的).LongAd ...
- 从无到有构建vue实战项目(六)
十.徒手撸一个vue下拉左侧二级导航 先附上最终效果图: vue代码: <div class="dropdown-menu-explore" v-on:mouseover=& ...
- C#编程.函数.Main()函数
Main()函数是C#应用程序的入口点,执行这个函数就是执行应用程序.也就是说,在执行过程开始时,会执行Main()函数,在Main()函数执行完毕时,执行过程就结束了. Main()函数可以使用如下 ...
- 【杂谈】如何对Redis进行原子操作
什么时候需要进行需要原子操作? 很常见的例子,就是利用Redis实现分布式锁. 实现锁需要哪些条件? 我们知道要实现锁,就需要一个改变锁状态的方法.这个方法能原子地对锁的状态进行检查并修改.如果修改成 ...
- jquery的addClass、removeClass、toggleClass方法
1.addClass()添加类 1.1 用jquery为某一个元素添加类 $("div").addClass("active");//给div元素添加activ ...
- for循环打印空心菱形的新方法
相信大家在学习流程控制的循环结构时,一定都用for循环绘制过菱形和空心菱形吧,我记得我当时写的很麻烦,把一个菱形分为上下两部分,上面2重for循环,下面2重for循环,相信有很多的小伙伴都是这样做的吧 ...
- Qt Socket 收发图片——图像拆包、组包、粘包处理
之前给大家分享了一个使用python发图片数据.Qt server接收图片的Demo.之前的Demo用于传输小字节的图片是可以的,但如果是传输大的图片,使用socket无法一次完成发送该怎么办呢?本次 ...
- LR(1)语法分析器生成器(生成Action表和Goto表)java实现(二)
本来这次想好好写一下博客的...结果耐心有限,又想着烂尾总比断更好些.于是还是把后续代码贴上.不过后续代码是继续贴在BNF容器里面的...可能会显得有些臃肿.但目前管不了那么多了.先贴上来吧hhh.说 ...