eShopOnWeb

https://www.cnblogs.com/sheng-jie/p/9616675.html

构建现代Web应用

1.引言

eShopOnWeb是基于ASP.NET Core构建,官方创建这样一个示例项目的目的,我想无非以下几点:

推广ASP.NET Core

指导利用ASP.NET Core如何进行架构设计

普及架构设计思想

eShopOnWeb 与另外一个eShopOnContainers互相补充。eShopOnContainers是基于微服务和容器技术的应用程序架构,支持多重部署。而eShopOnWeb相较于它就简单的多,其是基于传统Web应用开发,仅支持单一部署。

本文就简单梳理下自己的所学所得。

2.MPA Or SPA

eShopOnWeb的示例项目中包含两个Web项目,一个是基于MVC创建的MPA多页面应用,一个是基于Razor创建的SPA单页面应用。在此之间我该如何选择呢?

是否需要丰富的交互行为?

是否足够的前端技术积累?

是否主要通过API进行交互?

决策表:Mpa or Spa

  1. 架构设计

    eShopOnWeb中应用了DDD和整洁架构的部分思想,值得了解一下。

3.1 架构原则

关注点分离:简称SOP。在分层架构设计中,关注点分离是核心设计思想,每一层独自负责不同的职责。从架构上讲,可以通过将核心业务与基础设施和用户界面逻辑分离来实现。该原则旨在避免紧耦合,又可确保各个模块独立发展。

封装:封装的是什么?是对象的状态和行为。外部对象无需关注其内部的实现机制。

在类中,通过使用访问修饰符来限制外部的访问来实现封装。 如果外部想要操纵对象的状态,它应该通过定义良好的函数(或属性设置器)来实现,而不是直接访问对象的私有状态。

而不同模块之间通过公开定义良好的接口进行方法调用,来实现封装。以隔离内部的实现机制。通过封装来确保应用程序间不同部分之间的隔离,正确使用封装有助于在应用程序设计中实现松耦合和模块化。

依赖倒置:简称DIP。高层模块不应该依赖低层模块,均应该依赖与抽象;抽象不应该依赖于细节;细节应该依赖于抽象。DIP是构建松耦合应用的关键部分,从而确保应用程序模块化,更易于测试和维护。 通过遵循DIP,可以应用依赖注入。

显式依赖:方法和类应明确指定所需的协作对象(依赖)以确保正常运行。简单来说,对于类而言,提供明确的构造函数(即在构造函数参数中指定该类需要正常工作所需的依赖对象),以便调用者正确传参以正确实例化对象。

单一职责:简称SRP。SRP作为面向对象设计的原则之一,也适用于架构原则。其与SOP类似。它强调对象应该只有一个责任,他们只应该仅有一个改变的理由。换言之,对象应该改变的唯一情况是它的职责需要被更新。遵守该原则,可以编写松耦合和模块化的应用。因为大量的新的行为都应该创建新类去实现,而不是添加到已经存在的类中。添加新类永远比修改一个类安全,因为尚无代码依赖于新类。

在复杂的大型应用中,可以将SRP应用到分层应用的各个层。展现职责应保留在UI项目中,而数据访问职责应保留在基础设施项目中, 业务逻辑应该保留在应用程序核心项目中。如此,即易于测试又可以独立于其他职责持续演化。

该原则的更高级应用,就是微服务了。每个微服务负责独立的职责。

摒弃重复:当出现重复时,应该实施重构。避免当功能改进时,需要同时修改多个部分。

透明持久化:要求可以轻松切换持久化技术,而实现持久化无感知(透明持久化)。

限界上下文:该概念是DDD战略设计的一部分,通过限界上下文来划分领域,作为领域的显式边界,为领域提供上下文语境,保证在领域之内的一些术语、业务相关对象等(通用语言)有一个确切的含义,没有二义性。

3.2. 传统分层架构和整洁架构

传统的分层架构是大家所熟知的三层架构。

传统三层架构

三层架构示例

这样的架构的缺点是:

依赖关系由上至下,不易解耦

不易测试,需要测试数据库

那如何解决三层架构的问题呢,借助【依赖倒置原则】。

DDD的分层架构思想和整洁架构中都是借助【依赖倒置原则】实现层与层之间强依赖关系的解耦。我们来看下整洁架构:

整洁架构——洋葱视图

从该洋葱视图中我们可以看到:

