介绍

本章节对 ABP 框架进行一个简单的介绍,摘自ABP官方,后面会在使用过程中对各个知识点进行细致的讲解。

领域驱动设计

领域驱动设计(简称:DDD)是一种针对复杂需求的软件开发方法。将软件实现与不断发展的模型联系起来,专注于核心领域逻辑,而不是基础设施细节。DDD适用于复杂领域和大规模应用,而不是简单的CRUD应用。它有助于建立一个灵活、模块化和可维护的代码库。

一个基于领域驱动的解决方案有四个基本层:

领域层:实现领域(或系统)中的用例独立的核心业务逻辑。

应用层:基于领域的应用程序用例,应用程序用例可以看作是用户界面上的用户交互。

展示层:包含应用程序UI元素(页面、组件等)。

基础层:支持层,通过对第三方类库的调用或系统的抽象和集成来实现对其他层的支持。

核心构件

DDD主要关注领域层和应用层,展示层和基础层被看作是细节,业务层不应该依赖于它们,但这并不意味着展示层和基础层不重要,它们也非常重要。展示层中的UI框架和基础层中的数据提供程序有他们自己的实现规则和最佳实践,需要了解和应用。然而,这些并不在DDD的主题中,我们重点来看领域层和应用层的基本构件。

领域层构件

实体(Entity):一个实体是一个对象,该对象包含自己的属性和方法,属性用于存储数据和描述状态;方法结合属性实现业务逻辑。一个实体使用唯一标识(ID)来表示,两个实体对象ID不同则是为不同的实体。

值对象(Value Object):值对象是另一种类型的领域对象,该对象由其属性而不是唯一ID来标识。意思是说,只有全部属性相同才会被认为是同一个对象。值对象通常被实现为不可变的,而且大多比实体简单得多。

聚合和聚合根:聚合根是一个特定类型的实体,具有额外的职责。聚合是以聚合根为中心绑定在一起的一组对象,对象包括实体和值对象。

仓储(接口):仓储是一个类似集合的接口,被领域层和应用层用来访问数据持久化系统(数据库)。它将数据库的复杂性从业务代码中隐藏起来。领域层包含仓储接口。

领域服务:领域服务是无状态服务,实现核心领域业务规则。用于实现依赖于多个聚合(实体)或外部服务的领域逻辑。

规约:用于为实体和其他业务对象定义可命名的、可重用的和可组合的过滤器。

领域事件:领域事件是一种低耦合的通知方式,当一个特定的领域事件发生时,会通知其他服务。

应用层构件

应用服务:应用服务是无状态服务,实现应用程序用例。一个应用服务通常获取和返回数据传输对象(DTOs),用于展示层。调用领域对象来实现用例。一个用例通常被认为是一个工作单元。

数据传输对象(DTO):DTO是简单对象,不包含任何业务逻辑,只用于在应用层和展示层传递数据。

工作单元:一个工作单元是一个原子工作。在工作单元中的所有操作统一提交,要么全部成功,失败则全部回滚。

ABP项目分层解析

领域层

领域层拆分为两个项目:

Bcvp.Blog.Core.Domain:领域层,该项目包含所有领域层构件,比如:实体、值对象、领域服务、规约、仓储接口等。

Bcvp.Blog.Core.Domain.Shared:领域共享层,包含属于领域层,但是与其他层共享的类型。举个例子:定义的常量和枚举,既在领域对象中使用,也要在其他层中使用,放在该项目中。

应用层

应用层拆分为两个项目:

Bcvp.Blog.Core.Application.Contracts:应用契约层,包含应用服务接口和数据传输对象(用于接口),该项目被应用程序客户端引用,比如:WEB项目、API客户端项目。

Bcvp.Blog.Core.Application:应用层,实现在 Contracts 项目中定义的接口。

展示层

Bcvp.Blog.Core.HttpApi.Host 项目作为一个独立的端点提供 HTTP API 服务,供客户端调用。

远程服务层

Bcvp.Blog.Core.HttpApi:远程服务层,该项目用于定义 HTTP APIs,通常包含 MVC Controller 及相关的模型。

Bcvp.Blog.Core.HttpApi.Client:远程服务代理层,客户端应用程序引用该项目,将直接通过依赖注入使用远程应用服务,该项目基于ABP Framework动态C#客户端API代理系统实现。在C#项目中需要调用HTTP APIs时,会非常有用。

基础层

实现DDD时,可以使用一个基础层项目来实现所有的集成和抽象,当然也可以为不同依赖创建不同项目。

建议折中处理,为核心基础依赖创建单独项目,比如:Entity Framework Core;另外创建一个公共基础项目存放其他基础设施。

启动模板中包含两个项目对 Entity Framework Core 进行集成:

Bcvp.Blog.Core.EntityFrameworkCore:EF Core核心基础依赖项目,包含:数据上下文、数据库映射、EF Core仓储实现等。

其他项目

还有一个项目 Bcvp.Blog.Core.DbMigrator,一个简单的控制台应用程序,当你执行它时,会迁移数据库结构并初始化种子数据。这是一个有用的实用程序,可以在开发和生产环境中使用它。

