简单代码!

踢足球很简单,但踢简单的足球是最难的。- 约翰 · 克鲁伊夫

如果我们把这个著名的名言作为编程,我们可以说:

编写代码非常简单,但编写简单的代码是最 难的事情。

什么是域驱动设计?

域驱动设计 (DDD) 是将实现与 不断发展的模型连接起来,满足复杂需求的软件开发方法;

DDD 适用于复杂的领域和大规模应用,而不是简单的 CRUD 应用程序。

它侧重于核心域逻辑,而不是基础架构细节。它有助于构建灵活、模块化和可维护的代码库。

OOP & SOLID

实现 DDD 高度依赖于对象导向 编程 (OOP) 和固态原则。

实际上,它实施并扩展了这些原则。因此,对 OOP 和 SOLID 的良好理解对您帮助 很大,同时真正实现 DDD。

DDD 层 = 清洁架构

基于域 的解决方案有四个基本层。

image

业务逻辑分为两层,领域层和 应用层,同时包含不同类型的 业务逻辑:

  • 域层实现领域/系统的核心、用例独立业务逻辑。
  • 应用层根据领域实施应用程序的使用案例。使用案例可视为用户界面 (UI) 上的用户交互。
  • 演示层包含应用程序的 UI 元素(页面、组件)。
  • 基础设施层通过实施对第三方库和系统的抽象和集成来支持其他层。

相同的分层可以显示为下面的图表,称为清洁架构,有时洋葱架构:

image

核心构建块

DDD 主要关注域和应用层,而忽略演示文稿和基础架构。它们被视为详细信息,业务层不应依赖于它们。

这并不意味着演示文稿和基础架构层并不重要。

它们非常重要。UI 框架和数据库提供商有自己的规则和最佳实践,您需要了解和应用这些规则和最佳实践。但是,这些不在 DDD 主题中。

本节介绍了域和应用层的基本构建基块。

领域层构建块

• 实体:实体是具有自身属性(状态、数据)和方法的对象,它们执行在这些属性上执行的业务逻辑。实体由其唯一标识符 (Id) 表示。具有不同 Id 的两个实体对象被视为不同的实体。

• 值对象:值对象是另一种由其属性而不是唯一 ID 标识的域对象。这意味着具有相同属性的两个值对象被视为同一对象。值对象通常以不可变的身份实现,并且大多比实体简单得多。

• 聚合和聚合根:聚合是由聚合根对象结合在一起的对象(实体和值对象)集群。聚合根是具有某些额外职责的实体的特定类型。

• 存储库(接口):存储库是域和应用层用于访问数据 持久性系统(数据库)的类似集合的界面。它从业务代码中隐藏了 DBMS 的复杂性。域层包含存储库的界面 s。

• 域服务:域服务是执行域核心业务规则的无国籍服务。实现依赖于 多个聚合(实体)类型或某些外部服务的域逻辑是有用的。

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

• 域事件:域事件是当域特定事件发生时以松散耦合的方式通知其他服务的一种方式。

应用层构建块

• 申请服务:应用程序服务是实现应用程序使用 案例的无国籍服务。应用程序服务通常获取并返回 DTO。它由演示层使用。它使用并协调域对象以实现使用案例。使用案例通常被视为工作单位。

• 数据传输对象 (DTO):DTO 是一个简单的对象,没有任何业务逻辑用于在应用程序和演示层之间传输状态(数据)。

• 工作单位(UOW):工作单位是一项原子工作,应作为交易单位进行。UOW 内部的所有操作都应在成功时承诺,或在失败时回滚。

实施:大局

.NET 解决方案的分层

下图显示了使用ABP 的应用程序启动模板创建的视觉工作室解决方案:

image

领域层

领域层分为两个项目:

  • 问题跟踪.领域是包含之前引入的所有构建基块(实体、值对象、领域服务、规范、存储库接口等)的基本域层。

• 问题跟踪.领域共享是一个薄项目,包含属于域层的某些类型,但与所有其他层共享。例如,它可能包含一些与域对象相关的常数和内含物,但需要由其他层重复使用。

应用层

应用层也分为两个项目:

  • 问题跟踪.应用程序.合同包含这些接口使用的应用程序服务接口和 DTO。此项目可由客户申请(包括 UI)共享。
  • 问题跟踪.应用程序是实现合同项目中 定义的界面的基本应用层。

演示层

  • 问题跟踪.Web 是本示例的核心 MVC / 剃刀页面应用程序的 ASP.NET。这是唯一可执行的应用程序,为应用程序和 API 提供服务。

远程服务层

  • 问题跟踪.httpapi 项目包含解决方案定义的 HTTP API。如果可用,它通常包含 MVC 控制器和相关型号。因此,您在本项目中编写 HTTP API。
  • 当您 有需要消耗 HTTP API 的 C#应用程序时,问题跟踪.Httpappi.客户端项目是有用的。一旦客户应用程序引用此项目,它可以直接注入和使用应用程序服务。这可以通过 ABP 框架的动态 C# 客户端 API 代理系统的帮助实现。

基础设施层

在 DDD 实施中,您可能有一个 单一的基础设施项目来实现所有抽象和集成,或者您可能为每个依赖项有不同的项目。

我们建议采取平衡的办法:为 主要基础设施依赖性(如实体框架核心) 创建单独的项目,并为其他基础设施创建共同基础设施项目。

ABP 的启动解决方案有两个实体 框架核心集成项目;

• 问题跟踪.实体框架核心是 EF 核心的基本集成包。您的应用程序的 DbContext、数据库映射、存储库的实现和其他 EF 核心相关内容都位于此处。

• 问题跟踪.实体框架工作核心.DbM移民是一个管理代码第一数据库迁移的特殊项目。此项目中有一个单独的 DbContext 来跟踪迁移。您通常不会经常接触此项目,只是需要创建新的数据库迁移或添加具有某些数据库表的应用程序模块,并且自然需要创建新的数据库迁移。

还有一个项目,问题跟踪.DbMigrator,这是一个简单的控制台应用程序,迁移数据库模式和种子的初始数据,当你执行它。

它是一个有用的实用程序应用程序,您可以将其用于开发和生产环境中。

解决方案中项目的依赖性

image

基于 DDD 的应用程序的执行流

下图显示了基于 DDD 模式开发的 Web 应用的典型请求流。

image

• 请求通常从 UI 上的用户交互(一个使用案例)开始,该交互会导致 HTTP 请求到服务器。

• 演示层(或分布式服务层中的 Razor Page 处理器)处理请求,并在此阶段执行一些交叉问题(授权、验证、异常处理等)。 控制器/Page 注入相关的应用程序服务界面,并通过发送和接收 DTO 来调用其方法。

• 应用服务使用域对象(实体、存储库接口、域服务等)实现使用案例。应用层实现一些交叉问题(授权、验证等)。应用服务方法应是工作单位。这意味着它应该是原子的。

ABP 框架会自动和常规地实现大多数交叉问题,您通常不需要为它们编写代码。

数据库提供商 / ORM 独立性

域和应用程序层应为 ORM/DB 提供商不可知论。

它们应该仅取决于存储库接口,存储库界面不使用任何 ORM 特定对象。

在这里,这一原则的主要原因:

  1. 要使您的域/应用程序基础架构独立,因为基础架构将来可能会发生变化,或者您以后可能需要支持第二个数据库类型。
  2. 要通过隐藏存储库背后的基础架构详细信息 ,使您的域名/应用程序专注于业务代码。
  3. 使您的自动测试更容易,因为在这种情况下,你可以嘲笑回购。