解耦,未解耦的区别

This article would not be possible without the help of Rodrigo Jardim da Fonseca, Edison Junior, and Lemuel Roberto.

没有Rodrigo Jardim da Fonseca, Edison Junior和Lemuel Roberto的帮助,这篇文章是不可能的。

Disclaimer: I feel like I should address that the architecture I’m about to present already existed when I arrived at Arquivei, almost two years ago and didn’t change much since. I didn’t create it, but I did learn a lot from it. Hopefully, you will too.

免责声明:我觉得我应该说的是,大约两年前到达Arquivei时,我将要介绍的体系结构已经存在。 我没有创建它,但是我确实从中学到了很多。 希望你也会。

I don’t think I need to sell you on the idea of an architecture that enables your team to build decoupled solutions, but if I do, here are the basics:

我认为我不需要向您推销可以使您的团队构建分离的解决方案的体系结构的想法,但是如果我这样做,这里是基础知识:

  • Separation of concerns: you get pieces of software that are easier to understand (and maintain), not only because of their size but also because each piece only does one job.关注点分离:您会获得易于理解(和维护)的软件,不仅因为它们的大小,而且因为每个软件只能完成一项工作。
  • Testability: automated tests (especially unit tests) are essential to ensure quality in delivery. The trend for continuous deployments with shorter intervals between them made it almost impossible for an application to be successful if it’s hard to create tests for it.可测试性:自动化测试(尤其是单元测试)对于确保交付质量至关重要。 连续部署的趋势是它们之间的间隔较短,因此如果很难为其创建测试,则几乎不可能使应用程序成功。

With that in mind, when you look for a solution you’re likely to land on the well-known pages of Alistair Cockburn’s Hexagonal Architecture and Jeffrey Palermo’s Onion Architecture. If your journey is anything like ours, you may also find yourself reading about the five SOLID principles and Uncle Bob’s Clean Architecture, that pretty much sums it all. Those will give you a theoretical foundation to build what you need and it may look very different from what I’m about to show you, but the problem of a decoupled architecture is not a problem with a single solution. So, how do we do it at Arquivei?

考虑到这一点,当您寻找解决方案时,您可能会进入Alistair Cockburn的Hexagonal Architecture和Jeffrey Palermo的Onion Architecture的知名页面。 如果您的旅程像我们一样,您可能还会发现自己了解了SOLID的五项原则和Bob叔叔的Clean Architecture ,几乎可以全部概括。 这些将为您提供构建所需内容的理论基础,它看起来可能与我要向您展示的内容完全不同,但是架构分离的问题并不是单个解决方案的问题。 那么,我们如何在Arquivei做到这一点?

First of all, our application is divided into two layers: app and core. The app layer holds all vendor-specific code: infrastructure, adapters, and framework utilities while in the core layer you will find pure PHP code that handles our business logic. Most changes will affect the contents of app, but only changes in requirements should affect the code living in core.

首先,我们的应用程序分为两层: appcore应用程序层包含所有特定于供应商的代码:基础结构,适配器和框架实用程序,而在核心层中,您将找到处理我们的业务逻辑的纯PHP代码。 大多数更改将影响应用程序的内容,但只有需求的更改会影响核心代码。

One important aspect of this division is that all contracts are specified by the core layer: gateways, requests, responses, and entities. The pieces on the app side will follow these contracts by implementing interfaces and handling the entities to produce the result expected from them.

这种划分的一个重要方面是所有合同都由核心层指定:网关,请求,响应和实体。 应用程序端的各个部分将通过实现接口并处理实体以产生预期的结果来遵循这些合同。

The main class in the core layer is called UseCase and it represents, well, a use case of the application (creating a user, for example). It is the only point of interaction with the world outside of core. Its input and output are specified by the Request and Response classes respectively. To use it, we need to pass all the dependencies required to the constructor and call its execute() with request. The method returns a response whenever one is needed.

核心层中的主类称为UseCase,它很好地表示了应用程序的用例(例如,创建用户)。 这是与核心之外的世界进行交互的唯一点。 它的输入和输出分别由Request和Response类指定。 要使用它,我们需要将所有必需的依赖项传递给构造函数,并通过请求调用其execute() 。 该方法在需要响应时返回一个响应。