项目依赖关系

Domain.Shared 其他项⽬直接或间接引⽤,项⽬中定义的类型在所有项⽬中共享。

Domain 只引⽤ Domain.Shared ,⽐如:在 Domain.Shared 中定义的 IssuType 枚举类型需要 在 Domain 项⽬中 Issue 实体中⽤到。

Application.Contracts 依赖 Domain.Shared ,这样我们可以在 DTOs 中使⽤这些共享类型。 ⽐如: CreateIssueDto 中可以直接使⽤ IssueType 枚举。

Application 依赖 Application.Contracts ,因为 Application 实现 Application.Contracts 中定义的服务接⼝和使⽤ DTO 对象。同时,引⽤ Domain 项⽬,在应 ⽤服务中使⽤仓储接⼝或领域对象。

EntiryFrameworkCore 依赖 Domain ,映射 Domain 对象(实体和值类型)到数据库表 (ORM)并实现在 Domain 中定义的仓储接⼝。

HttpApi 依赖 Application.Contract ,在控制器在内部对 应⽤服务接⼝ 进⾏依赖注⼊。

HttpApi.Client 依赖 Application.Contract 消费应⽤服务 Web 依赖 HttpApi ,发布⾥⾯定义的 HTTP APIs 。另外,通过这种⽅式,它间接地依赖于 Application.Contracts 项⽬,可以在⻚⾯/组件中使⽤应⽤服务

DDD通用原则

在正式开始之前我们在梳理一下DDD的通用原则。

数据库(Database Provider / ORM)独⽴性原则

领域层和应⽤层不知道项⽬中使⽤的 ORM 和 Database Provider。只依赖于仓储接⼝,并且仓储接⼝ 不适合使⽤⽤任何 ORM 特殊对象

这⼀原则的主要原因是:

  1. 使领域层和应⽤层与基础层独⽴,因为基础层将来可能更改,或者你可能需要⽀持其他类型数据库。

  2. 使领域和应⽤聚焦在业务代码上,通过将基础设施实现细节隐藏于仓储之后,使您的领域和应⽤服 务专注于业务代码。

  3. 易于⾃动化测试,因为可以通过仓储接⼝模拟仓储数据。

关于数据库独⽴性原则的讨论

假设你当前使⽤ Entity Framework Core 操作关系型数据库,后期希望切换为 MongoDB,这就决定你不能使⽤ EF Core 中独 有功能,因为在MongoDB中不被⽀持.

举个例⼦: 

    不能使⽤更改跟踪(Change Tacking),因为 MongoDB 不⽀持。所以,需要显式更改实体。 

    不能在实体中使⽤导航属性(Navigation Properties) 或集合关联其他聚合,因为可能在⽂档数 据库中不⽀持。

那么如何解决实体关联的问题?记住规则:仅通过Id引⽤其他聚合

如果你认为这些功能对你很重要,⽽且你永远不会弃⽤ EF Core,我们认为这个原则是可以有弹性的, 但是我们仍然建议使⽤仓储模式来隐藏基础设施的实现细节

ABP Framework 为仓储接⼝ IRepository 提供获取 IQueryable 对象的扩展⽅法 GetQueryableAsync() ,使我们在使⽤仓储时可以直接使⽤标准LINQ扩展⽅法。

展示技术⽆关性原则

展示层技术(UI框架)是应⽤程序中变化最多的部分,将领域层和应⽤层设计成完全不知道展示层技术的框架⾮常重要的。

这⼀原则相对容易实现,⽽ABP的启动模板使其更加容易实现,选择不同UI框架⾃动⽣成对应的启动模板项⽬。

在某些场景下,你可能需要在应⽤层和展示层使⽤相同的逻辑。举例,你可能需要在两个层中进⾏验证和授权。在UI层检测是为了提⾼⽤户体验,在应⽤层和领域层是出安全和数据有效性考虑。这是⾮常正常和必要的。

聚焦状态变化,⽽不是性能优化

DDD聚焦领域对象如何变化和如何交互;如何创建实体和改变属性,并且保持数据的完整性、有效性; 如何创建⽅法,实现业务规则。

DDD没有考虑报表和⼤规模查询等需要⾼性能的业务场景,如果你的应⽤程序中没有花哨的仪表盘或报表功能,谁会去考虑呢?意思是我们需要⾃⼰考虑性能问题。

性能优化或技术选型,只要不影响到业务逻辑,可以⾃由使⽤ SQL Server 全部功能。

结语

本节知识点:

  • 1.讲解Abp和DDD的分层架构介绍
  • 2.很重要的知识点DDD聚焦状态变化而非性能优化

联系作者:加群:867095512 @MrChuJiu

