krak / effects
在领域层内安全地管理副作用。
v0.3.1
2021-02-05 16:07 UTC
Requires
- php: >=7.2
Requires (Dev)
- phpunit/phpunit: ^8.5|^9.3
- prewk/result: ^2.1
- vimeo/psalm: ^4.3
README
效果库是一组小型实用工具,帮助启用代码中的副作用,使用PHP生成器来转移所有权。
这有助于领域驱动设计和维护纯净的领域模型。
使用方法
<?php use function Krak\Effects\{handleEffects, expect}; // Domain Entity final class ShoppingCart { public function checkOut(CheckOutShoppingCart $checkOutShoppingCart) { // ... build up captueCharge command $capturedCharge = expect(CapturedCharge::class, yield new CaptureCharge(/* args */)); } } // Domain Commands/Effects final class CheckOutShoppingCart {} final class CaptureCharge {} final class CapturedCharge {} // Application Command Handler final class HandleCheckOutShoppingCart { public function __invoke(CheckOutShoppingCart $checkOutShoppingCart): void { $shoppingCart = $this->shoppingCarts->get($checkOutShoppingCart->shoppingCart()); handleEffects($shoppingCart->checkOut($checkOutShoppingCart), [ CaptureCharge::class => function(CaptureCharge $captureCharge) { return $this->paymentGateway->capture($captureCharge); // returns a CapturedCharge instance } ]); } }
工作原理
这是通过利用PHP生成器允许将值发送回产生结果的事实来工作的。`handleEffects` 函数只是简单地迭代领域方法,拉取所有命令,将它们传递给命令处理映射,然后将响应发送回领域方法。
期望函数只是一个安全辅助工具,提供类型自动完成,并在出现映射错误时断言期望的类,以便使调试更加愉快。技术上这不是必需的,所以如果你不关心psalm和PHPStorm的自动完成帮助,那么请随意使用`yield`关键字而不使用`expect`函数。
使用yield from嵌套效果
如果你需要一个方法引发几个效果,那么可能有必要有专门的方法来管理和引发这些效果。
你可以使用`yield from`语句从子方法引发效果。以下是一个例子
final class Product { public function checkout() { yield from $this->raiseEffects(); } private function raiseEffects() { $result = yield new Effect1(); } }
Prewk\Result集成
如果你正在处理更复杂的领域方法/服务,将代码的各个部分结构化成独立的函数,这些函数返回结果并可以短路操作(就像使用正常的Result类一样),这可能是有帮助的。
让我们看看如何使用MapEffectResults类来实现这一点。
use Prewk\Result; use Krak\Effects\Bridge\Result\MapEffectResults; use function Krak\Effects\expect; final class Product { public function syncInventory() { expect(Result::class, yield from MapEffectResults::map( $this->fetchInventoryFromERP(), $this->fetchPricingRules(), $this->pushInventoryToThirdParty() ))->mapErr(function() { // set some error state maybe. })->map(function() { // set some success state maybe. }); } public function fetchInventoryFromERP(){ return function() { return expect(Result::class, yield new FetchInventoryFromERP($this->productId)); }; } public function fetchPricingRules(){ return function(InventoryFromERP $inventoryFromERP) { return expect(Result::class, yield new FetchPricingRulesForProduct($this->productId)) ->map(function(PricingRules $pricingRules) use ($inventoryFromERP) { return [$inventoryFromERP, $pricingRules]; }); }; } public function pushInventoryToThirdParty() { return function(array $tup) { [$inventoryFromERP, $pricingRules] = $tup; // calculate final inventory using special logic return expect(Result::class, yield new PushInventoryToThirdParty($finalInventory)); }; } } // in some application service \Krak\Effects\handleEffects($product->syncInventory(), []); // with handlers accordingly
安装
使用composer在krak/effects
中安装
灵感
这种设计受到了Elm语言设计的影响,即在保持纯应用代码的同时,将副作用留给运行时管理。
以下是一些关于领域模型纯净性和副作用的其它有帮助的资源