学海无涯

导航

CleanArchitecture 清洁架构

 

 

 

 

 

 

 https://github.com/ardalis/CleanArchitecture

Questions

Why do we separate applications into multiple projects?

为什么我们要将应用程序分成多个项目?

What are some principles we can apply when organizing our software modules?

设计我们的软件模块时,哪些原则我们可以应用?

How does the organization of our application’s solution impact coupling?

我们的解决方案怎么设计才不会高度耦合?

What problems result from certain common approaches?

某些常见的方法会导致什么问题?

How does Clean Architecture address these problems?

清洁架构如何解决这些问题?

Principles 原则

 Separation of Concerns 关注点分离

Avoid mixing different code responsibilities in the same (method | class | project)

避免在代码中混合不同的代码职责,例如:(方法 | 类 | 项目)

The Big Three™ 经典三层架构

▪Data Access 数据访问层

▪Business Rules and Domain Model 业务规则和领域模型层

▪User Interface 用户界面UI

Single Responsibility 单一职责原则

Works in tandem with Separation of Concerns 与关注点分离协同工作

Classes should focus on a single responsibility – a single reason to change. 类应该专注于一个单一的职责——类的变更仅仅只有一个理由。

Following Don’t Repeat Yourself… ▪ 遵循不要重复自己原则……

Refactor repetitive code into functions 将重复的代码重构为方法

▪Group functions into cohesive classes 将方法分组到有高内聚的类中

▪Group classes into folders and namespaces by 将类分组到文件夹和命名空间中

▪ Responsibility 单一职责

▪ Level of abstraction 抽象更高级别

▪ Etc.

▪ Further group class folders into projects 最终将类文件夹分组到一个项目中

Invert (and inject) Dependencies 控件反转(DI注入)依赖倒置

Both high level classes and implementation-detail classes should depend on abstractions (interfaces).

领域类和实现细节类都应该依赖于抽象(接口)。

Classes should follow Explicit Dependencies Principle:类应遵循显式依赖原则:

▪ Request all dependencies via their constructor 通过其构造函数显示请求所有依赖项

▪ Make your types honest, not deceptive 让你的类型诚实,而不是欺骗

Corollary: Abstractions/interfaces must be defined somewhere accessible by: 推论:抽象/接口必须定义在可以通过以下方式都能访问的某个地方:

▪Low level implementation services 底层实现服务

▪High level business services 领域业务服务

▪User interface entry points  UI用户界面

直接依赖 (差的设计)

 

依赖倒置 (好的依赖关系)

 

 

 经典三层架构

 

 

 

Transitive Dependencies  传递依赖  依赖层层传递,高度耦合

 

 Domain-Centric Design  领域驱动设计

Domain Model 领域模型

 Not just business logic, but also:不仅仅是业务逻辑,还有:

A model of the problem space composed of Entities, Interfaces, Services, and more. 实体、接口、服务 所组成的领域问题空间模型

Interfaces define contracts for working with domain objects Everything in the application (including infrastructure and data access) depends on these interfaces and domain objects

应用程序中的所有内容(包括基础设施和数据访问)都取决(围绕)于在这些接口和领域对象上

Clean Architecture “Rules” 清洁架构“规则”

The Application Core contains the Domain Model 应用核心包含领域模型

All projects depend on the Core project; dependencies point inward toward this core.  所有项目都依赖于Core项目;依赖关系向内指向这个核心

Inner projects define interfaces; Outer projects implement them.内部项目定义接口;外部项目实现它们

Avoid direct dependency on the Infrastructure project (except from Integration Tests and possibly Startup.cs )

避免直接依赖于基础设施项目(除了集成测试和可能的 Startup.cs)

Clean Architecture Features 清洁架构特性

Framework Independent ◦ 框架独立

You can use this architecture with ASP.NET (Core), Java, Python, etc. ◦   ASP.NET (Core), Java, Python 都可以使用这个架构

It doesn’t rely on any software library or proprietary codebase. 它不依赖任何软件库或专有代码库。

Database Independent ◦ 数据库独立

The vast majority of the code has no knowledge of persistence details. 绝大多数代码都没有持久化的细节。

This knowledge may exist in just one class, in one project that no other project references.

这些细节仅存在于一个单独的项目中,其它项目不会依赖这个项目

UI Independent ◦ UI独立

Only the UI project cares about the UI. ◦ 其它项目不需要依赖UI

