landingi / 模块化单体捆绑
Landingi 模块化单体捆绑
Requires
- php: >=7.4
- symfony/framework-bundle: ^5.2
Requires (Dev)
- landingi/php-coding-standards: ^1.0
- phpunit/phpunit: ^9.5
- roave/security-advisories: dev-master
This package is auto-updated.
Last update: 2024-09-18 16:37:06 UTC
README
模块化单体中的模块
从模块化单体的角度来看,避免模块之间的代码级别耦合是很重要的。
通信
模块化单体中特定模块之间的通信不应引入代码级别的耦合。每个模块必须被视为系统的一个独立元素(我们甚至可以说每个模块都是单体内部的微服务)。为了满足这些要求,模块之间的通信可以有两种方式,具体取决于特定需求。
通信方式借鉴了Bottega(Devmentors)在其用.NET技术编写的示例模块化单体中提出的模型。这个想法被吸收并适应到PHP技术(不幸的是,并没有像.NET那样做得那么优雅和通用)。
- (未完成)我们可以从模块的角度对公共事件(应用/集成)做出反应。
- (完成)我们可以使用类似于Http协议的机制向特定模块发送请求。此实现现已准备好并可供使用。整个过程完全在内存中进行,这保证了高性能。
在事件驱动架构中,通信主要通过集成事件进行。然而,并非所有内容都可以由事件驱动。这些主要是包含通用上下文,可以被多个模块使用的模块。在这里,“模块请求”帮助我们。
1. 集成/应用事件
(待实现)
2. 模块请求
使用我们所说的“模块请求”在模块之间进行请求的背后的想法是,能够在模块化单体内部像分布式架构中的微服务那样进行模块间的通信。这允许我们在不引入系统特定元素之间的黑客攻击和耦合的情况下运行项目。此外,该实现还允许在必要时轻松地将特定模块提取为微服务。
当然,当前实现只是开始。它支持基本假设,但可能还有改进的空间。当前实现是在内存中进行的,如果有一天将某个模块拉出作为超过单体的微服务,则应添加新的ModuleClient实现以支持通过基础设施进行通信。
2.1.它是如何工作的?
任何希望将某些资源供其他模块使用的模块都应该在特定路径(路径应类似于HTTP的格式,例如:“module/acl/v1/is-functionality-granted”)添加“请求处理器”。此时,以与我们在通过HTTP API进行API时相同的方式订阅所有前往此路径的请求,除了默认情况下所有事情都在内存中发生(当然,像之前提到的,提取模块作为微服务并更改通信不是问题)。
因此,我们使用具有给定请求路径的tag: app.module.handler
在Symfony DI中注册处理器:module/acl/v1/is-functionality-granted
Acl\Application\ModuleRequest\Handler\IsFunctionalityGrantedHandler: arguments: ['@Acl\Application\Service\AccessService'] tags: - { name: 'app.module.handler', path: 'module/acl/v1/is-functionality-granted' }
此处理器将处理对ACL的请求以检查其他模块的权限,并且与HTTP API完全相同。
然后,从另一个模块的级别来看,例如灯箱模块,我们可以轻松地引入ACL检查服务(记住,每个模块都是一个独立的实体)。在这个服务中,我们将通过ModuleClient
调用我们创建的处理程序,而不需要进行代码级别的耦合。
use SharedKernel\Application\Module\GenericMessage; $accountUuid = 'c87ceb7b-4272-4126-abeb-1d778ff89ed2'; $userUuid = '639e4f08-d176-4c15-9f2f-94ad69d6ccd9'; $functionalityPermission = 'lightboxes.view_list'; $result = $this->moduleClient->request( 'module/acl/v1/is-functionality-granted', new GenericMessage( [ 'account_uuid' => (string) $accountUuid, 'user_uuid' => (string) $userUuid, 'permission' => $functionalityPermission ] ) );
在$result
变量中,我们将简单地获取我们的处理程序结果。这与我们调用HTTP API的方式完全相同。