依赖关系由外而内。

处于核心的是实体和接口,不依赖任何其他项。其次是领域服务,仅依赖实体和接口,也相对独立。它们统称为应用程序内核。

应用程序内核之外是基础架构层和展现层,彼此也不一定依赖。

由于应用程序内核不依赖于基础设施层,所以可以很容易编写单元测试。

单元测试位置

由于UI层也不直接依赖于基础设施层,所以我们可以轻松置换基础设施层的实现(比如使用内存数据库),以进行集成测试。

集成测试位置

整洁架构——水平视图

下面我们就来看看eShopOnWeb是如何应用整洁架构的。

  1. 项目结构

    首先我们看下模板架构的项目结构。

    eShopOnWeb Solution

从上图来看其项目结构十分简单,简单的三层,加上三个测试项目。

三层对应:

ApplicationCore:领域层

Infrastructure:基础设施层

Web/WebRazorPages:展现层

DDD使用的传统分层架构

其实该项目架构是DDD经典四层架构,只不过其将应用层集成到展现层中去了。

Web应用服务

4.1 基础设施层

主要提供通用的基础服务和持久化。

Infrastructure

从上图的代码结构我们可以看出:

在Data文件夹下定义了用于持久化的商品目录数据库上下文CatalogContext和泛型仓储EfRepository。

Identity文件夹下定义了身份数据库上下文的。

Logging文件夹定义了一个日志适配器。

Services定义了一个通用的邮件发送基础服务。

4.2. 领域层

领域层是一个项目的核心,用来定义业务规则并实现。其主要用来实体、值对象、聚合、仓储、领域服务和领域事件等。

ApplicationCore

从上图来看:

Entities文件夹下定义了三个聚合根和相关的实体及值对象。

Exceptions文件夹定义了公共的异常。

Interfaces文件夹定义了系列接口。

Services文件夹定义了两个领域服务。

Specifications文件夹下是实现的规约模式。

4.2.1. 聚合根的相关实现

这里我们来看下聚合根的相关定义和实现。

///抽象的聚合根空接口

public interface IAggregateRoot

{ }

//所有的实体基类

public class BaseEntity

{

public int Id { get; set; }

}

//购物车聚会根

public class Basket : BaseEntity, IAggregateRoot

{

public string BuyerId { get; set; }

private readonly List _items = new List();

public IReadOnlyCollection Items => _items.AsReadOnly();

public void AddItem(int catalogItemId, decimal unitPrice, int quantity = 1)
{
if (!Items.Any(i => i.CatalogItemId == catalogItemId))
{
_items.Add(new BasketItem()
{
CatalogItemId = catalogItemId,
Quantity = quantity,
UnitPrice = unitPrice
});
return;
}
var existingItem = Items.FirstOrDefault(i => i.CatalogItemId == catalogItemId);
existingItem.Quantity += quantity;
}

}

从这个实现中我们可以学习到:

通过定义一个空的接口IAggregateRoot,要求所有的聚会根来实现它。

这样做的体现了什么思想:

面向接口编程

约定大于配置

依赖注入

通过定义一个BaseEntity,要求所有的实体继承它。

为什么这样做?

因为实体的特征是具有唯一的身份标识,所以通过在父类来定义Id属性来实现。这也就是层超类型的实现方式。

这样做有什么缺点?

因为所有实体的主键类型不一定都是int类型,所以这个基类型最好改成泛型。

Basket聚合根中将Items定位为Readonly,是为了封装集合,避免子项被其他地方更改。

4.2.2. 仓储的相关实现

仓储是用来透明持久化领域对象的。

public interface IRepository where T : BaseEntity

{

T GetById(int id);

T GetSingleBySpec(ISpecification spec);

IEnumerable ListAll();

IEnumerable List(ISpecification spec);

T Add(T entity);

void Update(T entity);

void Delete(T entity);

}

public interface IAsyncRepository where T : BaseEntity

{

Task GetByIdAsync(int id);

Task<List> ListAllAsync();

Task<List> ListAsync(ISpecification spec);

Task AddAsync(T entity);

Task UpdateAsync(T entity);

Task DeleteAsync(T entity);

}

从以上代码我们可以学到两点:

面向接口编程

职责分离,同步异步接口分离。

4.2.3. 领域服务相关实现

领域服务用来实现业务逻辑的。