Figure 2: An example of a core folders structure with the user creation module. More modules can be added as required.
图2:带有用户创建模块的核心文件夹结构示例。 可以根据需要添加更多模块。
<?phpnamespace Arquivei\Boltons\Example\Modules\User\Creation;use Arquivei\Boltons\Example\Modules\User\Creation\Gateways\CheckUniqueEmailGateway;
use Arquivei\Boltons\Example\Modules\User\Creation\Gateways\UserSaveGateway;
use Arquivei\Boltons\Example\Modules\User\Creation\Requests\Request;
use Arquivei\Boltons\Example\Modules\User\Creation\Responses\Response;
use Arquivei\Boltons\Example\Modules\User\Creation\Rules\CheckUniqueEmailRule;
use Arquivei\Boltons\Example\Modules\User\Creation\Rules\UserSaveRule;
use Arquivei\Boltons\Example\Modules\User\Creation\Rulesets\Ruleset;class UseCase
{private $checkUniqueEmailGateway;private $userSaveGateway;public function __construct(CheckUniqueEmailGateway $checkUniqueEmailGateway,UserSaveGateway $userSaveGateway) {$this->checkUniqueEmailGateway = $checkUniqueEmailGateway;$this->userSaveGateway = $userSaveGateway;}public function execute(Request $request): Response{$ruleset = new Ruleset(new CheckUniqueEmailRule($this->checkUniqueEmailGateway,$request->getUser()->getEmail()),new UserSaveRule($this->userSaveGateway,$request->getUser()));return $ruleset->apply();}
}

Sometimes your use case is not simple. It is very common for us to retrieve data, process it in a few different steps (like generating PDF files and then a ZIP with all of them) before presenting a response. To avoid clutter in the UseCase::execute() method, we use a Ruleset.

有时,您的用例并不简单。 在呈现响应之前,我们通常会先检索数据,然后按照几个不同的步骤对其进行处理(例如生成PDF文件,然后将其全部包含为ZIP)。 为了避免UseCase :: execute()方法中的混乱,我们使用规则集。

The Ruleset class takes however many Rule objects we need (hopefully each with a single responsibility) and orchestrates how the rules are applied. A response is built with the result and returned.

规则集类负责然而,许多规则对象,我们需要(希望每一个责任)和编排规则是如何应用的。 使用结果构建响应并返回。

<?phpnamespace Arquivei\Boltons\Example\Modules\User\Creation\Rulesets;use Arquivei\Boltons\Example\Modules\User\Creation\Responses\Response;
use Arquivei\Boltons\Example\Modules\User\Creation\Rules\CheckUniqueEmailRule;
use Arquivei\Boltons\Example\Modules\User\Creation\Rules\UserSaveRule;class Ruleset
{private $checkUniqueEmailRule;private $userSaveRule;public function __construct(CheckUniqueEmailRule $checkUniqueEmailRule,UserSaveRule $userSaveRule) {$this->checkUniqueEmailRule = $checkUniqueEmailRule;$this->userSaveRule = $userSaveRule;}public function apply(): Response{$this->checkUniqueEmailRule->apply();$userId = $this->userSaveRule->apply();return new Response($userId);}
}

The constructor of each rule will take all it needs for the rule to do its job: dependencies and request data and will perform its task. If an exception is encountered, it is to be wrapped in a core-specific exception and thrown. This makes it easier for us to track which part of our application failed. Consider how easier it is to understand what is going on when you come across a UsernameNotAvailableException than a PDOException.

每个规则的构造函数都将使用规则完成其工作所需的全部:依赖关系和请求数据,并将执行其任务。 如果遇到异常,则将其包装在特定内核的异常中并引发。 这使我们更容易跟踪应用程序的哪个部分失败。 考虑一下,遇到UsernameNotAvailableException时PDOException来了解发生了什么事情要容易得多

<?phpnamespace Arquivei\Boltons\Example\Modules\User\Creation\Rules;use Arquivei\Boltons\Example\Modules\User\Creation\Entities\User;
use Arquivei\Boltons\Example\Modules\User\Creation\Exceptions\UserSaveException;
use Arquivei\Boltons\Example\Modules\User\Creation\Gateways\UserSaveGateway;class UserSaveRule
{private $userSaveGateway;private $user;public function __construct(UserSaveGateway $userSaveGateway,User $user) {$this->userSaveGateway = $userSaveGateway;$this->user = $user;}public function apply(): string{try {return $this->userSaveGateway->save($this->user);} catch (\Throwable $t) {throw new UserSaveException('Error saving user', 500, $t);}}
}

