burzum / cakephp-service-layer
为CakePHP提供的服务层和领域/业务模型实现
Requires
- php: >=8.1
- cakephp/cakephp: ^5.0.0
Requires (Dev)
- cakephp/cakephp-codesniffer: ^4.5
- dereuromark/cakephp-ide-helper: ^2.0.0
- phpstan/phpstan: ^1.0.0
- phpunit/phpunit: ^10.2.0
README
这更像是一种设计模式和概念性想法,而不是大量的代码,这将提高您代码库的可维护性。此插件仅提供一些类来帮助您在遵循框架约定优于配置的方式下,在CakePHP框架中应用此概念。
支持的CakePHP版本
此分支用于与CakePHP 5.0+一起使用。有关详细信息,请参阅版本映射。
简介
任何MVC框架的黄金法则基本上是“胖模型,瘦控制器”。
虽然这效果很好,但可以通过分离,例如将数据库操作从实际的业务逻辑中分离出来来进一步抽象化。大多数Cake开发者可能将表对象用作所有事物的容器。从严格意义上讲,这是不正确的。业务逻辑不应该属于数据库表的环境,而应与任何持久层分离。CakePHP喜欢将持久性混合到业务逻辑中。非常出色的业务逻辑应该对任何框架都是无感知的。您只需使用框架来持久化业务逻辑的结果。
表对象应仅封装与该表直接相关的任何内容。与该表相关的查询、自定义查找器等。我们想要遵循的一些原则是关注点分离和单一职责原则。CakePHP中的Model
目录代表数据模型,不应将其用于添加与此关注点无关的内容。服务层有助于解决这个问题。
服务类是一个自定义的类,不是CakePHP框架的一部分,它将实现真正的业务逻辑,执行任何类型的计算或其他逻辑操作,并将结果传递回控制器,然后控制器将结果传递给视图。
这确保了代码的每个部分都很容易测试和交换。例如,服务也可以在shell应用中使用,因为它不依赖于控制器。如果很好地分离,理论上可以在两个应用之间共享一个包含所有表对象的插件,因为特定于每个应用的应用逻辑将在服务层中实现,而不是在表对象中。
Martin Fowler的书籍"企业应用架构模式"中提到
回答起来可能更容易的问题是,在什么情况下不需要使用它。如果您的应用程序的业务逻辑只有一个客户端类型 - 例如,用户界面 - 并且其使用场景响应不涉及多个事务性资源,那么您可能不需要服务层 [...]
但是,一旦您设想了第二种类型的客户端或使用场景响应中的第二种事务性资源,从一开始就在设计中考虑服务层就会有所裨益。
具有意见
在此页面上的一个简单的段落解释了为什么DDD在MVC中是一个非常抽象且具有争议的话题
根据埃里克·埃文斯(Eric Evans)的观点,领域驱动设计(DDD)不是一种技术或方法。它是一种不同的思维方式,用于组织应用程序和构建代码结构。这种思维方式与流行的MVC架构非常契合。领域模型提供了系统的结构视图。大多数时候,应用程序不会改变,改变的是领域。然而,MVC并没有真正告诉你模型应该如何结构化。这就是为什么一些框架不会强迫你使用特定的模型结构,相反,它们允许你的模型随着你的知识和专业技能的增长而演变。
由于这个原因,CakePHP没有提供任何DDD或服务层架构的模板结构。这基本上取决于你自己。
此插件为你提供了一个可能的实现。它不是固定不变的,你也不必同意它。将此插件视为实现的一个建议或模板,以及为关心可维护代码但不知道如何进一步改进其代码库的开发者提供的指导。
如何使用它
CakePHP默认使用定位器而不是依赖注入容器。此插件为你提供了一个带有CakePHP风格的定位器和特性,你可以通过使用特性在应用程序的任何位置简单地加载服务。
以下示例使用SomeServiceNameService
类
use Burzum\CakeServiceLayer\Service\ServiceAwareTrait; class AppController extends Controller { use ServiceAwareTrait; } class FooController extends AppController { public function initialize() { parent::initialize(); $this->loadService('Articles'); } /** * Get a list of articles for the current logged in user */ public function index() { $this->set('results', $this->Articles->getListingForUser( $this->Auth->user('id') $this->getRequest()->getQueryParams() )); } }
如果控制器中已经存在一个与所使用的服务名称相同的属性,则会抛出一个警告。在理想情况下,当使用服务时,你的控制器不必使用任何表实例。表不是控制器关心的。
上述代码的优点是,传递给服务的参数可以来自shell输入或其他来源。逻辑与控制器或模型无关。使用适当的抽象,底层数据源、服务使用的仓库应可以透明地替换为任何匹配所需实现的接口。
你还可以加载命名空间服务
// Loads BarService from MyPlugin and src/Service/Foo/ $this->loadService('MyPlugin.Foo/Bar');
请确保使用文档化的IdeHelper增强功能以获取IDE支持。
有关详细信息,请参阅文档。
为什么没有DI容器?
你可以通过使用你选择的DI容器来实现同样的效果,但在此之前从未真正需要这样做,定位器也工作得很好,而且它们比添加完整的DI容器库更少冗余。在过去大约10年的任何CakePHP应用程序中,包括有500多个表的重大项目,我从未需要添加DI容器。CakePHP的一个核心概念是遵循惯例,而不是在巨大的DI配置中将事物连接在一起,或者在使用容器的地方使用容器,这在大多数情况下只是许多开发者像使用超级全局桶一样使用。
当然,这是一个非常主观的话题,所以如果你不同意并想选择DI容器,请随意!有选择权真是太好了!
CakePHP的DI插件
你可以在Awesome CakePHP插件列表中找到更多的DI插件。
演示
沙盒展示了实时演示。有关详细信息,请查看公开可用的代码。
许可证
版权所有:弗洛里安·克雷默(Florian Krämer)
许可协议:MIT许可证。文件的分发必须保留上述版权声明。