使用 nHydrate 进行模块化开发





5.00/5 (3投票s)
通过建模和代码生成支持多团队开发。
nHydrate 模块
模块是一项高级功能,对于大型项目非常有用。这个概念允许您将模型对象分组到逻辑模块中,这些模块将在 Visual Studio 中创建为完全独立的项目。模块是一种逻辑分组,它将表现为独立的 API。这使得您可以拥有一个通用的模型并在团队之间共享它。这对于在数据库(或应用程序部分)方面具有一些共性的产品上工作的团队来说非常有用,但这些产品仍应被视为不同的 API。
让我们更详细地讨论这一点。我们可以使用模块将功能分组到独立的 API 层中。假设有两个团队正在开发两个应用程序或应用程序的两个部分。每个团队都有自己的数据库(甚至是一个不同的数据库)与之交互,并且团队之间存在许多重叠的共性。Team1 拥有 3 个表(订单、订单明细和产品)与 Team2 重叠,还有一个 Team2 不共享的额外表(客户)。Team2 共享这三个通用表,但有一个 Team1 不共享的额外表(员工)。
完整数据库
目标是共享一个数据库,但有一个有限的 API,它只公开每个团队所需的实体。Team1 将负责应用程序的客户方面。他们可能会通过 ID、姓名或电子邮件查找客户,然后遍历该客户的订单关系,最后显示每个订单的详细信息。他们使用的 API 完全没有员工的实体或概念。
Team2 也必须编写类似的功能,但他们只关心员工方面。他们可能会查找一名员工,并使用该实体导航到订单表,最后是订单的详细信息。Team2 不了解客户表。它不在他们的 API 中。
现在,功能结构允许两个团队使用同一个数据库,同时轻松维护一个模型。如果任何公共表更改其结构,两个 API 都会受到影响。例如,如果订单表添加了一个列“税费”,那么两个 API 都将重新生成。安装程序项目将反映这个新列,每个 Entity Framework API 也将反映这个更改。
模块 1(客户 + 通用)
模块 2(员工 + 通用)
通常,要创建不同的 API,您需要为每个团队创建一个模型,每个模型都会生成一个单独的 API。然而,由于存在重叠,模型需要大量的重复维护。有两个模型,订单表的更改将需要同时更改两个模型。这容易出错且效率低下。使用单个模型,更改将从同一个模型传播到两个模块 API。
我们可以通过使用 nHydrate 的模块功能轻松实现这种拆分。首先选择模型,然后在属性窗口中将 UseModules 属性设置为 true。现在,在定义了某些模块之前,无法生成模型。在 nHydrate Explorer 窗口中,右键单击根节点,然后添加两个名为 Customer 和 Employer 的模块。
Explorer 添加
一旦创建了这些模块,您就可以在 explorer 树中看到它们,就像其他模型元素一样。
带模块的 Explorer
现在您必须将所有模型对象分配给一个或多个模块。右键单击模型画布上的任何空白区域,然后选择菜单 Model | Module Associations。这将弹出模块分配窗口。从这里,您可以选择一个模块并将对象分配给它。一个表(或任何其他对象)可以分配给任意数量的模块。通过这种方式,模块可以根据您的业务规则具有任意程度的重叠功能。
视图、存储过程和函数具有全有或全无的包含。换句话说,它们在一个模块中,或者不在。您不能将一个视图的一部分包含在模块中。然而,表的情况则不同。您可以包含部分表到模块中。
部分表在需要组件功能时非常有用。您可能有一个非常大的表(很多列),但应用程序的一部分可能只需要其中的一部分。在我们的 Employee 模块中,可能不需要公开 Order 表的 Freight 属性;这可能是不必要的,也可能是安全问题。无论如何,如果使用 Employee 模块的开发人员永远不需要该字段,那么一开始就不需要包含它。当您将对象分配给 Employee 模块时,只需取消选中 Order 表中的 Freight 字段。现在,当生成模块 API 时,会有一个 Order 实体,但它缺少 Freight 字段。请放心,该字段仍然在数据库中,但无法从 Employee 模块程序集中访问或设置。
Customer Module
现在我们可以定义 Employee 模块。从下拉框中选择指定的模块,并勾选要包含的对象。
员工模块
现在我们已经定义了表并将它们分配给了模块,我们可以生成代码。右键单击模型画布上的任何空白区域,然后选择 Model | Generate 菜单。在这里,系统会提示您要生成什么。
生成对话框
您还必须选择要生成的模块。
选择模块
生成完成后,您应该会在 Solution Explorer 中看到新的项目。
解决方案资源管理器
模块将通过指定的生成器运行,以创建离散的 API。通常,当不使用模块时,模型生成会为每个生成器创建一个单独的项目。例如,要创建 Entity Framework API,需要使用三个生成器模板:EFDAL、Interfaces 和 Installer。使用模块时,每个模块和每个生成器都会有一个新项目。在此示例中,如果您定义了两个模块:Customer 和 Employee,那么将创建六个项目而不是三个(3 个生成器 x 2 个模块 = 6 个程序集)。
通用模块
我们现在可以管理整个应用程序的单个模型,但实际上为每个团队生成一个唯一的 API。这实际上带来了一些我们已经涵盖过的显而易见的优势之外的巨大好处。如果您考虑上面生成的两个库,仍然存在一个问题。我们无法编写在团队之间共享的通用库。这是因为这些库中的每一个都特定于上述实现之一:Customer 或 Employee。我们真正需要的是一个通用的 API。
我们希望有一个通用的业务库,它可以与任何数据库通信(这些 API 实际上可能与同一个数据库或不同的数据库通信),而不会出现运行时缺少对象的错误。如果我们创建一个模块,它是上述两个模块的子集,我们就拥有一个通用的框架 API。创建一个新模块并将通用实体添加到其中。现在我们可以编写一个通用的框架程序集,为两个团队执行通用的操作。这意味着现在实际上有三个独立的 API:Employee、Customer 和 Common。然而,它们都来自同一个模型进行管理。
模块 3(通用)
由于这是从通用模型进行管理,因此它始终是最新的。如果我们更改模型中的一个通用表,重新生成将确保所有 API 和安装程序项目都得到更新。一旦我们更新了数据库,通用 API 就可以在多个数据库模式上运行。现在我们可以自由地编写一个封装全局(跨模块)业务规则的共享程序集,所有开发组都可以使用它。这个共享程序集使用通用的已生成 API 来应用通用的业务规则,并且可以被两个团队引用以执行供应功能。现在,两个团队不再需要编写相同的代码两次来复制他们共享的业务规则。我们可以一次编写,无需重复工作。
当然,这些将是我们应用程序中的低级共享程序集。每个编写其应用程序部分的团队也将编写使用其自身特定 API 的自定义程序集。这使得具有重叠功能的两个应用程序可以共享代码。
应用程序结构
上面的图表显示了一种可能的应用程序配置。主应用程序知道 Team1 和 Team2 编写的库:Customer 和 Employee 业务 API。这两个库可以利用通用的业务 API,因为它是两者的子集。每个业务 API 都使用生成的 Entity Framework API。所有生成的 API 库都由一个模型管理。通过重新生成,可以跨所有 API 传播对模型的更改。每个团队手动编写的自定义 API 使用生成的 API,因此它们也与正确版本的数据库通信,因为安装程序项目始终使数据库与模型保持同步。
模块规则
上面的示例描述了一个模块如何是另一个模块的子集。如果这是您希望组织模型的方式,最好不要手动进行。如果您将一个表添加到 Common 模块而不是 Customer 模块,那么 Customer 模块就不再是 Common 的超集。我们真正需要的是一种方法来让模型强制执行这个模型规则。
模块规则允许您定义模块之间的包含或排除规则。这些规则将由模型强制执行,并在必要时提供验证错误。规则可以定义为子集或外集。在验证过程中,将应用规则以确保满足定义的规则。子集或外集规则将仅应用于包含属性中指定的对象类型。如果您想确保 Common 模块是 Customer 模块的子集,您可以定义一个模块规则如下。
- 在 Common 模块上创建一个模块规则。
- 将其状态设置为 Subset
- 将其依赖模块设置为 Customer。
- 将其 Inclusion 属性设置为 Entity only。
这确保了视图、存储过程和函数不会被检查以满足子集规则。如果 Common 模块中的所有实体都不在 Customer 模块中,此定义的规则将导致验证错误,并且您将无法生成。
外集规则的作用与此类似,但它确保两个模块之间没有重叠。您可以应用任意数量的模块规则。如果您正在管理一个具有许多交互式模块的模型,这是一个非常有用的功能。