walnut/lib_actionbus

There is no license information available for the latest version (dev-main) of this package.

dev-main 2022-05-13 22:33 UTC

This package is not auto-updated.

Last update: 2024-10-24 10:07:40 UTC


README

This is library that can be used as a Command Bus, Query Bus or any other Bus type.

Examples

In the examples below the following classes are used:

  • For adding a product:
/**
 * @implements Action<bool>
 */
final class AddProductAction implements Action {
   public function __construct(
      public /*readonly*/ string $productId,
      public /*readonly*/ int $amount
   ) {}
}

/**
 * @implements ActionHandler<bool, AddProductAction>
 */
final class AddProductActionHandler implements ActionHandler {
   /**
    * @param AddProductAction $action
    * @return bool
    */
   public function execute(Action $action): bool {
      return true; //@TODO - implement
   }
}
  • For retrieving a product:
final class ProductData {
   public function __construct(
      public /*readonly*/ string $productId,
      public /*readonly*/ string $productName,
      public /*readonly*/ int $amount
   ) {}
}

/**
 * @implements Action<ProductData>
 */
final class GetProductAction implements Action {
   public function __construct(
      public /*readonly*/ string $productId
   ) {}
}

/**
 * @implements ActionHandler<ProductData, GetProductAction>
 */
final class GetProductActionHandler implements ActionHandler {
   /**
    * @param GetProductAction $action
    * @return bool
    */
   public function execute(Action $action): ProductData {
      return new ProductData('p1', 'Test Product', 10); //@TODO - implement
   }
}

Basic Example (Explicit Mapping)

use Walnut\Lib\ActionBus\ActionBus;
use Walnut\Lib\ActionBus\Handler\DefaultActionHandler;
use Walnut\Lib\ActionBus\Handler\Mapper\ArrayActionHandlerMapper;
use Walnut\Lib\ActionBus\Handler\Loader\ContainerActionHandlerLoader;

$defaultActionHandler = new DefaultActionHandler(
    new ArrayActionHandlerMapper([
        AddProductAction::class => AddProductActionHandler::class,
        GetProductAction::class => GetProductActionHandler::class,
    ]),
    new ContainerActionHandlerLoader(getPsrContainer())
);

$actionBus = new ActionBus($defaultActionHandler);
$result = $actionBus->execute(new AddProductAction('p2', 5));

$productName = $actionBus->execute(new GetProductAction('p2'))->productName;

Basic Example (Implicit Mapping)

use Walnut\Lib\ActionBus\ActionBus;
use Walnut\Lib\ActionBus\Handler\DefaultActionHandler;
use Walnut\Lib\ActionBus\Handler\Mapper\NameActionHandlerMapper;
use Walnut\Lib\ActionBus\Handler\Loader\ContainerActionHandlerLoader;

$defaultActionHandler = new DefaultActionHandler(
    new NameActionHandlerMapper(),
    new ContainerActionHandlerLoader(getPsrContainer())
);

$actionBus = new ActionBus($defaultActionHandler);
$result = $actionBus->execute(new AddProductAction('p2', 5));

$productName = $actionBus->execute(new GetProductAction('p2'))->productName;

Using middlewares

use Walnut\Lib\ActionBus\Handler\DefaultActionHandler;
use Walnut\Lib\ActionBus\Handler\Mapper\NameActionHandlerMapper;
use Walnut\Lib\ActionBus\Handler\Loader\ContainerActionHandlerLoader;

$defaultActionHandler = new DefaultActionHandler(
    new NameActionHandlerMapper(),
    new ContainerActionHandlerLoader(getPsrContainer())
);

final class TransactionMiddleware implements ActionHandlerMiddleware {

   public function __construct(
      private /*readonly*/ TransactionContext $transactionContext
   ) {}

   /**
    * @throws Exception
    */
   public function process(Action $action, ActionHandler $actionHandler): mixed {
      try {
         $result = $actionHandler->execute($action);
         $this->transactionContext->saveChanges();
         return $result;
      } catch (Exception $e) {
         $this->transactionContext->revertChanges();
         throw $e;
      }
   }
}

final class ErrorLoggerMiddleware implements ActionHandlerMiddleware {

   public function __construct(
      private /*readonly*/ LoggerInterface $logger
   ) {}

   /**
    * @throws Throwable
    */
   public function process(Action $action, ActionHandler $actionHandler): mixed {
      try {
         return $actionHandler->execute($action);
      } catch (Throwable $e) {
         $this->logger->error($e);
         throw $e;
      }
   }
}

$actionBus = new ActionBus($defaultActionHandler, [
   new TransactionMiddleware(getTransactionContext()),
   new ErrorLoggerMiddleware(getPsrLogger())
]);
$result = $actionBus->execute(new AddProductAction('p2', 5));