public interface IOrderService

{

Task CreateOrderAsync(int basketId, Address shippingAddress);

}

public class OrderService : IOrderService

{

private readonly IAsyncRepository _orderRepository;

private readonly IAsyncRepository _basketRepository;

private readonly IAsyncRepository _itemRepository;

public OrderService(IAsyncRepository basketRepository,

IAsyncRepository itemRepository,

IAsyncRepository orderRepository)

{

_orderRepository = orderRepository;

_basketRepository = basketRepository;

_itemRepository = itemRepository;

}

public async Task CreateOrderAsync(int basketId, Address shippingAddress)

{

var basket = await _basketRepository.GetByIdAsync(basketId);

Guard.Against.NullBasket(basketId, basket);

var items = new List();

foreach (var item in basket.Items)

{

var catalogItem = await _itemRepository.GetByIdAsync(item.CatalogItemId);

var itemOrdered = new CatalogItemOrdered(catalogItem.Id, catalogItem.Name, catalogItem.PictureUri);

var orderItem = new OrderItem(itemOrdered, item.UnitPrice, item.Quantity);

items.Add(orderItem);

}

var order = new Order(basket.BuyerId, shippingAddress, items);

await _orderRepository.AddAsync(order);

}

从以上代码我们可以学习到:

依赖注入

领域服务负责实现真正的业务逻辑

4.3. 应用层和展现层

如上面所阐述,在示例项目中应用层和展现层合二为一。应用层负责展现层与领域层之间的协调,协调业务对象来执行特定的应用程序。

  1. 面向切面编程(AOP)

    eShopOnWeb中也提到了AOP,介绍了在ASP.NET Core中如何应用过滤器来进行AOP,比如:身份验证、模型验证、输出缓存和错误处理等。

    通过过滤器和请求管道执行

执行顺序

  1. 简明DDD

    在eShopOnWeb中,也对DDD的概念,是否使用,何时使用,何时不用,都略有介绍。这里就摘录一二,当然也可以参考我之前的写的DDD理论学习系列。

结论

DDD首先是一个方法论,其注重于领域的合理建模,分为战略建模和战术建模。

如果你不知道你需要它,那么你可能不需要它。

如果你不知道到DDD用于解决什么问题,那么你可能没有遇到这些问题。

DDD倡导者也经常指出其仅适用于大型项目 (>6个月)。

相关概念

DDD是用来对真实世界系统或流程的建模。

使用DDD时,你需要和领域专家紧密合作,领域专家能够解释真实的系统该如何运行。在和领域专家的交流中确定通用语言,其主要用来描述系统中的一些概念。而之所以是通用,是因为不管是开发人员还是领域专家都应能够读懂。而通用语言描述的概念将构成面向对象设计的基础。其体现在代码中的理想状态是代码即设计。

战术

值对象:不可变。

实体:具有唯一标识符可变。

聚会根:在DDD中,用来表示整体与部分的关系,聚合是将相关联的领域对象进行显式分组,来表达整体的概念(也可以是单一的领域对象)。比如将表示订单与订单项的领域对象进行组合,来表达领域中订单这个整体概念。

仓储:一种持久化的模式,用于隔离具体持久化措施,实现透明持久化。

工厂:用于对象的创建。

服务:应用服务和领域服务。领域服务负责业务逻辑,应用服务用于表达业务用例和用户故事。

战略

限界上下文:来为领域提供上下文语境,保证在领域之内的一些术语、业务相关对象等(通用语言)有一个确切的含义,没有二义性。

上下文映射图:限界上下文之间的关联关系。

6. 应用测试

在eShopOnWeb中,还示例了三个测试项目,来指导我们合理的进行测试。

单元测试、集成测试和功能测试的区别

  1. 总结

    总体而言,示例项目简单容易理解,也主要是为了便于推广和演示。但里面涉及的知识点并没有想象的那么简单,从架构原则到设计和应用,每一个环节都包含不简单的知识体系。

所以等什么呢?结合示例项目和官方文档使用 ASP.NET Core 和 Azure 构建新式 Web 应用程序开始学习吧,相信你也会收获颇丰。

eShopOnWeb的更多相关文章

  1. eShopOnWeb 知多少

    1.引言 eShopOnWeb是基于ASP.NET Core构建,官方创建这样一个示例项目的目的,我想无非以下几点: 推广ASP.NET Core 指导利用ASP.NET Core如何进行架构设计 普 ...

