前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >软件架构编年史:EBI架构

软件架构编年史:EBI架构

作者头像
张逸
发布2023-03-23 16:07:52
5120
发布2023-03-23 16:07:52
举报
文章被收录于专栏:斑斓斑斓

覃宇,Android开发者/ThoughtWorks技术教练//译者,热衷于探究软件开发的方方面面,从端到云,从工具到实践。喜欢通过翻译来学习和分享知识,译作有《Kotlin实战》、《领域驱动设计精粹》、《Serverless架构:无服务器应用与AWS Lambda》和《云原生安全与DevOps保障》。

EBI 架构(Entity-Boundary-Interactor,实体-边界-交互器)架构因为 Robert C. Martin 关于整洁架构(我会在后续的文章中介绍)的讲座而被人熟知。

然而,Ivar Jacobson 早在 1992 年就在他的著作 Object-Oriented Software Engineering: A use case driven approach中提出了这个模式。那时,Jacobson 实际上把它叫做实体-接口-控制(Entity-Interface-Control),但是后来改成了 EBI,避免“接口”和编程语言中的结构“接口”混淆,以及“控制”和 MVC 中的控制器混淆。

◐ 实体

实体对象承载着系统使用的数据与所有和这些数据天然耦合在一起的行为。每一个实体对象都代表着一个和问题域相关的概念,以及它承载的身份和可恢复的(持久化)数据。Jacobson 告诉我们,实体对象应该包含和对象自己变同时发生变化的逻辑,例如,如果它的数据结构发生变化,这些数据上的操作也需要改变,因此它们也应该放在实体内。

有意思的是,早在 1992 年,Jacobson 就作出了如下警告:

新手也许有时会让实体对象只携带数据,把所有动态的行为放到控制对象中[...]。然而,这是应该避免的。[...] 许多行为反而应该放在实体对象中。——Ivar Jacobson 1992, pp. 134

这就是我们现在所知的“贫血实体”。

◐ 边界(接口)

边界对象是对系统接口的建模。

[…] 和系统接口有关的一切都应该放在接口对象中——Ivar Jacobson 1992, pp. 134

所有依赖系统环境(工具和传达机制)的功能都属于边界对象。

任何角色和系统的交互都要经过边界对象。如 Jacobson 所述,角色可以是像客户或管理员(操作员)这样的人类用户,也可以是像告警、打印机或者第三方 API 这样的非人类“用户”。

回味一下边界的概念,再看看图 7.14,把其中的四个边界想像成六个,我不禁联想到 2005 年才提出的端口和适配器架构(我将在后续的文章中介绍),整整晚了 13 年。

◐ 交互器(控制)

交互器对象承载了和其它任何对象类型天然无关的行为。

这些行为通常由对实体一些操作组成,最后将某个结果返回给边界对象。

边界对象和实体对象挑剩下的行为会被放在控制对象中。—— Ivar Jacobson 1992, pp. 185

这意味着所有不适合放在边界或实体的行为都会被放在一个或多个控制对象中。

因此,Jacobson 认为控制对象不仅仅是编排用例的对象,也包括那些拥有和用例有关的行为的对象,它们既不是边界也不是实体。

根据我的经验,我认为他称为交互器的对象就是我称为应用服务(编排用例)和领域服务(包含领域行为但不是实体)的对象。

位于中间的交互器对象地位十分重要,原因在于,如果去掉它们,特定用例的逻辑就会被放到实体中。然而,实体会被多个用例使用,因此它们会有通用的用法。特定用例的逻辑如果被放到了实体中,就能被多个边界使用,这些边界最终会把这个实体当成通用逻辑。而我们就会不得不修改这个实体让它适应另一个边界,这会增加实体的复杂性而且可能会破坏用到该实体的其它用例。

◐ 为什么是三种对象类型?

当时,Jacobson 宣称其它的 OO 方法会把所有的职责都放在实体本身,但是他(和他的支持者)则倾向与将这些职责分散到三种对象类型中,因为这样能让系统更适应变化。

[…] 所有的系统都会发生变化。因此,只有所有的变化都发生在局部,稳定性才会存在,也就是说,变化最好只能影响系统中的一个对象。—— Ivar Jacobson 1992, pg. 135

通过职责的封装将系统的变化控制在局部,就是 EBI 架构的目标。我们仔细思考一下,Jacobson 只是没有直接说出十年之后由 Robert C. Martin 在他的 “Agile Software Development, Principles, Patterns, and Practices”一书中提出的单一职责原则罢了。

◐ 总结

和 MVC 模式中的 Model 代表着整个后端(包括所有实体、服务和它们之间的关系在内的一切)一样,EBI 模式将边界看作是和外部世界的完整连接,而不仅仅是一个视图、一个控制器或是一个接口(这里指的是编程语言结构的接口)。边界代表了对应着 MVC 中的 View 和 Controller 的整个展现层。EBI 中的实体代表了承载着数据及其行为的真正实体,而交互器对象代表了展现层和实体之间的连接,也就是我所谓的应用服务和领域服务。

EBI 模式关注后端而 MVC 更关注前端。它们不能互相取代,它们是对方的补充。如果把它们放在一个模式中,我们可以把它叫做视图-控制器-交互器-实体 (View-Controller-Interactor-Entity)。

◐ 引用来源

  • 1992 – Ivar Jacobson – Object-Oriented Software Engineering: A use case driven approach
  • 2002 – Robert C. Martin – Agile Software Development, Principles, Patterns, and Practices
  • 2002 – Robert C. Martin – Single Responsibility Principle
  • Eclipse Process Framework – Entity-Control-Boundary Pattern
  • Jon Pearce – Implementing Use Cases
  • 2012 – Robert C. Martin – Clean Architecture (NDC 2012)
  • 2014 – Adam Bien – How to tackle JEE
  • 2014 – Ali Parvini – Model View Controller vs Boundary Control Entity

张逸注:不得不说,现在回头看Jacobson提出的EBI,真的是遵循了极简主义,用最简单的模型说清楚了对象范式进行建模的本质。不错,六边形架构似乎更为对称,然则,端口和适配器二者结合起来其实就是边界对象要做的工作:端口是边界的抽象,适配器是边界的实现。

我个人提出的菱形对称架构脱胎于整洁架构思想与六边形架构,实际上也可以认为是对DDD中上下文映射模式的运用,北向网关是开放主机服务,南向网关是防腐层,正是因为菱形对称架构的思想更贴近于DDD,与限界上下文也更加匹配,我才不揣冒昧地在我的书中提出了自己总结的模式。

从本质上讲,无论菱形对称架构的北向网关还是南向网关,其实都是EBI中的边界对象。本文原作者认为应用服务是控制对象(交互器),我并不这么认为。应用服务在面向进程内的调用时,它扮演的角色其实与作为支持跨进程调用的远程服务没有什么区别,都是边界。领域服务才是控制对象。

结合菱形对称架构与业务服务,我总结了角色构造型,它与EBI的映射关系如下所示:

  • 边界:北向网关中的远程服务与应用服务,南向网关中的端口与适配器
  • 控制:领域服务、工厂
  • 实体:封装了实体与值对象的聚合

我将资源库放到了南向网关的端口,这一做法并不符合Eric对资源库的定义(他将资源库放到了领域层);然而,根据边界对象的定义——“所有依赖系统环境(工具和传达机制)的功能都属于边界对象”——资源库是对资源访问的一种抽象,将其视为边界对象更加合理,本质上,它和访问第三方接口的客户端,以及发布事件的发布者并无本质差别。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-04-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 逸言 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档