v0.4.0 2019-04-20 07:22 UTC

This package is auto-updated.

Last update: 2024-09-06 02:12:14 UTC


README

Build Status Scrutinizer Code Quality

是什么?

Tale 是一个小型库,帮助跨多个服务编写“分布式事务类似”的对象。它基于 saga 模式。Couchbase 博客上有一篇很好的介绍:[https://blog.couchbase.com/saga-pattern-implement-business-transactions-using-microservices-part/](https://blog.couchbase.com/saga-pattern-implement-business-transactions-using-microservices-part/)

安装

composer require mead-steve/tale

示例用法

该示例的一个用例是一些假日预订软件,被分解为几个服务。

假设我们有以下服务:航班预订 API、酒店预订 API 和客户 API。

我们将编写以下步骤

class DebitCustomerBalanceStep implements Step
{
    //.. Some constructor logic for initialising the api etc...
    
    public function execute(CustomerPurchase $state)
    {
        $paymentId = $this->customerApi->debit($state->Amount);
        return $state->markAsPaid($paymentId);
    }

    public function compensate($state): void
    {
        $this->customerApi->refundAccountForPayment($state->paymentId)
    }
class BookFlightStep implements Step
{
    //.. Some constructor logic for initialising the api etc...
    
    public function execute(FlightPurchase $state)
    {
        $flightsBookingRef = $this->flightApi->buildBooking(
            $state->Destination, 
            $state->Origin,
            self::RETURN,
            $this->airline
        );
        if ($flightsBookingRef=== null) {
            raise \Exception("Unable to book flights");
        }
        return $state->flightsBooked($flightsBookingRef);
    }

    public function compensate($state): void
    {
        $this->customerApi->cancelFlights($state->flightsBookingRef)
    }

对于任何需要的步骤,以此类推。然后在处理用户请求的任何东西中,可以构建一个分布式事务

       $transaction = (new Transaction())
            ->add(new DebitCustomerBalance($user))
            ->add(new BookFlightStep($airlineOfChoice))
            ->add(new BookHotelStep())
            ->add(new EmailCustomerDetailsOfBookingStep())

        $result = $transaction
            ->run($startingData)
            ->throwFailures()
            ->finalState();

如果任何步骤失败,则会以相反的顺序调用每个步骤的补偿方法,直到一切都撤销。

状态不可变性

当前状态从一步传递到下一步。相同的州还用于补偿事务中更远处的失败。由于这种情况,实现者考虑使状态不可变是很重要的。

Tale 提供了一个 CloneableState 接口来帮助实现这一点。任何实现此接口的状态都将在其 cloneState 方法被调用之前传递给步骤,以确保步骤不会共享同一状态。

        class FakeState implements CloneableState
        {
                public function cloneState()
                {
                    return clone $this;
                }
        }
        
        $stepOne = new LambdaStep(
            function (MyStateExample $state) {
                $state->mutateTheState = "step one"
                return $state;
            }
        );
        $stepTwo = new LambdaStep(
            function (MyStateExample $state) {
                $state->mutateTheState = "step two"
                return $state;
            }
        );
        $transaction = (new Transaction())
            ->add($stepOne)
            ->add($stepTwo);

        $startingState = new MyStateExample();
        $finalState = $transaction->run($startingState)->finalState();

在上面的示例中,$startingState$finalState$state 给出的所有函数调用都是彼此的克隆,所以改变一个不会影响任何早期状态。

测试 / 开发

欢迎贡献。如果更改较大或会破坏向后兼容性,请首先打开一个问题。

所有构建必须在合并之前通过 travis 测试。运行 ./run_tests.sh 将运行与 travis.yml 中相同的测试,但本地运行。

dockerfile 提供了一个可以执行所有测试和静态分析的执行环境。