The rest of the system is UI-agnostic. 系统其它项目都不依赖UI

Testable ◦ 可测试

Apps built using this approach, and especially the core domain model and its business rules, are easy to test.

使用这种方法构建的应用程序,尤其是核心域模型及其业务规则,易于测试。

The Core Project (domain model) 核心项目(领域模型)

 

 

The Infrastructure Project 基础设施项目,所有的具体实现,以及解决方案依赖的具体实现包在此项目中

 The Web Project Web 项目

 

 Sharing Between Solutions: Shared Kernel 共享内核(接口、抽象层)

公共类型可以在解决方案之间共享。 将被核心项目引用。理想情况下作为 Nuget 包分发。

细节设计原则

 What belongs in actions/handlers? 什么属于动作/处理程序?

Controller Actions (or Page Handlers) should: 控制器操作(或页面处理程序)应该:

1) Accept task-specific types (ViewModel, ApiModel, BindingModel) 接受特定于任务的类型(ViewModel、ApiModel、BindingModel)

2) Perform and handle model validation (ideally w/filters) 执行和处理模型验证(最好使用过滤器)

3) “Do Work” (More on this in a moment) “工作”(稍后会详细介绍)

4) Create any model type required for response (ViewModel, ApiModel, etc.)  创建响应所需的任何模型类型(ViewModel,ApiModel 等)

5) Return an appropriate Result type (View, Page, Ok, NotFound, etc.)  返回一个合适的 Result 类型 (View, Page, Ok, NotFound,ETC。)

“Do Work” – Option One  “待做任务”——选项一

Repositories and Entities 存储库和实体

1) Get entity from an injected Repository 从注入的存储库中获取实体

2) Work with the entity and its methods. 使用实体及其方法。

3) Update the entity’s state using the Repository 使用存储库更新实体的状态

Great for simple operations 非常适合简单的操作

Great for CRUD work 非常适合 CRUD 工作

Requires mapping between web models and domain model within controller 需要控制器内的 Web 模型和领域模型之间的映射

namespace CleanArchitectureApp.Web.Api;
/// <summary>
/// A sample API Controller. Consider using API Endpoints (see Endpoints folder) for a more SOLID approach to building APIs
/// https://github.com/ardalis/ApiEndpoints
/// </summary>
public class ProjectsController : BaseApiController
{
  private readonly IRepository<Project> _repository;//注入的存储库

  public ProjectsController(IRepository<Project> repository)
  {
    _repository = repository;
  }
// PATCH: api/Projects/{projectId}/complete/{itemId}
  [HttpPatch("{projectId:int}/complete/{itemId}")]
  public async Task<IActionResult> Complete(int projectId, int itemId)
  {
    var projectSpec = new ProjectByIdWithItemsSpec(projectId);
    //使用注入的存储库获取领域实体
    var project = await _repository.FirstOrDefaultAsync(projectSpec); 
    if (project == null) return NotFound("No such project");

    var toDoItem = project.Items.FirstOrDefault(item => item.Id == itemId);
    if (toDoItem == null) return NotFound("No such item.");

    toDoItem.MarkComplete(); //调用实体及其方法。
    await _repository.UpdateAsync(project);//使用存储库更新领域实体的状态

    return Ok();
  }

}

“Do Work” – Option Two  “待做任务”——选项二
Work with an application service. 使用应用程序服务API。
1) Pass ApiModel types to service 将 ApiModel 类型传递给服务
2) Service internally works with repositories and domain model types. 服务在内部使用存储库和域模型类型。
3) Service returns a web model type 服务返回一个 web 模型类型
Better for more complex operations 更适合更复杂的操作
Application Service is responsible for mapping between models 应用服务负责模型之间的映射
Keeps controllers lightweight, and with fewer injected dependencies 保持控制器轻量级,并减少注入的依赖项

 

// POST: api/Projects
  //将 ApiModel 类型传递给服务
  [HttpPost]
  public async Task<IActionResult> Post([FromBody] CreateProjectDTO request)
  {//将ApiModel模型转换成领域模型
    var newProject = new Project(request.Name, PriorityStatus.Backlog);
    var createdProject = await _repository.AddAsync(newProject);
    //返回ApiModel模型
    var result = new ProjectDTO
    (
        id: createdProject.Id,
        name: createdProject.Name
    );
    return Ok(result);
  }

  

posted on 2022-10-23 11:23  宁静致远.  阅读(684)  评论(0编辑  收藏  举报