一.概述

  本章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架构分层指南的更多相关文章

  1. asp.net core系列 40 Web 应用MVC 介绍与详细示例

    一. MVC介绍 MVC架构模式有助于实现关注点分离.视图和控制器均依赖于模型. 但是,模型既不依赖于视图,也不依赖于控制器. 这是分离的一个关键优势. 这种分离允许模型独立于可视化展示进行构建和测试 ...

  2. asp.net core系列 39 Web 应用Razor 介绍与详细示例

    一. Razor介绍 在使用ASP.NET Core Web开发时, ASP.NET Core MVC 提供了一个新特性Razor. 这样开发Web包括了MVC框架和Razor框架.对于Razor来说 ...

  3. asp.net core 系列 18 web服务器实现

    一. ASP.NET Core Module 在介绍ASP.NET Core Web实现之前,先来了解下ASP.NET Core Module.该模块是插入 IIS 管道的本机 IIS 模块(本机是指 ...

  4. asp.net core 系列 16 Web主机 IWebHostBuilder

    一.概述 在asp.net core中,Host主机负责应用程序启动和生存期管理.host主机包括Web 主机(IWebHostBuilder)和通用主机(IHostBuilder).Web 主机是适 ...

  5. asp.net core系列 63 领域模型架构 eShopOnWeb项目分析 上

    一.概述 本篇继续探讨web应用架构,讲基于DDD风格下最初的领域模型架构,不同于DDD风格下CQRS架构,二者架构主要区别是领域层的变化. 架构的演变是从领域模型到CQRS,  一开始DDD是用领域 ...

  6. asp.net core系列 62 CQRS架构下Equinox开源项目分析

    一.DDD分层架构介绍 本篇分析CQRS架构下的Equinox开源项目.该项目在github上star占有2.4k.便决定分析Equinox项目来学习下CQRS架构.再讲CQRS架构时,先简述下DDD ...

  7. asp.net core系列 44 Web应用 布局

    一.概述 MVC的视图与Razor页面经常共享视觉和程序元素,通过使用布局来完成,布局还可减少重复代码.本章演示了以下内容的操作方法:(1)使用通用布局,(2)自定义布局,(3) 共享指令,(4)在呈 ...

  8. asp.net core系列 43 Web应用 Session分布式存储(in memory与Redis)

    一.概述 HTTP 是无状态的协议. 默认情况下,HTTP 请求是不保留用户值或应用状态的独立消息. 本文介绍了几种保留请求间用户数据和应用状态的方法.下面以表格形式列出这些存储方式,本篇专讲Sess ...

  9. asp.net core系列 41 Web 应用 MVC视图

    一.MVC视图 在Web开发的MVC和Razor中,都有使用视图,在Razor中称为"页"..cshtml视图是嵌入了Razor标记的HTML模板. Razor 标记使用C#代码, ...

随机推荐

  1. Appium+python自动化(二十二)- 三个臭皮匠顶个诸葛亮-控件坐标获取(超详解)

    简介 有些小伙伴或者是童鞋可能会好奇会问上一篇中的那个monkey脚本里的坐标点是如何获取的,不是自己随便蒙的猜的,或者是自己用目光或者是尺子量出来的吧,答案当然是:NO.获取控件坐标点的方式这里宏哥 ...

  2. C#3.0新增功能09 LINQ 基础07 LINQ 中的查询语法和方法语法

    连载目录    [已更新最新开发文章,点击查看详细] 介绍性的语言集成查询 (LINQ) 文档中的大多数查询是使用 LINQ 声明性查询语法编写的.但是在编译代码时,查询语法必须转换为针对 .NET ...

  3. Js中关于内部方法、实例方法、原型方法、静态方法的个人见解。

    function foo(name){ this.name=name; // 实例方法 this.GetName=function(){ console.log("my name is &q ...

  4. 浏览器和Node 中的Event Loop

    前言 js与生俱来的就是单线程无阻塞的脚本语言. 作为单线程语言,js代码执行时都只有一个主线程执行任务. 无阻塞的实现依赖于我们要谈的事件循环.eventloop的规范是真的苦涩难懂,仅仅要理解的话 ...

  5. Visual Studio 调试系列2 基本调试方法

    系列目录     [已更新最新开发文章,点击查看详细] 在 Visual Studio 上下文中,当调试应用时,这通常意味着你在附加了调试器的情况下(即在调试器模式下)运行应用程序. 执行此操作时,调 ...

  6. 基础篇-1.2Java世界的规章制度(下)

    1 Java运算符 Java世界中的运算其实就是数学运算,而运算符就是其中的媒介. 算术运算符 操作符 描述 + 加法,对符号两边的数值相加 - 减法,符号左边的数减去右边的数 * 乘法,符号两边的数 ...

  7. ironic+nova详解

    ironic+nova详解 说明: Openstack 的安装步骤省略,按照社区的文档即可搭建出一套相对稳定的使用环境.本文档基于Newton版本. 假设现在已经有一套可用的Newton环境, 以下的 ...

  8. 更简洁的log4j日志输出

    参考博客:https://blog.csdn.net/guoquanyou/article/details/5689652 只输出到文本,不输出到控制台 log4j.rootLogger=debug, ...

  9. 浏览器如何加载和解析CSS——CSS样式来源与层叠规则

    关于CSS样式首先得理解浏览器如何加载它们,最终的页面样式是如何呈现的? CSS层叠样式表的关键在于"层叠",会根据选择符的使用而将样式相互叠加或者覆盖. CSS样式表之所有有&q ...

  10. 高级查询MYsql(二) 练习

    一.单词部分 ①exist存在②temp临时的③district区域 ④content内容⑤temporary暂时的 二.预习部分 1.表连接都可以用子查询替换吗 是的 2.检测某列是否存在某个范围可 ...