You probably noticed that in the example above that the constructor does not take a database adapter, but a Gateway. Gateways are interfaces that dependencies must implement, that way we know they will follow the contract that the core layer specified and all the effort in switching vendors, for example, will be that of writing a new adapter and changing the calling code (usually a controller) to use it. Also, note that the core layer does not know about the data types returned by your database because it uses the entities defined within itself. App always adapts to core, never the other way around.

您可能已经注意到,在上面的示例中,构造函数没有采用数据库适配器,而是采用了网关。 网关是依赖项必须实现的接口,这样我们就知道它们将遵循核心层指定的约定,并且交换供应商的所有工作,例如,将是编写新的适配器并更改调用代码(通常是控制器) )使用它。 另外,请注意, 核心层不知道数据库返回的数据类型,因为它使用自身定义的实体。 App始终适应核心 ,而别无所求。

<?phpnamespace Arquivei\Boltons\App\Example\Adapters;class DatabaseAdapter implements CheckUniqueEmailGateway, UserSaveGateway
{private $takenEmails = ['test@test.com', 'newtest@test.com'];public function save(User $user): string{if (((int) date('s')) % 2 == 1) {throw new \Exception('Cannot save user because the current time is odd');}return date('is');}public function isEmailTaken(string $email): bool{return in_array($email, $this->takenEmails);}
}

The last piece of our puzzle is the testing and with this architecture, it is pretty simple: mock the gateways, create a request, and assert on the response. PHPUnit has utilities to ensure the flow works as expected and even the failure scenarios can easily be tested for 100% code coverage on your business logic. Keep in mind that you still need to be smart about your test cases: coverage is just one way to help you make sure you did everything you needed.

最后一个难题是测试,并使用这种架构,这非常简单:模拟网关,创建请求并在响应中进行声明。 PHPUnit具有实用程序,可确保流程按预期工作,甚至可以轻松测试失败场景,以确保业务逻辑上100%的代码覆盖率。 请记住,您仍然需要对测试用例保持精明:覆盖率只是帮助您确保已完成所需一切的一种方法。

It is also worth noting that some people in our team prefer to build tests for every class as they write tests alongside application code. Nothing wrong with that approach if it works for you.

还值得注意的是,我们团队中的某些人喜欢在为每个类编写测试以及应用程序代码的同时为其构建测试。 如果这种方法对您有用,那没什么错。

<?phpnamespace Arquivei\Boltons\Example\Tests;use Arquivei\Boltons\Example\Modules\User\Creation\Entities\User;
use Arquivei\Boltons\Example\Modules\User\Creation\Exceptions\EmailTakenException;
use Arquivei\Boltons\Example\Modules\User\Creation\Exceptions\UserSaveException;
use Arquivei\Boltons\Example\Modules\User\Creation\Gateways\CheckUniqueEmailGateway;
use Arquivei\Boltons\Example\Modules\User\Creation\Gateways\UserSaveGateway;
use Arquivei\Boltons\Example\Modules\User\Creation\Requests\Request;
use Arquivei\Boltons\Example\Modules\User\Creation\UseCase;
use PHPUnit\Framework\TestCase;class ExampleTest extends TestCase
{public function testSuccess(){$checkUniqueEmailGateway = $this->createMock(CheckUniqueEmailGateway::class);$checkUniqueEmailGateway->expects($this->once())->method('isEmailTaken')->willReturn(false);$userSaveGateway = $this->createMock(UserSaveGateway::class);$userSaveGateway->expects($this->once())->method('save')->with($this->callback(function ($user) {$this->assertSame('name', $user->getName());$this->assertSame('email', $user->getEmail());$this->assertSame('phone', $user->getPhone());return true;}))->willReturn('1');$useCase = new UseCase($checkUniqueEmailGateway, $userSaveGateway);$request = new Request(new User('name', 'email', 'phone'));$response = $useCase->execute($request);$this->assertSame('1', $response->getUserId());}public function testEmailTakenError(){$this->expectException(EmailTakenException::class);$checkUniqueEmailGateway = $this->createMock(CheckUniqueEmailGateway::class);$checkUniqueEmailGateway->expects($this->once())->method('isEmailTaken')->willReturn(true);$userSaveGateway = $this->createMock(UserSaveGateway::class);$useCase = new UseCase($checkUniqueEmailGateway, $userSaveGateway);$request = new Request(new User('name', 'email', 'phone'));$response = $useCase->execute($request);}public function testSaveError(){$this->expectException(UserSaveException::class);$checkUniqueEmailGateway = $this->createMock(CheckUniqueEmailGateway::class);$checkUniqueEmailGateway->expects($this->once())->method('isEmailTaken')->willReturn(false);$userSaveGateway = $this->createMock(UserSaveGateway::class);$userSaveGateway->expects($this->once())->method('save')->willThrowException(new \Exception('Cannot save'));$useCase = new UseCase($checkUniqueEmailGateway, $userSaveGateway);$request = new Request(new User('name', 'email', 'phone'));$response = $useCase->execute($request);}
}

