[ABP教程]第六章 作者:领域层
Web开发教程6 作者:领域层
关于此教程
在这个教程系列中,你将要构建一个基于ABP框架的应用程序 Acme.BookStore。这个应用程序被用于甘丽图书页面机器作者。它将用以下开发技术:
- Entity Framework Core 作为数据提供器
- MVC / Razor Pages 作为UI框架
这个教程全部由下面几个部分构成:
- 第一章:创建服务端
- 第二章:构建书籍列表页面
- 第三章:增删改图书
- 第四章:集成测试
- 第五章:授权
- 第六章:领域层(本教程)
- 第七章:数据库集成
- 第八章:应用层
- 第九章:用户接口
- 第十章:预定作者关系
下载源码
教程根据您的UI和数据库首选项有多个版本。我们准备了一些可供下载的源码组合:
- MVC(Razor Pages)界面和EFCore版本
- Blazor和EF Core版本
- Angular和MongoDB版本
导论
上一教程中,我们使用ABP框架很容易的构建了一些服务:
- 使用CrudAppService 基类替代基础的方法实现,而不是按部就班的创建、读取、更新和删除操作。
- 使用通用的仓储自动化实现数据访问层
对于”作者“部分:
- 我们将手动执行某些操作,以显示需要的时候是如何执行的操作
- 我们将实现一些DDD的最佳实践
作者实体
在*.BookStore.Domain 项目中创建一个 Authors 文件夹 ,并添加添加类Author:
using System;
using JetBrains.Annotations;
using Volo.Abp;
using Volo.Abp.Domain.Entities.Auditing;
namespace Acme.BookStore.Authors
{
public class Author : FullAuditedAggregateRoot<Guid>
{
public string Name { get; private set; }
public DateTime BirthDate { get; set; }
public string ShortBio { get; set; }
private Author()
{
/* This constructor is for deserialization / ORM purpose */
}
internal Author(
Guid id,
[NotNull] string name,
DateTime birthDate,
[CanBeNull] string shortBio = null)
: base(id)
{
SetName(name);
BirthDate = birthDate;
ShortBio = shortBio;
}
internal Author ChangeName([NotNull] string name)
{
SetName(name);
return this;
}
private void SetName([NotNull] string name)
{
Name = Check.NotNullOrWhiteSpace(
name,
nameof(name),
maxLength: AuthorConsts.MaxNameLength
);
}
}
}
- 继承自 FullAuditedAggregateaRoot,能够实现实体软删除(标记删除)等审计属性
- Name属性私有设置保证了无法从外部类修改。这有两个方法设置name名称
- 在构造函数中
- 使用方法ChangeName
- 构造方法和ChangeName方法标记为 internal 内部的,用于强制限制在此域AuthorManager中使用。
- Check类是ABP框架中的工具类,用于检查方法参数(若检查不过将抛错 ArgumentException)
AuthorConsts是一个简单类,位于项目.BookStore.Domain.Shared命名空间Authors 下 :
namespace Acme.BookStore.Authors
{
public static class AuthorConsts
{
public const int MaxNameLength = 64;
}
}
创建此类将被DTOs(Data Transfer Object) 复用
AuthorManager : 领域服务
Author类构造函数和ChangeName 方法是 internal 类型,所以只能被此域中使用。
在Author文件夹中创建类AuthorManager ,并参考下方实现:
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Volo.Abp;
using Volo.Abp.Domain.Services;
namespace Acme.BookStore.Authors
{
public class AuthorManager : DomainService
{
private readonly IAuthorRepository _authorRepository;
public AuthorManager(IAuthorRepository authorRepository)
{
_authorRepository = authorRepository;
}
public async Task<Author> CreateAsync(
[NotNull] string name,
DateTime birthDate,
[CanBeNull] string shortBio = null)
{
Check.NotNullOrWhiteSpace(name, nameof(name));
var existingAuthor = await _authorRepository.FindByNameAsync(name);
if (existingAuthor != null)
{
throw new AuthorAlreadyExistsException(name);
}
return new Author(
GuidGenerator.Create(),
name,
birthDate,
shortBio
);
}
public async Task ChangeNameAsync(
[NotNull] Author author,
[NotNull] string newName)
{
Check.NotNull(author, nameof(author));
Check.NotNullOrWhiteSpace(newName, nameof(newName));
var existingAuthor = await _authorRepository.FindByNameAsync(newName);
if (existingAuthor != null && existingAuthor.Id != author.Id)
{
throw new AuthorAlreadyExistsException(newName);
}
author.ChangeName(newName);
}
}
}
AuthorManger 强制创建作者并以受控方式更改作者的名称,应用程序层后续将使用这些方法。
DDD小贴士: 不要引入域服务方法,除非确实要它们并执行一些核心业务规则。对于这种情况,我们需要此服务能后强制唯一名称的约束。
两种方法都是检查是否存在具有给定名称的作者,并引发一个特俗的业务异常**AuthorAlreadyExistsException ** ,该错误位于项目*.BookStore.Domain中,如下所示:
using Volo.Abp;
namespace Acme.BookStore.Authors
{
public class AuthorAlreadyExistsException : BusinessException
{
public AuthorAlreadyExistsException(string name)
: base(BookStoreDomainErrorCodes.AuthorAlreadyExists)
{
WithData("name", name);
}
}
}
BussinessException 是一个特殊的错误类型。在需要时抛出与域相关的异常是一个好习惯。它将由ABP框架自动处理,并且可以轻松地进行本地化。WithData(...) 方法用于向异常对象提供其他数据,这些数据以后将在本地化消息上使用或用于其他目的。
在项目*.BookStore.Domain.Shared中打开 BookStoreDomainErrorCodes,并作如下的更改:
namespace Acme.BookStore
{
public static class BookStoreDomainErrorCodes
{
public const string AuthorAlreadyExists = "BookStore:00001";
}
}
这有个唯一的字符串,用于表示您的应用程序引发的错误代码,并且可由客户端应用程序处理。对于用户,您可能想对其进行本地化。打开*.BookStore.Domain.Shared项目内的 Localization/BootStore/en.json 并添加以下条目:(译者:中文 zh.json中也需要添加)
"BookStore:00001": "There is already an author with the same name: {name}"
每当您抛出AuthorAlreadyExistsException 时,最终用户将在UI看到此消息。
IAuthorRepository
AuthorManager 注入了IAuthorRepository ,所以我们需要对其定义。在Authors文件夹下创建这个新的接口:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace Acme.BookStore.Authors
{
public interface IAuthorRepository : IRepository<Author, Guid>
{
Task<Author> FindByNameAsync(string name);
Task<List<Author>> GetListAsync(
int skipCount,
int maxResultCount,
string sorting,
string filter = null
);
}
}
- IAuthorRepository扩展自标准的IRepository<Author,Guid> 接口,因此所有标准的仓储方法也可以用于 IAuthorRepository。
- AuthorManager中使用FindByNameAsync 通过名称查询作者。
- GetListAsync将在应用层中使用,以获取排序、筛选后的作者列表,以显示在UI中。
我们将在下一部分中现实此仓储
由于标准仓储中已经是IQueryable,因此这两种方法似乎都是必要的,您可以直接使用他们,而不必定义此类自定义的方法。您说对了,就像在实际应用中一样。但是,对于学习本教程,很有必要解释如何在真正需要的时候创建自定义的仓储方法。
总结
这部分涵盖了书店应用程序作者功能的领域层。主要文件如下:

