Clean Architecture

国内对于Clean Architecture的翻译很多,洁净/整洁/清晰。但无论哪一种都阐明了它简洁、清晰的个性。

晚期它长这样

看到这张图的同学可能会对另外一张图有印象

洋葱架构(Onion)

当初长这样

看起来如同是亲戚,它们确实也有着千头万绪的关系

剖析Clean Architecture

这部分次要是依据explicit architecture文章的了解整顿的,有翻译也有本人理解消化的。如有错漏欢送斧正,谢谢

三大构建块

  • 用户界面
  • 基础设施
  • 利用外围

控制流

  1. 用户界面
  2. 利用外围
  3. 基础设施
  4. 利用外围
  5. 用户界面

工具

左右两侧造成鲜明对比,动机不同

  • HTTP/CLI:通知利用要做什么
  • SMS/Mailing Server/Search Engine...:利用通知它们要做什么

链接工具和交付机制到利用外围

将工具连贯到应用程序外围的代码单元称为适配器(端口和适配器架构)。

通知咱们的应用程序做某事的适配器称为主适配器或被动适配器,而咱们的应用程序通知咱们做某事的适配器称为从适配器或被动适配器。

端口

这些适配器为了适应利用外围的一个十分特定的入口点,即端口。端口只不过是工具如何应用应用程序外围或利用外围如何应用它的标准。

你能够看作是接口和DTO

主适配器或被动适配器

主适配器或被动适配器围绕一个端口并应用它来通知利用外围该做什么。

咱们的被动适配器是Controller或Console Commands,它们在其构造函数中注入了一些对象,该对象的类实现了Controller或Console Commands所需的接口(端口)。

端口能够是控制器须要的服务接口或存储库接口,而后将 Service、Repository 或 Query 的具体实现注入并在 Controller 中应用。

或者,端口能够是Command Bus或Query Bus的接口。在这种状况下,将Command Bus或Query Bus的具体实现注入到Controller中,而后Controller结构Command或Query并将其传递给相干Bus。

注:这里其实提到了CQRS

从适配器或被动适配器

与围绕在端口四周的被动适配器不同,从适配器实现一个端口、一个接口,而后在须要端口的任何中央注入利用外围。

能够了解为是右侧是合乎利用外围的须要的接口或者对象,而左侧则是包装用例的传播机制,如HTTP/CLI等

假如咱们有一个须要长久化数据的需要:

  1. 咱们创立一个长久化接口(在左侧端口四周),有一个保留数据的办法和一个通过ID删除数据的办法
  2. 基础设施中提供一个实现类,通过IoC注入这个接口和对应的实现类
  3. 在须要长久化数据的类的构造函数中注入一个长久化接口
  4. 如果有一天咱们须要从SQL Server换到MongoDb,只须要替换步骤2中的实现类和注入新的实现类即可

IoC

与上图相比,仅仅是多了一个蓝色的箭头从内部直插入利用外围外部。在下面例子中有提到通过IoC的作用这里就不再反复了。

至此,咱们统一在解说利用外围的外围,而利用外围是咱们架构设计的重点。

利用外围组织

洋葱架构采纳 DDD 分层并将它们合并到端口和适配器架构中。这些层旨在为业务逻辑带来一些组织,即端口和适配器“六边形”的外部,就像在端口和适配器中一样,依赖方向是朝向核心的。

应用层

用例(Use Case)是咱们的利用中的一个或多个用户界面触发的流程(业务逻辑)。用户界面能够是终端用户界面也能够是治理界面,或者控制台界面和API。

用例在应用层定义,由DDD和洋葱架构提供,它能够蕴含端口,ORM接口,搜索引擎接口,音讯接口等,也能够是CQRS解决Handlers的中央,发送邮件,调用第三方API等。

应用服务/Command Handler蕴含用例的业务逻辑,作用是:

  1. 应用Repository查找一个或多个实体
  2. 通知这些实体做一些畛域逻辑
  3. 应用Repository长久化这些实体,保留数据更改

Command Handler能够有两种不同的应用形式:

  1. 蕴含执行用例的实在业务逻辑
  2. 作为架构中的中间件,接管Command并触发应用服务中的逻辑

畛域层

畛域内的对象除了有对象自身的属性外,还能够操作该对象外部的属性,这是特定于域自身的,并且独立于触发该逻辑的业务流程,它们是独立的,齐全不晓得应用层。

畛域服务

有时咱们会遇到一些波及不同实体的畛域逻辑,无论是否雷同,该畛域逻辑不属于实体自身,它没有间接责任。

那咱们能够应用畛域服务来承载这部分逻辑,可能有人会感觉那能够放应用层,但畛域逻辑在其余用例中就不能重用了。畛域逻辑应该在畛域外部,不要回升到应用层。

畛域服务能够应用其余畛域服务,或者其余畛域对象

畛域模型

在最核心,依赖于它之外的任何货色,是畛域模型,它蕴含代表畛域中某些事物的业务对象。至于如何定义畛域模型能够参考第一篇。

组件

组件与利用外围内所有的层穿插,从外贯通到外部。例如身份验证、受权、计费、用户、评论或账户,但它们仍然与畛域无关。

像受权和身份验证这样的限界上下文应该被视为暗藏在某种端口前面的内部工具。

解耦组件

具备齐全解耦的组件意味着一个组件不间接理解任何另一个组件。换句话说,它可能没有接口,所以咱们须要一些新的架构构造。

比方事件、最终一致性、服务发现等。当你往这条路上走的时候,你就开始脱离单体了。

这里Dapr或者是个不错的抉择,它蕴含了这些性能,对Dapr感兴趣的能够看之前的手把手教你学Dapr系列

MASA Framework解决方案