There’s a lot of questions we ask ourselves in our weekly meetings about this architecture and you may have some right now. It can often seem like too much or like it doesn’t fit your problem and we don’t have all the answers. This is never ending work in progress that has helped us achieve a good level of decoupling and made our applications way easier to create and maintain. I hope it gives you some insights.

在每周一次的会议上,关于此体系结构我们有很多问题要问,您现在可能有一些疑问。 它通常看起来太多或不适合您的问题,并且我们没有所有答案。 这永无止境的进行中的工作已帮助我们实现了良好的去耦水平,并使我们的应用程序更易于创建和维护。 我希望它能给您一些见识。

If you want a more detailed example, you can find it here.

如果您需要更详细的示例,可以在此处找到。

Thanks for reading.

感谢您的阅读

翻译自: https://medium.com/engenharia-arquivei/a-decoupled-php-architecture-inspired-by-the-clean-architecture-788b30ab52c2

解耦,未解耦的区别


http://www.taodudu.cc/news/show-3883115.html

相关文章:

  • Java中解耦测试分析
  • 分层软件架构及其数据解耦
  • 如何实现业务解耦?spring中事件监听了解一下
  • 库存转换是什么意思_什么是供应链中的解耦点Decoupling point?它到底有啥用?...
  • php什么是耦合关系,耦合与解耦 · iThinkphp完全开发手册 · 看云
  • 什么是解耦?
  • 心形图Python代码详细解析
  • (自创)世界上最美丽浪漫的函数组合
  • 逐行分析如何用C语言输出心形图案(详细教学)
  • 三维旋转心形图(matlab 含源码及注释)
  • 抖音上的c语言动态爱心代码,教程:利用Excel 制作 抖音上的心形动态函数图像 ,可以用来表白哈...
  • matlab心形图大全,几个经典的函数图像,有趣的函数图像,matlab画图
  • 【C语言】心形函数
  • 测试狗:公司已与赛默飞组建联合实验室,引进FIB聚焦离子束
  • 氮化物 SiN-Ni/AlN-Ni复合材料|氮化铝颗粒增强铝基复合材料|纳米氮化铝/纳米铝双纳米复合材料|多元硼硅酸玻璃+AlN低温共烧陶瓷材料
  • TOF-SIMS测试常见的问题及解答(一)
  • DDA数据长啥样
  • 《葵花宝典》实验室前辈总结的液质联用经验!
  • gcms基峰有什么用_干货!气相色谱仪常见故障及维护知识集锦
  • 请概述计算机技术的发展是现代核磁共振光谱法得以应用的关键,仪器分析复习题...
  • SIMS(secondary ion mass spectroscopy)二次离子质谱
  • 2021-12-04 micropython esp32 cam 照相并上传服务器, 参考贴 ,二进制字符串MQTT传输和转换,获取字典键的技巧, 4G MQTT 串口分段传送大文件
  • TCP通信Socket编程----传输不同数据类型。
  • 最详细Android连接远程的MySQL数据库实例
  • 连接oracle数据库经常遇到错误汇总
  • 【USB】Android实现读写USB串口数据
  • 运行循环(Run Loops)
  • 使用Java实现串口通信(二)
  • 数据库连接之jdbc连接池
  • 【HuoLe的面试凉经】