[ABP教程]第六章 作者:领域层的更多相关文章
- [ABP教程]第七章 作者:数据库集成
Web开发教程7 作者:数据库集成 关于此教程 在这个教程系列中,你将要构建一个基于ABP框架的应用程序 Acme.BookStore.这个应用程序被用于甘丽图书页面机器作者.它将用以下开发技术: E ...
- [ABP教程]第五章 授权
原文档 地址: Web Application Development Tutorial - Part 5: Authorization 关于此教程 在这个教程系列中,您将构建一个基于ABP的Web应 ...
- [ABP教程]第四章 集成测试
Web应用程序开发教程 - 第三章: 集成测试 //[doc-params] { "UI": ["MVC","NG"], "DB& ...
- [ABP教程]第三章 创建、更新和删除图书
Web应用程序开发教程 - 第三章: 创建,更新和删除图书 关于本教程 在本系列教程中, 你将构建一个名为 Acme.BookStore 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以 ...
- Objective-C 基础教程第六章,源文件组织
目录 Object-C 基础教程第六章,源文件组织 0x00:前言 0x01:Xcode创建OC类 0x02:Xcode群组 0x03 Xcode跨文件依赖关系 @class关键字 导入和继承 小结 ...
- ABP入门系列(4)——领域层定义仓储并实现
一.先来介绍下仓储 仓储(Repository): 仓储用来操作数据库进行数据存取.仓储接口在领域层定义,而仓储的实现类应该写在基础设施层. 在ABP中,仓储类要实现IRepository接口,接口定 ...
- ABP入门系列(3)——领域层创建实体
这一节我们主要和领域层打交道.首先我们要对ABP的体系结构以及从模板创建的解决方案进行一一对应.网上有代码生成器去简化我们这一步的任务,但是不建议初学者去使用. 一.首先来看看ABP体系结构 领域层就 ...
- ABP入门系列(2)——领域层创建实体
ABP入门系列目录--学习Abp框架之实操演练 这一节我们主要和领域层打交道.首先我们要对ABP的体系结构以及从模板创建的解决方案进行一一对应.网上有代码生成器去简化我们这一步的任务,但是不建议初学者 ...
- ABP入门系列(3)——领域层定义仓储并实现
ABP入门系列目录--学习Abp框架之实操演练 一.先来介绍下仓储 仓储(Repository): 仓储用来操作数据库进行数据存取.仓储接口在领域层定义,而仓储的实现类应该写在基础设施层. 在ABP中 ...
随机推荐
- Monkey的使用
1.进入adb shell 环境 在Windows环境下进入DOS界面,输入adb shell 注意:adb shell服务使用的端口是5037,如果此端口被其他进程占用时,将不能正常启动adb sh ...
- CNVD漏洞证书(2)
第二张CNVD的原创漏洞证书. 关于证书申请可以看我之前写的这篇博客: https://www.cnblogs.com/Cl0ud/p/12720413.html 继续加油
- kubeadm 的工作原理
kubeadm 的工作原理 作者:张首富 时间:2020-06-04 w x:y18163201 相信使用二进制部署过 k8s 集群的同学们都知道,二进制部署集群太困难了,有点基础的人部署起来还有成功 ...
- Java程序员普遍存在的面试问题以及应对之道(新书第一章节摘录)
其实大多数Java开发确实能胜任日常的开发工作,但不少候选人却无法在面试中打动面试官.因为要在短时间的面试中全面展示自己的实力,这很需要技巧,而从当前大多数Java开发的面试现状来看,会面试的候选人不 ...
- 题解-CF1396C Monster Invaders
题面 CF1396C Monster Invaders 有 \(n\) 层关卡,每层有 \(a_i\) 个小怪(\(1\) 血)和 \(1\) 个老怪(\(2\) 血).有三种武器:\(1\) 武器每 ...
- AcWing 232. 守卫者的挑战
题目链接 大型补档计划. 比较显然的dp 设 \(f[i][j][k]\) 为前 \(i\) 次,擂台上了 \(j\) 次,空闲容量(背包 - 使用的)为 \(k\) 的概率. 不上擂台的转移:\(f ...
- 题解-MtOI2019 幽灵乐团
题面 MtOI2019 幽灵乐团 给定 \(p\),\(Cnt\) 组测试数据,每次给 \(a,b,c\),求 \[\prod_{i=1}^a\prod_{j=1}^b\prod_{k=1}^c\le ...
- nginx根据http请求,将JSON的请求转发到后端,将非JSON请求,转发到前端
nginx.conf worker_processes 1; events { worker_connections 1024; } http { include mime.types; defaul ...
- uni-app微信小程序登录授权
微信小程序授权是非常简单和常用的功能,但为了方便,还是在此记录一下要点: 首先是需要用到一个授权按钮来触发获取用户信息授权: 关键在于 open-type 为 getUserInfo , 然后有个@g ...
- C++异常之四 异常类型的生命周期
异常类型的生命周期 1. throw 基本类型: int.float.char 这三种类型的抛出和函数的返回传值类似,为参数拷贝的值传递. 1 int test_1(int num) throw (i ...