联合DDD和Clean Architecture以及MASA Framework的个性,咱们将在MASA.BuildingBlocks中以接口的模式定义标准,在MASA.Contrib中对接口进行实现。

这意味着你能够只关怀BuildingBlocks中的接口定义来编写你的代码,也能够基于接口从新实现在DDD落地中你本人的业务个性来调整或扩大咱们提供的默认行为。比方,你有本人的UoW、仓储层等都能够随便换掉。

应用层

应用服务:

实现应用程序的用例,连接表示层(接口层)与畛域层

除此之外,基于MASA EShop的示例中的MASA.EShop.Services.Catalog的CQRS架构演示,应用层也能够承载CQRS的Command Hanlder。除了能够持续应用畛域层来解决Command业务外,你也能够抉择在此停止,在Command Handler里简化架构间接对Command进行解决。

示例代码:https://github.com/masalabs/M...

工作单元:

默认事件是在应用服务中首次开启,所以UoW也会在应用层被激活(实际上底层会依据仓储的操作,只有首次增删改才会主动激活,这个性能能够敞开,改为手动管制)

中间件:

对于应用Event Bus开发来说,应用层还能够作为对立的AOP出入口。

例如对立的事件参数验证:

首先须要启用对立验证中间件 https://github.com/masalabs/M...

而后为对应的Event/Command编写验证逻辑 https://github.com/masalabs/M...

畛域层

对于实体、聚合、值对象等概念就不再介绍了,能够参考上一章的内容。

贫血模型VS充血模型

畛域中须要限定畛域内的业务逻辑,加上EF Core对充血模型的反对,充血模型更适宜用作畛域模型的开发。

将数据与行为封装,体现出事实业务对象残缺行为,每个畛域具备明确的职责划分,将逻辑扩散到畛域对象中。这也是应用层与畛域层的一个比拟显著的区别。

对于实体相干对象,咱们提供了对应的类,当然也包含审计和值对象可能须要用到的枚举类。

枚举类:咱们提供了Enumeration,参考自:https://docs.microsoft.com/zh...

畛域服务:

畛域服务中能够调用其余的畛域服务(包含过程内或跨过程),所以咱们提供了IDomainService,它的性能包含:

  • 主动协调过程内和跨过程的事件传递
  • 反对被调用方是CQRS
  • 默认反对事件压栈,在UoW Commit后对立触发。也反对实时发送事件(如后续业务可被降级,但跨过程的事件为主业务逻辑不可被降级)
  • 跨过程事件反对最终一致性和Saga

仓储:

畛域中定义的仓储为接口,代表在以后畛域内关怀的业务。比方用户,在用户治理和名片两种业务中,对于IRepository<User>的定义是不同的。但在基础设施中BaseRepository<User>能够是同一个,因为BaseRepository<User>能够是最残缺的实现,但畛域内仓储服务只认其中一部分

基础设施层

给接口提供实现,如仓储接口的实现、Event Bus中MQ或中介者的实现(MASA Framework已实现,所以咱们的示例中目前只有仓储接口的实现)等。

MASA Framework模板架构

在MASA Framework模板中提供了自由组合的形式,你能够依据你的需要随便调整如是否蕴含Blazor、Dapr、DDD、CQRS等。

咱们的MASA.EShop举荐采纳了4种架构形式,从简到繁,本篇介绍最简单的一个

Minimal APIS + CQRS + Dapr actor

MASA.EShop中的Ordering服务就是采纳这种架构分层,其实分层解释下面也有,只是之前解释的是站在MASA BuildingBlocks的角度,而接下来将是站在开发者角度。
  • User Interface Layer:它负责提供用户接口,残缺前端逻辑。用户也能够是计算机系统,不特指是人。所以这里既能够是API,也能够是Blazor、MVC等。
  • Application Layer:它能够很薄也能够很厚(在以后分层下举荐薄)。负责协调User Interface和Domain,包含服务的编排和转发,AOP,发送事件等。

    如果你有Domain Layer能够把Command做的很薄调用Domaiin。如果你要精简CQRS,也能够不必Domain,在这一层间接做应用服务。当然Query也一样,但Query即使应用Domain也举荐把查问放在应用服务里,这样能够把Query和Command拆散来取得CQRS的劣势。
  • Domain Layer:业务外围,包含了畛域对象和畛域服务以及适配器的接口。倡议采纳充血模型将行为留在畛域内,跨畛域且须要被复用的能够应用畛域服务。仓储接口则限定畛域内的仓储行为,与物理仓储不同的是更聚焦业务自身,而不是实体的残缺仓储能力。
  • Infrastructure Layer:给接口提供实现,如仓储接口的实现、Event Bus中MQ或中介者的实现(MASA Framework已实现,所以咱们的示例中目前只有仓储接口的实现)等。

总结

至此,咱们不仅实现了对单体架构的反对,还通过Event Bus对微服务架构提供了反对。

如果你对DDD或者MASA Framework感兴趣,无妨把MASA.EShop跑起来看一下,它提供了4种架构形式参考,能够满足大部分业务场景对架构的要求。

学以致用,学无止境。

参考:

DDD, Hexagonal, Onion, Clean, CQRS, … How I put it all together:https://herbertograca.com/201...

开源地址

MASA.BuildingBlocks:https://github.com/masastack/...

MASA.Contrib:https://github.com/masastack/...

MASA.Utils:https://github.com/masastack/...

MASA.EShop:https://github.com/masalabs/M...

MASA.Blazor:https://github.com/BlazorComp...

Gitee地址 :MASA.Blazor:BlazorComponent/MASA.Blazor

如果你对咱们的 MASA Framework 感兴趣,无论是代码奉献、应用、提 Issue,欢送分割咱们