  2. asp.net core系列 64 结合eShopOnWeb全面认识领域模型架构

    一.项目分析 在上篇中介绍了什么是"干净架构",DDD符合了这种干净架构的特点,重点描述了DDD架构遵循的依赖倒置原则,使软件达到了低藕合.eShopOnWeb项目是学习DDD领域 ...

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

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

  4. DDD领域驱动设计理论篇 - 学习笔记

    一.Why DDD? 在加入X公司后,开始了ASP.NET Core+Docker+Linux的技术实践,也开始了微服务架构的实践.在微服务的学习中,有一本微软官方出品的<.NET微服务:容器化 ...

  5. ABP框架入门踩坑-添加实体

    添加实体 ABP踩坑记录-目录 这里我以问答模块为例,记录一下我在创建实体类过程中碰到的一些坑. 审计属性 具体什么是审计属性我这里就不再介绍了,大家可以参考官方文档. 这里我是通过继承定义好的基类来 ...

  6. 一系列令人敬畏的.NET核心库,工具,框架和软件

    内容 一般 框架,库和工具 API 应用框架 应用模板 身份验证和授权 Blockchain 博特 构建自动化 捆绑和缩小 高速缓存 CMS 代码分析和指标 压缩 编译器,管道工和语言 加密 数据库 ...

  7. .NET Conf 2019日程(北京时间)

    一年一度的 .NET Conf马上就要开始了,我将日程简易的翻译了一下,并且时间全部转换为北京时间,以方便国内.NETer. 日程 第1天 (北京时间9月24日) .NET Conf 2019 基调 ...

  8. Unable to connect to web server 'IIS Express'(无法连接到Web服务器“IIS Express”)的解决方式-Jexus Manager

    在运行微软示例工程eShopOnWeb时候, 在经过一段时间再运行启动报Error "Unable to connect to web server 'IIS Express'"  ...

  9. 使用事件和 CQRS 重写 CRUD 系统

    使用事件和 CQRS 重写 CRUD 系统 https://msdn.microsoft.com/zh-cn/magazine/mt790196.aspx https://github.com/mem ...

随机推荐

  1. eclipse(1)----ubuntu下的安装与配置

    eclipse的安装与配置 1.eclipse官网下载,最新版本eclipse-jee-oxygen-3-linux-gtk-x86_64.tar.gz 2.tar包存在~/Download/下,解压 ...

  2. Linux基本命令 网络命令

    概述 网络和监控命令类似于这些: hostname, ping, ifconfig, iwconfig, netstat, nslookup, traceroute, finger, telnet, ...

  3. qt的udp的初步使用(转)

    该程序实现的功能是:局域网内,每个用户登录到聊天软件,则软件界面的右端可以显示在线用户列表,分别显示的是用户名,主机名,ip地址.软件左边那大块是聊天内容显示界面,这里局域网相当于qq中的qq群,即群 ...

  4. DNS 安装配置

    DNS 安装配置 实验环境 一台主机:Linux Centos 6.5 32位 安装包: DNS服务:bind.i686 DNS测试工具:bind-utils DNS 服务安装 1.yum安装DNS服 ...

  5. eval 加密 js,把js代码重新编续成新的代码,然后eval运行

    eval( function(p, a, c, k, e, r) { e = function(c) { return c.toString(a) //35 }; if (!''.replace(/^ ...

  6. spark学习2(hive0.13安装)

    第一步:hive安装 通过WinSCP将apache-hive-0.13.1-bin.tar.gz上传到/usr/hive/目录下 [root@spark1 hive]# chmod u+x apac ...

  7. <转载>Win x86-64 - Download & execute (Generator)

    #Title: Obfuscated Shellcode Windows x86/x64 Download And Execute [Use PowerShell] - Generator #leng ...

  8. JavaScript -- 操作input CheckBox 全选框

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. Virtio SCSI设备介绍

    Qemu的存储栈 在KVM虚拟化环境中,当客户机的内核存储系统像在物理机上一样通过页缓存.文件系统.通用块设备层运行到实际设备驱动时,这时驱动对设备寄存器的访问会触发CPU从客户机代码切换到物理机内的 ...

  10. keystone cache

    http://docs.openstack.org/juno/config-reference/content/section_keystone.conf.html http://docs.opens ...