From 41cd83240f09bda635b2a00e56945d77f735db2d Mon Sep 17 00:00:00 2001 From: hpstory <33348162+hpstory@users.noreply.github.com> Date: Fri, 11 Mar 2022 21:22:44 +0800 Subject: [PATCH 1/4] Update abp/docs/zh-Hans/Modules/Background-Jobs.md --- docs/zh-Hans/Modules/Background-Jobs.md | 56 ++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/docs/zh-Hans/Modules/Background-Jobs.md b/docs/zh-Hans/Modules/Background-Jobs.md index b183febd80..87c57acde7 100644 --- a/docs/zh-Hans/Modules/Background-Jobs.md +++ b/docs/zh-Hans/Modules/Background-Jobs.md @@ -1,3 +1,55 @@ -# Background Jobs Module +# 后台作业模块 -待添加 \ No newline at end of file +后台作业模块实现了 `IBackgroundJobStore` 接口,并且可以使用ABP框架的默认后台作业管理。如果你不想使用这个模块,那么你需要自己实现 `IBackgroundJobStore` 接口。 + +> 本文档仅介绍后台作业模块,该模块将后台作业持久化到数据库。有关后台作业系统的更多信息,请参阅[后台作业](../Background-Jobs.md)文档。 + +## 如何使用 + +当您使用ABP框架[创建一个新的解决方案](https://abp.io/get-started)时,这个模块是(作为NuGet/NPM包)预先安装的。您可以继续将其作为软件包使用并轻松获取更新,也可以将其源代码包含到解决方案中(请参阅 `get-source` [CLI](../CLI.md)命令)以开发自定义模块。 + +### 源代码 + +此模块的源代码可在[此处](https://github.com/abpframework/abp/tree/dev/modules/background-jobs)访问。源代码是由[麻省理工学院](https://choosealicense.com/licenses/mit/)授权的,所以你可以自由使用和定制它。 + +## 内部结构 + +### 领域层 + +#### 聚合 + +- `BackgroundJobRecord` (聚合根): 表示后台工作记录. + +#### 仓储 + +为该模块定义了以下自定义仓储: + +- `IBackgroundJobRepository` + +### 数据库提供程序 + +#### 通用 + +##### 表/集合的前缀与架构 + +默认情况下,所有表/集合都使用 `Abp` 前缀。如果需要更改表前缀或设置架构名称(如果数据库提供程序支持),请在 `BackgroundJobsDbProperties` 类上设置静态属性。 + +##### 连接字符串 + +此模块使用 `AbpBackgroundJobs` 作为连接字符串名称。如果不使用此名称定义连接字符串,它将返回 `Default` 连接字符串。有关详细信息,请参阅[连接字符串](https://docs.abp.io/en/abp/latest/Connection-Strings)文档。 + +#### Entity Framework Core + +##### 表 + +- **AbpBackgroundJobs** + +#### MongoDB + +##### 集合 + +- **AbpBackgroundJobs** + +## 另请参阅 + +* [后台作业系统](../Background-Jobs.md) From 4c67d1a1dc8a63108d1d21d7def67effbe1f12b7 Mon Sep 17 00:00:00 2001 From: hpstory <33348162+hpstory@users.noreply.github.com> Date: Fri, 11 Mar 2022 22:45:11 +0800 Subject: [PATCH 2/4] Update Specification.md --- docs/zh-Hans/Specifications.md | 258 ++++++++++++++++++++++++++++++++- 1 file changed, 256 insertions(+), 2 deletions(-) diff --git a/docs/zh-Hans/Specifications.md b/docs/zh-Hans/Specifications.md index f927c80255..bc5c67101b 100644 --- a/docs/zh-Hans/Specifications.md +++ b/docs/zh-Hans/Specifications.md @@ -1,3 +1,257 @@ -## 规约 +## 规范 -TODO.. \ No newline at end of file +规范模式用于为实体和其他业务对象定义 **命名、可复用、可组合和可测试的过滤器** 。 + +> 规范是领域层的一部分。 + +## 安装 + +> 这个包 **已经安装** 在启动模板中。所以,大多数时候你不需要手动去安装。 + +添加 [Volo.Abp.Specifications](https://abp.io/package-detail/Volo.Abp.Specifications) 包到你的项目. 如果当前文件夹是您的项目的根目录(`.csproj`)时,您可以在命令行终端中使用 [ABP CLI](CLI.md) *add package* 命令: + +````bash +abp add-package Volo.Abp.Specifications +```` + +## 定义规范 + +假设您定义了如下的顾客实体: + +````csharp +using System; +using Volo.Abp.Domain.Entities; + +namespace MyProject +{ + public class Customer : AggregateRoot + { + public string Name { get; set; } + + public byte Age { get; set; } + + public long Balance { get; set; } + + public string Location { get; set; } + } +} +```` + +您可以创建一个由 `Specification` 派生的新规范类。 + +**例如:规定选择一个18岁以上的顾客** + +````csharp +using System; +using System.Linq.Expressions; +using Volo.Abp.Specifications; + +namespace MyProject +{ + public class Age18PlusCustomerSpecification : Specification + { + public override Expression> ToExpression() + { + return c => c.Age >= 18; + } + } +} +```` + +您只需通过定义一个lambda[表达式](https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/operators/lambda-expressions)来定义规范。 + +> 您也可以直接实现`ISpecification`接口,但是基类`Specification`做了大量简化。 + +## 使用规范 + +这里有两种常见的规范用例。 + +### IsSatisfiedBy + +`IsSatisfiedBy` 方法可以用于检查单个对象是否满足规范。 + +**例如:如果顾客不满足年龄规定,则抛出异常** + +````csharp +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace MyProject +{ + public class CustomerService : ITransientDependency + { + public async Task BuyAlcohol(Customer customer) + { + if (!new Age18PlusCustomerSpecification().IsSatisfiedBy(customer)) + { + throw new Exception( + "这位顾客不满足年龄规定!" + ); + } + + //TODO... + } + } +} +```` + +### ToExpression & Repositories + +`ToExpression()` 方法可用于将规范转化为表达式。通过这种方式,您可以使用规范在**数据库查询时过滤实体**。 + +````csharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Domain.Services; + +namespace MyProject +{ + public class CustomerManager : DomainService, ITransientDependency + { + private readonly IRepository _customerRepository; + + public CustomerManager(IRepository customerRepository) + { + _customerRepository = customerRepository; + } + + public async Task> GetCustomersCanBuyAlcohol() + { + var queryable = await _customerRepository.GetQueryableAsync(); + var query = queryable.Where( + new Age18PlusCustomerSpecification().ToExpression() + ); + + return await AsyncExecuter.ToListAsync(query); + } + } +} +```` + +> 规范被正确地转换为SQL/数据库查询语句,并且在DBMS端高效执行。虽然它与规范无关,但如果您想了解有关 `AsyncExecuter` 的更多信息,请参阅[仓储](Repositories.md)文档。 + +实际上,没有必要使用 `ToExpression()` 方法,因为规范会自动转换为表达式。这也会起作用: + +````csharp +var queryable = await _customerRepository.GetQueryableAsync(); +var query = queryable.Where( + new Age18PlusCustomerSpecification() +); +```` + +## 编写规范 + +规范有一个强大的功能是,它们可以与`And`、`Or`、`Not`以及`AndNot`扩展方法组合使用。 + +假设您有另一个规范,定义如下: + +```csharp +using System; +using System.Linq.Expressions; +using Volo.Abp.Specifications; + +namespace MyProject +{ + public class PremiumCustomerSpecification : Specification + { + public override Expression> ToExpression() + { + return (customer) => (customer.Balance >= 100000); + } + } +} +``` + +您可以将 `PremiumCustomerSpecification` 和 `Age18PlusCustomerSpecification` 结合起来,查询优质成人顾客的数量,如下所示: + +````csharp +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Domain.Services; +using Volo.Abp.Specifications; + +namespace MyProject +{ + public class CustomerManager : DomainService, ITransientDependency + { + private readonly IRepository _customerRepository; + + public CustomerManager(IRepository customerRepository) + { + _customerRepository = customerRepository; + } + + public async Task GetAdultPremiumCustomerCountAsync() + { + return await _customerRepository.CountAsync( + new Age18PlusCustomerSpecification() + .And(new PremiumCustomerSpecification()).ToExpression() + ); + } + } +} +```` + +如果你想让这个组合成为一个可复用的规范,你可以创建这样一个组合的规范类,它派生自`AndSpecification`: + +````csharp +using Volo.Abp.Specifications; + +namespace MyProject +{ + public class AdultPremiumCustomerSpecification : AndSpecification + { + public AdultPremiumCustomerSpecification() + : base(new Age18PlusCustomerSpecification(), + new PremiumCustomerSpecification()) + { + } + } +} +```` + +现在,您就可以向下面一样重新编写 `GetAdultPremiumCustomerCountAsync` 方法: + +````csharp +public async Task GetAdultPremiumCustomerCountAsync() +{ + return await _customerRepository.CountAsync( + new AdultPremiumCustomerSpecification() + ); +} +```` + +> 你可以从这些例子中看到规范的强大之处。如果您之后想要更改 `PremiumCustomerSpecification` ,比如将余额从 `100.000` 修改为 `200.000` ,所有查询语句和合并的规范都将受到本次更改的影响。这是减少代码重复的好方法! + +## 讨论 + +虽然规范模式通常与C#的lambda表达式相比较,算是一种更老的方式。一些开发人员可能认为不再需要它,我们可以直接将表达式传入到仓储或领域服务中,如下所示: + +````csharp +var count = await _customerRepository.CountAsync(c => c.Balance > 100000 && c.Age => 18); +```` + +自从ABP的[仓储](Repositories.md)支持表达式,这是一个完全有效的用法。您不必在应用程序中定义或使用任何规范,可以直接使用表达式。 + +所以,规范的意义是什么?为什么或者应该在什么时候考虑去使用它? + +### 何时使用? + +使用规范的一些好处: + +- **可复用**:假设您在代码库的许多地方都需要用到优质顾客过滤器。如果使用表达式而不创建规范,那么如果以后更改“优质顾客”的定义会发生什么?假设您想将最低余额从100000美元更改为250000美元,并添加另一个条件,成为顾客超过3年。如果使用了规范,只需修改一个类。如果在任何其他地方重复(复制/粘贴)相同的表达式,则需要更改所有的表达式。 +- **可组合**:可以组合多个规范来创建新规范。这是另一种可复用性。 +- **命名**:`PremiumCustomerSpecification` 更好地解释了为什么使用规范,而不是复杂的表达式。因此,如果在您的业务中使用了一个有意义的表达式,请考虑使用规范。 +- **可测试**:规范是一个单独(且易于)测试的对象。 + +### 什么时侯不要使用? + +- **没有业务含义的表达式**:不要对与业务无关的表达式和操作使用规范。 +- **报表**:如果只是创建报表,不要创建规范,而是直接使用 `IQueryable` 和LINQ表达式。您甚至可以使用普通SQL、视图或其他工具生成报表。DDD不关心报表,因此从性能角度来看,查询底层数据存储的方式可能很重要。 From 1263bf51e6a82494d77926513bdfc4dca913640e Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Sat, 12 Mar 2022 13:27:44 +0800 Subject: [PATCH 3/4] Update Background-Jobs.md --- docs/zh-Hans/Modules/Background-Jobs.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/zh-Hans/Modules/Background-Jobs.md b/docs/zh-Hans/Modules/Background-Jobs.md index 87c57acde7..fe021f1a56 100644 --- a/docs/zh-Hans/Modules/Background-Jobs.md +++ b/docs/zh-Hans/Modules/Background-Jobs.md @@ -1,16 +1,16 @@ # 后台作业模块 -后台作业模块实现了 `IBackgroundJobStore` 接口,并且可以使用ABP框架的默认后台作业管理。如果你不想使用这个模块,那么你需要自己实现 `IBackgroundJobStore` 接口。 +后台作业模块实现了 `IBackgroundJobStore` 接口,并且可以使用ABP框架的默认后台作业管理.如果你不想使用这个模块,那么你需要自己实现 `IBackgroundJobStore` 接口. -> 本文档仅介绍后台作业模块,该模块将后台作业持久化到数据库。有关后台作业系统的更多信息,请参阅[后台作业](../Background-Jobs.md)文档。 +> 本文档仅介绍后台作业模块,该模块将后台作业持久化到数据库.有关后台作业系统的更多信息,请参阅[后台作业](../Background-Jobs.md)文档. ## 如何使用 -当您使用ABP框架[创建一个新的解决方案](https://abp.io/get-started)时,这个模块是(作为NuGet/NPM包)预先安装的。您可以继续将其作为软件包使用并轻松获取更新,也可以将其源代码包含到解决方案中(请参阅 `get-source` [CLI](../CLI.md)命令)以开发自定义模块。 +当你使用ABP框架[创建一个新的解决方案](https://abp.io/get-started)时,这个模块是(作为NuGet/NPM包)预先安装的.你可以继续将其作为软件包使用并轻松获取更新,也可以将其源代码包含到解决方案中(请参阅 `get-source` [CLI](../CLI.md)命令)以开发自定义模块. ### 源代码 -此模块的源代码可在[此处](https://github.com/abpframework/abp/tree/dev/modules/background-jobs)访问。源代码是由[麻省理工学院](https://choosealicense.com/licenses/mit/)授权的,所以你可以自由使用和定制它。 +此模块的源代码可在[此处](https://github.com/abpframework/abp/tree/dev/modules/background-jobs)访问.源代码是由[MIT](https://choosealicense.com/licenses/mit/)授权的,所以你可以自由使用和定制它. ## 内部结构 @@ -32,11 +32,11 @@ ##### 表/集合的前缀与架构 -默认情况下,所有表/集合都使用 `Abp` 前缀。如果需要更改表前缀或设置架构名称(如果数据库提供程序支持),请在 `BackgroundJobsDbProperties` 类上设置静态属性。 +默认情况下,所有表/集合都使用 `Abp` 前缀.如果需要更改表前缀或设置架构名称(如果数据库提供程序支持),请在 `BackgroundJobsDbProperties` 类上设置静态属性. ##### 连接字符串 -此模块使用 `AbpBackgroundJobs` 作为连接字符串名称。如果不使用此名称定义连接字符串,它将返回 `Default` 连接字符串。有关详细信息,请参阅[连接字符串](https://docs.abp.io/en/abp/latest/Connection-Strings)文档。 +此模块使用 `AbpBackgroundJobs` 作为连接字符串名称.如果不使用此名称定义连接字符串,它将返回 `Default` 连接字符串.有关详细信息,请参阅[连接字符串](https://docs.abp.io/en/abp/latest/Connection-Strings)文档. #### Entity Framework Core From 00014eee2f58f50d5b3508cffe5b42ede6694e69 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Sat, 12 Mar 2022 13:30:19 +0800 Subject: [PATCH 4/4] Update Specifications.md --- docs/zh-Hans/Specifications.md | 70 +++++++++++++++++----------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/docs/zh-Hans/Specifications.md b/docs/zh-Hans/Specifications.md index bc5c67101b..40a473dbc0 100644 --- a/docs/zh-Hans/Specifications.md +++ b/docs/zh-Hans/Specifications.md @@ -1,22 +1,22 @@ -## 规范 +## 规约 -规范模式用于为实体和其他业务对象定义 **命名、可复用、可组合和可测试的过滤器** 。 +规约模式用于为实体和其他业务对象定义 **命名、可复用、可组合和可测试的过滤器** . -> 规范是领域层的一部分。 +> 规约是领域层的一部分. ## 安装 -> 这个包 **已经安装** 在启动模板中。所以,大多数时候你不需要手动去安装。 +> 这个包 **已经安装** 在启动模板中.所以,大多数时候你不需要手动去安装. -添加 [Volo.Abp.Specifications](https://abp.io/package-detail/Volo.Abp.Specifications) 包到你的项目. 如果当前文件夹是您的项目的根目录(`.csproj`)时,您可以在命令行终端中使用 [ABP CLI](CLI.md) *add package* 命令: +添加 [Volo.Abp.Specifications](https://abp.io/package-detail/Volo.Abp.Specifications) 包到你的项目. 如果当前文件夹是你的项目的根目录(`.csproj`)时,你可以在命令行终端中使用 [ABP CLI](CLI.md) *add package* 命令: ````bash abp add-package Volo.Abp.Specifications ```` -## 定义规范 +## 定义规约 -假设您定义了如下的顾客实体: +假设你定义了如下的顾客实体: ````csharp using System; @@ -37,9 +37,9 @@ namespace MyProject } ```` -您可以创建一个由 `Specification` 派生的新规范类。 +你可以创建一个由 `Specification` 派生的新规约类. -**例如:规定选择一个18岁以上的顾客** +**例如:规定选择一个18岁以上的顾客** ````csharp using System; @@ -58,19 +58,19 @@ namespace MyProject } ```` -您只需通过定义一个lambda[表达式](https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/operators/lambda-expressions)来定义规范。 +你只需通过定义一个lambda[表达式](https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/operators/lambda-expressions)来定义规约. -> 您也可以直接实现`ISpecification`接口,但是基类`Specification`做了大量简化。 +> 你也可以直接实现`ISpecification`接口,但是基类`Specification`做了大量简化. -## 使用规范 +## 使用规约 -这里有两种常见的规范用例。 +这里有两种常见的规约用例. ### IsSatisfiedBy -`IsSatisfiedBy` 方法可以用于检查单个对象是否满足规范。 +`IsSatisfiedBy` 方法可以用于检查单个对象是否满足规约. -**例如:如果顾客不满足年龄规定,则抛出异常** +**例如:如果顾客不满足年龄规定,则抛出异常** ````csharp using System; @@ -98,7 +98,7 @@ namespace MyProject ### ToExpression & Repositories -`ToExpression()` 方法可用于将规范转化为表达式。通过这种方式,您可以使用规范在**数据库查询时过滤实体**。 +`ToExpression()` 方法可用于将规约转化为表达式.通过这种方式,你可以使用规约在**数据库查询时过滤实体**. ````csharp using System; @@ -133,9 +133,9 @@ namespace MyProject } ```` -> 规范被正确地转换为SQL/数据库查询语句,并且在DBMS端高效执行。虽然它与规范无关,但如果您想了解有关 `AsyncExecuter` 的更多信息,请参阅[仓储](Repositories.md)文档。 +> 规约被正确地转换为SQL/数据库查询语句,并且在DBMS端高效执行.虽然它与规约无关,但如果你想了解有关 `AsyncExecuter` 的更多信息,请参阅[仓储](Repositories.md)文档. -实际上,没有必要使用 `ToExpression()` 方法,因为规范会自动转换为表达式。这也会起作用: +实际上,没有必要使用 `ToExpression()` 方法,因为规约会自动转换为表达式.这也会起作用: ````csharp var queryable = await _customerRepository.GetQueryableAsync(); @@ -144,11 +144,11 @@ var query = queryable.Where( ); ```` -## 编写规范 +## 编写规约 -规范有一个强大的功能是,它们可以与`And`、`Or`、`Not`以及`AndNot`扩展方法组合使用。 +规约有一个强大的功能是,它们可以与`And`、`Or`、`Not`以及`AndNot`扩展方法组合使用. -假设您有另一个规范,定义如下: +假设你有另一个规约,定义如下: ```csharp using System; @@ -167,7 +167,7 @@ namespace MyProject } ``` -您可以将 `PremiumCustomerSpecification` 和 `Age18PlusCustomerSpecification` 结合起来,查询优质成人顾客的数量,如下所示: +你可以将 `PremiumCustomerSpecification` 和 `Age18PlusCustomerSpecification` 结合起来,查询优质成人顾客的数量,如下所示: ````csharp using System; @@ -199,7 +199,7 @@ namespace MyProject } ```` -如果你想让这个组合成为一个可复用的规范,你可以创建这样一个组合的规范类,它派生自`AndSpecification`: +如果你想让这个组合成为一个可复用的规约,你可以创建这样一个组合的规约类,它派生自`AndSpecification`: ````csharp using Volo.Abp.Specifications; @@ -217,7 +217,7 @@ namespace MyProject } ```` -现在,您就可以向下面一样重新编写 `GetAdultPremiumCustomerCountAsync` 方法: +现在,你就可以向下面一样重新编写 `GetAdultPremiumCustomerCountAsync` 方法: ````csharp public async Task GetAdultPremiumCustomerCountAsync() @@ -228,30 +228,30 @@ public async Task GetAdultPremiumCustomerCountAsync() } ```` -> 你可以从这些例子中看到规范的强大之处。如果您之后想要更改 `PremiumCustomerSpecification` ,比如将余额从 `100.000` 修改为 `200.000` ,所有查询语句和合并的规范都将受到本次更改的影响。这是减少代码重复的好方法! +> 你可以从这些例子中看到规约的强大之处.如果你之后想要更改 `PremiumCustomerSpecification` ,比如将余额从 `100.000` 修改为 `200.000` ,所有查询语句和合并的规约都将受到本次更改的影响.这是减少代码重复的好方法! ## 讨论 -虽然规范模式通常与C#的lambda表达式相比较,算是一种更老的方式。一些开发人员可能认为不再需要它,我们可以直接将表达式传入到仓储或领域服务中,如下所示: +虽然规约模式通常与C#的lambda表达式相比较,算是一种更老的方式.一些开发人员可能认为不再需要它,我们可以直接将表达式传入到仓储或领域服务中,如下所示: ````csharp var count = await _customerRepository.CountAsync(c => c.Balance > 100000 && c.Age => 18); ```` -自从ABP的[仓储](Repositories.md)支持表达式,这是一个完全有效的用法。您不必在应用程序中定义或使用任何规范,可以直接使用表达式。 +自从ABP的[仓储](Repositories.md)支持表达式,这是一个完全有效的用法.你不必在应用程序中定义或使用任何规约,可以直接使用表达式. -所以,规范的意义是什么?为什么或者应该在什么时候考虑去使用它? +所以,规约的意义是什么?为什么或者应该在什么时候考虑去使用它? ### 何时使用? -使用规范的一些好处: +使用规约的一些好处: -- **可复用**:假设您在代码库的许多地方都需要用到优质顾客过滤器。如果使用表达式而不创建规范,那么如果以后更改“优质顾客”的定义会发生什么?假设您想将最低余额从100000美元更改为250000美元,并添加另一个条件,成为顾客超过3年。如果使用了规范,只需修改一个类。如果在任何其他地方重复(复制/粘贴)相同的表达式,则需要更改所有的表达式。 -- **可组合**:可以组合多个规范来创建新规范。这是另一种可复用性。 -- **命名**:`PremiumCustomerSpecification` 更好地解释了为什么使用规范,而不是复杂的表达式。因此,如果在您的业务中使用了一个有意义的表达式,请考虑使用规范。 -- **可测试**:规范是一个单独(且易于)测试的对象。 +- **可复用**:假设你在代码库的许多地方都需要用到优质顾客过滤器.如果使用表达式而不创建规约,那么如果以后更改“优质顾客”的定义会发生什么?假设你想将最低余额从100000美元更改为250000美元,并添加另一个条件,成为顾客超过3年.如果使用了规约,只需修改一个类.如果在任何其他地方重复(复制/粘贴)相同的表达式,则需要更改所有的表达式. +- **可组合**:可以组合多个规约来创建新规约.这是另一种可复用性. +- **命名**:`PremiumCustomerSpecification` 更好地解释了为什么使用规约,而不是复杂的表达式.因此,如果在你的业务中使用了一个有意义的表达式,请考虑使用规约. +- **可测试**:规约是一个单独(且易于)测试的对象. ### 什么时侯不要使用? -- **没有业务含义的表达式**:不要对与业务无关的表达式和操作使用规范。 -- **报表**:如果只是创建报表,不要创建规范,而是直接使用 `IQueryable` 和LINQ表达式。您甚至可以使用普通SQL、视图或其他工具生成报表。DDD不关心报表,因此从性能角度来看,查询底层数据存储的方式可能很重要。 +- **没有业务含义的表达式**:不要对与业务无关的表达式和操作使用规约. +- **报表**:如果只是创建报表,不要创建规约,而是直接使用 `IQueryable` 和LINQ表达式.你甚至可以使用普通SQL、视图或其他工具生成报表.DDD不关心报表,因此从性能角度来看,查询底层数据存储的方式可能很重要.