Abp vNext 基础篇丨分层架构的更多相关文章

  1. 五、Abp vNext 基础篇丨博客聚合功能

    介绍 业务篇章先从客户端开始写,另外补充一下我给项目起名的时候没多想起的太随意了,结果后面有些地方命名冲突了需要通过手动using不过问题不大. 开工 应用层 根据第三章分层架构里面讲到的现在我们模型 ...

  2. 六、Abp vNext 基础篇丨文章聚合功能上

    介绍 9月开篇讲,前面几章群里已经有几个小伙伴跟着做了一遍了,遇到的问题和疑惑也都在群里反馈和解决好了,9月咱们保持保持更新.争取10月份更新完基础篇. 另外番外篇属于 我在abp群里和日常开发的问题 ...

  3. 十一、Abp vNext 基础篇丨测试

    前言 祝大家国庆快乐,本来想国庆之前更新完的,结果没写完,今天把剩下的代码补了一下总算ok了. 本章节也是我们后端日常开发中最重要的一步就是测试,我们经常听到的单元测试.集成测试.UI测试.系统测试, ...

  4. 七、Abp vNext 基础篇丨文章聚合功能下

    介绍 不好意思这篇文章应该早点更新的,这几天在忙CICD的东西没顾得上,等后面整好了CICD我也发2篇文章讲讲,咱们进入正题,这一章来补全剩下的 2个接口和将文章聚合进行完善. 开工 上一章大部分业务 ...

  5. Abp vNext 基础篇丨领域构建

    介绍 我们将通过例⼦介绍和解释⼀些显式规则.在实现领域驱动设计时,应该遵循这些规则并将其应⽤到解决⽅案中. 领域划分 首先我们先对比下Blog.Core和本次重构设计上的偏差,可以看到多了一个博客管理 ...

  6. 八、Abp vNext 基础篇丨标签聚合功能

    介绍 本章节先来把上一章漏掉的上传文件处理下,然后实现Tag功能. 上传文件 上传文件其实不含在任何一个聚合中,它属于一个独立的辅助性功能,先把抽象接口定义一下,在Bcvp.Blog.Core.App ...

  7. 十、Abp vNext 基础篇丨权限

    介绍 本章节来把接口的权限加一下 权限配置和使用 官方地址:https://docs.abp.io/en/abp/latest/Authorization 下面这种代码可能我们日常开发都写过,ASP. ...

  8. 九、Abp vNext 基础篇丨评论聚合功能

    介绍 评论本来是要放到标签里面去讲的,但是因为上一章东西有点多了,我就没放进去,这一章单独拿出来,内容不多大家自己写写就可以,也算是对前面讲解的一个小练习吧. 相关注释我也加在代码上面了,大家看看代码 ...

  9. Abp vNext 番外篇-疑难杂症丨浅谈扩展属性与多用户设计

    说明 Abp vNext基础篇的文章还差一个单元测试模块就基本上完成了我争取10.1放假之前给大家赶稿出来,后面我们会开始进阶篇,开始拆一些东西,具体要做的事我会单独开一个文章来讲 缘起 本篇文章缘起 ...

随机推荐

  1. SpringBoot集成logback后访问日志端点

    问题描述 使用SpringBootAdmin(sba)监控Springboot服务时,配置了logback日志框架,按天滚动生成日志,此时在sba的日志监控页面出现404,如下图所示: 解决方案 查看 ...

  2. Mybatis学习(1)开发环境搭建

    什么是mybatis MyBatis是支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索.MyBatis使用简单的XML ...

  3. Spring WebFlux快速上手——响应式Spring的道法术器

    https://blog.csdn.net/get_set/article/details/79480233

  4. 剑指 Offer 12. 矩阵中的路径

    题目描述 是一道很常见的深搜题目,不过里面要考虑一些边界问题,比如走过的路径是不能再次走入的,所以我这里我自己的 代码想到是利用一个新的二维的数组,记录走过的路径,不过题解的直接将原二维数组中的路径隐 ...

  5. FreeRTOS基本概念

    1.在FreeRTOS中,使用的数据类型虽然都是标准C里面的数据类型,但是针对不同的处理器,对标准C的数据类型又进行了重新定义. 2.链表由节点组成,节点与节点之间首尾相连,节点包含用于指向后一个节点 ...

  6. hadoop源码_hdfs启动流程_2_DataNode

    执行start-dfs.sh脚本后,集群是如何启动的? 本文阅读并注释了start-dfs脚本,以及datanode的启动主要流程流程源码. DataNode 启动流程 脚本代码分析 start-df ...

  7. mybatis复杂映射

    1. 类型名对应 当实体类与表中字段完全一致时,mapper文件里返回类型用resultType,否则要用resultMap,并且建立resultMap映射 package com.rf.domain ...

  8. JMeter之BeanShell常用内置对象

    一.什么是Bean Shell BeanShell是一种完全符合Java语法规范的脚本语言,并且又拥有自己的一些语法和方法; BeanShell是一种松散类型的脚本语言(这点和JS类似); BeanS ...

  9. C语言:赋值

    #include <stdio.h> //=赋值运算符 //具有方向性,只能将赋值号右边的表达式的值给左边的变量 //赋值语句具有计算功能,赋值号右边可以是常量,变量或表达式都可以 //赋 ...

  10. python pandas inplace参数

    '''pandas 中 inplace 参数在很多函数中都会有,它的作用是:是否在原对象基础上进行修改 ​ inplace = True:不创建新的对象,直接对原始对象进行修改: ​ inplace ...