解耦,未解耦的区别_受干净架构启发的解耦php架构相关推荐

  1. 分子 原子 电子 质子_受质子碰撞启发的大量数据文本挖掘

    分子 原子 电子 质子 Many of us trapped in our increasingly disheveled home offices realize the difficulty in ...

  2. 解耦,未解耦的区别_幂等与时间解耦之旅

    解耦,未解耦的区别 HTTP中的幂等性意味着相同的请求可以执行多次,效果与仅执行一次一样. 如果用新资源替换某个资源的当前状态,则无论您执行多少次,最终状态都将与您仅执行一次相同. 举一个更具体的例子 ...

  3. 微服务 松耦合_超值干货:微服务架构下如何解耦,对于已经紧耦合下如何重构?...

    今天准备谈下微服务架构下各个微服务间如何解耦,以及对于已经紧耦合的微服务如何进行重构.要明白实际上微服务后续出现的诸多问题往往都是一开始微服务模块划分就不合理导致,对于具体的模块划分方法和原则,我总结 ...

  4. java未将对象引用设置_未将对象引用到实例怎么解决_常见问题解析,java

    PPT导入GIF图无法播放_常见问题解析 PPT导入GIF图无法播放,是因为PPT保存时会自动压缩图片,所以导致GIF图片动画效果就失效,解决方法进入图片工具栏,在"压缩图片"的& ...

  5. 架构 | 微服务架构下如何解耦,对于已经紧耦合下如何重构?

    点击上方"朱小厮的博客",选择"设为星标" 当当满200减40优惠码「J2KNAE」 来源:知乎 今天准备谈下微服务架构下各个微服务间如何解耦,以及对于已经紧耦 ...

  6. 代码重新发布后docker服务会不会受影响_分享点经验 | 浅谈微服务架构

    点击蓝字关注我们 AMP 背景简介 在最原始的系统设计中,我们通常使用单体架构.单体架构把所有的业务逻辑都写在一起,没有对业务场景进行划分.在规模比较小的情况下工作情况良好,但是随着系统规模的扩大,它 ...

  7. zoho邮箱收费和免费区别_您需要了解有关适用于ios和android的新zoho vault移动应用程序的所有信息...

    zoho邮箱收费和免费区别 The secret phrase is the true standard of computerized validation and access. Any run ...

  8. 单体 soa 微服务 区别_漫谈何时从单体架构迁移到微服务?

    面对微服务如火如荼的发展,很多人都在了解,学习希望能在自己的项目中帮得上忙,当你对微服务的庐山真面目有所了解后,接下来就是说服自己了,到底如何评估微服务,什么时候使用微服务,什么时间点最合适,需要哪些 ...

  9. bs cs架构区别_软件架构设计分层模型和构图思考

    今天谈下架构设计中的分层思维和分层模型以及基于分层思维下的架构构图逻辑. 架构思维概述 对于架构思维本身仍然是类似系统思维,结构化思维,编程思维等诸多思维模式的一个合集.由于架构的核心作用是在业务现实 ...

最新文章

  1. webpack之 loader
  2. 开源医学图像数据集(资源整合)
  3. [M]MagicTable转换异常解决方法
  4. 在HTML中怎么去掉超链接的下划线?
  5. iview vue 打包图标不显示_VueCLI3.0干货系列之集成iview
  6. 测试与开发的冲突举例
  7. 【发表案例】JCR1区计算机测量类SCI,仅3个月录用
  8. 三种分布式爬虫系统的架构方式
  9. word公式快捷键使用
  10. 一元线性回归方程的参数估计
  11. [noip2016]天天爱跑步
  12. 2022-C4-AI-基于飞桨的智慧书桌系统
  13. 大疆产品上岸经验分享
  14. 毛球科技论述区块链之符号理论(下)
  15. 简介:cs224n 2022 winter [Chris Manning]
  16. 02- web UI测试与UI Check List
  17. 3a企业信用等级证书怎么办理
  18. (深入.Net平台和C#编程)第五章.体检套餐管理项目.20170408
  19. 如何利用百度图片搜索进行引流?原理是怎样的?
  20. Python魔法方法指南

热门文章

  1. android实战简易教程-链接
  2. STL容器之<vector>
  3. python中try怎么用_python中的try的用法
  4. 在windows上安装配置msys2
  5. js运算符功能和运算规则
  6. 视频编解码技术发展趋势
  7. 邮件送达率低的六大症结
  8. STM32MP135利用ubuntu或PC端环境搭建,Device is under read out protion!解决方案,用program下载出现乱码解决
  9. 《遇见尊上》4.1上线链游玩家|不甘天命、恋爱修仙
  10. strtoul函数的使用