Skip to content

VantaFinance/temporal-bundle

Repository files navigation

Temporal Bundle

Temporal is the simple, scalable open source way to write and run reliable cloud applications.

Features

  • Sentry: Send throwable events (if the SentryBundle use)
  • Doctrine: clear opened managers and check connection is still usable after each request ( if DoctrineBundle is use)
  • Serializer: Deserialize and serialize messages (if Symfony/Serializer is use, Recommend use)

Requirements:

  • php >= 8.2
  • symfony >= 6.0

Installation:

  1. Connect recipes
composer config --json extra.symfony.endpoint '["https://raw.githubusercontent.com/VantaFinance/temporal-bundle/main/.recipie/index.json", "flex://defaults"]' 
  1. Install package
composer req temporal serializer
  1. Configure docker-compose-temporal.yml/Dockerfile

  2. Added Workflow/Activity. See examples to get started.

Doctrine integrations

If DoctrineBundle is use, the following finalizer is available to you:

  • temporal.doctrine_ping_connection_<entity-mananger-name>.finalizer
  • temporal.doctrine_clear_entity_manager.finalizer

And interceptors:

  • temporal.doctrine_ping_connection_<entity-mananger-name>_activity_inbound.interceptor

Example config:

temporal:
  defaultClient: default
  pool:
    dataConverter: temporal.data_converter
    roadrunnerRPC: '%env(RR_RPC)%'

  workers:
    default:
      taskQueue: default
      exceptionInterceptor: temporal.exception_interceptor
      finalizers: 
        - temporal.doctrine_ping_connection_default.finalizer
        - temporal.doctrine_clear_entity_manager.finalizer
      interceptors:
        - temporal.doctrine_ping_connection_default_activity_inbound.interceptor

  clients:
    default:
      namespace: default
      address: '%env(TEMPORAL_ADDRESS)%'
      dataConverter: temporal.data_converter
    cloud:
      namespace: default
      address: '%env(TEMPORAL_ADDRESS)%'
      dataConverter: temporal.data_converter
      clientKey: '%env(TEMPORAL_CLIENT_KEY_PATH)%'
      clientPem: '%env(TEMPORAL_CLIENT_CERT_PATH)%'

Sentry integrations

Install packages:

composer require sentry temporal-sentry

If SentryBundle is use, the following interceptors is available to you:

  • temporal.sentry_workflow_outbound_calls.interceptor
  • temporal.sentry_activity_inbound.interceptor

Example config:

temporal:
  defaultClient: default
  pool:
    dataConverter: temporal.data_converter
    roadrunnerRPC: '%env(RR_RPC)%'

  workers:
    default:
      taskQueue: default
      exceptionInterceptor: temporal.exception_interceptor
      interceptors:
        - temporal.sentry_workflow_outbound_calls.intercepto
        - temporal.sentry_activity_inbound.interceptor

  clients:
    default:
      namespace: default
      address: '%env(TEMPORAL_ADDRESS)%'
      dataConverter: temporal.data_converter

Worker Factory

By default the Temporal\WorkerFactory is used to instantiate the workers. However when you are unit-testing you may wish to override the default factory with the one provided by the 'Testing framework'

Example Config:

temporal:
  defaultClient: default
  pool:
    dataConverter: temporal.data_converter
    roadrunnerRPC: '%env(RR_RPC)%'

  workers:
    default:
      taskQueue: default
      exceptionInterceptor: temporal.exception_interceptor
      interceptors:
        - temporal.sentry_workflow_outbound_calls.intercepto
        - temporal.sentry_activity_inbound.interceptor

  clients:
    default:
      namespace: default
      address: '%env(TEMPORAL_ADDRESS)%'
      dataConverter: temporal.data_converter

when@test:
  temporal:
    workerFactory: Temporal\Testing\WorkerFactory

Assign worker

Running workflows and activities with different task queue Add a AssignWorker attribute to your Workflow or Activity with the name of the worker. This Workflow or Activity will be processed by the specified worker.

Workflow example:

<?php

declare(strict_types=1);

namespace App\Workflow;

use Vanta\Integration\Symfony\Temporal\Attribute\AssignWorker;
use Temporal\Workflow\WorkflowInterface;

#[AssignWorker(name: 'worker1')]
#[WorkflowInterface]
final class MoneyTransferWorkflow
{
    #[WorkflowMethod]
    public function transfer(...): \Generator;

    #[SignalMethod]
    function withdraw(): void;

    #[SignalMethod]
    function deposit(): void;
}

Activity example:

<?php

declare(strict_types=1);

namespace App\Workflow;

use Vanta\Integration\Symfony\Temporal\Attribute\AssignWorker;
use Temporal\Activity\ActivityInterface;
use Temporal\Activity\ActivityMethod;

#[AssignWorker(name: 'worker1')]
#[ActivityInterface(...)]
final class MoneyTransferActivity
{
    #[ActivityMethod]
    public function transfer(...): int;

    #[ActivityMethod]
    public function cancel(...): bool;
}

TODO

  • E2E test
  • documentation