Skip to content

Architecture

This guide explains the technical architecture of TMGMT Lara Translate, focusing on the Symfony Messenger implementation for asynchronous translation processing.

Overview

The module uses Symfony Messenger (via the sm module) to handle translation jobs asynchronously. This replaces the previous Drupal Queue API implementation with a more modern, flexible message-based system.

Architecture Diagram

flowchart TD
    A[TMGMT Job Submitted] --> B[LaraTranslator::requestTranslation]
    B --> C[Job Items Marked Active]
    C --> D[MessengerDispatcher::dispatchTranslationJob]
    D --> E[TranslationJobMessage Created]
    E --> F[Symfony Messenger Bus]
    F --> G[Message Transport]
    G --> H[TranslationJobMessageHandler]
    H --> I{Validate Job Item}
    I -->|Valid| J[Load Lara Translator Plugin]
    I -->|Invalid/Not Active| K[Log Warning, Exit]
    J --> L[LaraTranslator::processJobItem]
    L --> M[Call Lara API]
    M --> N[Save Translated Data]
    N --> O[Job Item Completed]
    O --> P{Success?}
    P -->|Yes| Q[Job Item Marked Complete]
    P -->|Transient Error| R[Messenger Auto-Retry]
    P -->|Permanent Error| S[Job Item Marked Failed]
    R --> H

Core Components

1. MessengerDispatcher (Service)

Location: src/Service/MessengerDispatcher.php

Responsibilities: - Creates TranslationJobMessage objects - Dispatches messages to Symfony Messenger bus - Provides interface between TMGMT and async processing

Key Methods:

public function dispatchTranslationJob(int $jobItemId): void {
    $message = new TranslationJobMessage($jobItemId);
    $this->messageBus->dispatch($message);
}

2. TranslationJobMessage (Message Object)

Location: src/Messenger/TranslationJobMessage.php

Purpose: Lightweight DTO carrying job item identifier - Contains only jobItemId for minimal memory footprint - Immutable object for thread safety

3. TranslationJobMessageHandler (Message Handler)

Location: src/Messenger/TranslationJobMessageHandler.php

Key Features: - Tagged with #[AsMessageHandler] for automatic discovery - Handles retry logic through Messenger's exception system - Validates job item state before processing - Provides comprehensive error handling

Handler Signature:

#[AsMessageHandler]
public function __invoke(TranslationJobMessage $message): void {
    // Processing logic here
}

4. LaraTranslator (Plugin)

Location: src/Plugin/tmgmt/Translator/LaraTranslator.php

Updated Methods: - requestTranslation(): Dispatches jobs instead of immediate processing - requestJobItemsTranslation(): Handles continuous translation scenarios - processJobItem(): Synchronous processing called by message handler

Symfony Messenger Integration

Service Registration

The module uses Drupal's autowiring and autoconfiguration. The handler is not explicitly registered in services.yml — it is auto-discovered via the #[AsMessageHandler] PHP attribute combined with the _defaults block:

# tmgmt_laratranslate.services.yml
services:
  _defaults:
    autowire: true
    autoconfigure: true

  # The handler (Drupal\tmgmt_laratranslate\Messenger\TranslationJobMessageHandler)
  # is auto-discovered via #[AsMessageHandler] — no explicit entry needed.

  Drupal\tmgmt_laratranslate\Service\MessengerDispatcher:
    arguments:
      $messageBus: '@messenger.default_bus'

The #[AsMessageHandler] attribute on the handler class, combined with autoconfigure: true, automatically registers it as a Symfony Messenger handler. The logger is injected via the #[Autowire(service: 'logger.channel.tmgmt_laratranslate')] attribute.

Message Bus Configuration

Uses the default message bus from the SM module: - Automatic handler registration - Built-in retry mechanisms - Configurable transports (database, Redis, etc.)

Transport Options

Available transports for different environments:

Default Database Transport

  • Development: Uses Drupal database for message storage
  • No external dependencies
  • Built-in persistence

Redis Transport (Production)

  • High performance: In-memory message processing
  • Scalability: Handles large message volumes
  • Persistence: Survives restarts

Custom Transports

  • AMQP: RabbitMQ message queues
  • SQS: Amazon Simple Queue Service
  • Kafka: High-throughput streaming

Error Handling Strategy

Transient Error Detection

The handler distinguishes between temporary and permanent errors:

private function isTransientError(\Throwable $exception): bool {
    $message = strtolower($exception->getMessage());
    $code = $exception->getCode();

    // HTTP status codes indicating transient errors
    $transientHttpCodes = [429, 500, 502, 503, 504];
    if (in_array($code, $transientHttpCodes, true)) {
        return true;
    }

    // Quota exceeded errors
    if (str_contains($message, 'quota') && str_contains($message, 'exceeded')) {
        return true;
    }

    // Network-related errors
    $networkKeywords = ['timeout', 'connection', 'network', 'unreachable'];
    foreach ($networkKeywords as $keyword) {
        if (str_contains($message, $keyword)) {
            return true;
        }
    }

    return false;
}

Retry Mechanism

Uses Symfony Messenger's built-in retry: - Exponential backoff: Increasing delays between attempts - Maximum attempts: Configurable retry limit - Automatic recovery: No manual intervention needed

Permanent Error Handling

Non-recoverable errors throw UnrecoverableMessageHandlingException: - Invalid job item IDs - Unsupported language pairs - Authentication failures

Performance Considerations

Memory Efficiency

  • Lightweight messages: Only carry essential data (job item ID)
  • Lazy loading: Job items loaded only when needed
  • Cleanup: Automatic removal of processed messages

Scalability

  • Horizontal scaling: Multiple worker processes possible
  • Queue depth handling: Large numbers of pending jobs supported
  • Resource isolation: Translation processing separate from web requests

Caching Strategy

  • Plugin instance caching: Reuse Lara translator instances
  • Configuration caching: Cache language mappings and settings
  • API response caching: Optional for repeated translations

Migration from Queue API

Key Differences

Aspect Old Queue API New Symfony Messenger
Dispatch Queue::createItem() MessageBus::dispatch()
Processing QueueWorker::processItem() MessageHandler::__invoke()
Retry Logic SuspendQueueException Built-in Messenger retry
Transport Drupal database only Multiple transport options
Configuration QueueWorker annotation #[AsMessageHandler] attribute + autoconfigure

Benefits of Migration

  • More flexible: Multiple transport options beyond database
  • Better retry: Built-in exponential backoff
  • Cleaner code: Separation of concerns
  • Future-proof: Compatible with modern PHP/Symfony practices

Testing and Debugging

Message Testing

// Test message dispatch
$message = new TranslationJobMessage(123);
$testResult = $this->messageBus->dispatch($message);

Handler Testing

// Test handler directly
$handler = new TranslationJobMessageHandler($entityTypeManager, $logger);
$message = new TranslationJobMessage(123);
$handler($message);

Transport Configuration

# config/install/messenger.settings.yml
transports:
  async: 'doctrine://default'
  priority: 'sync://default'

# Or Redis for production:
# async: 'redis://localhost:6379/messages'

Monitoring and Observability

Message Tracking

  • Message IDs: Unique identifiers for tracking
  • Processing time: Duration per job item
  • Success rates: Percentage of completed vs failed jobs
  • Queue depth: Current pending messages

Performance Metrics

  • Throughput: Messages processed per minute
  • Latency: Time from dispatch to completion
  • Error rates: Transient vs permanent failures
  • Resource usage: Memory and CPU during processing

Configuration Options

Messenger Settings

# settings.php
$settings['messenger'] = [
  'transports' => [
    'async' => 'redis://localhost:6379/messages'
  ],
  'routing' => [
    'Drupal\tmgmt_laratranslate\Messenger\TranslationJobMessage' => ['async']
  ]
];

Custom Retry Configuration

# Optional custom retry settings
messenger:
  retry_strategy: 'exponential'
  max_retries: 5
  initial_delay: 30
  max_delay: 300
  multiplier: 2

Future Extensibility

Message Types

The architecture supports additional message types: - Batch processing: Handle multiple items in one message - Priority messages: Urgent translations - Maintenance messages: Cleanup and optimization tasks

Handler Enhancements

  • Parallel processing: Multiple job items simultaneously
  • Circuit breaker: Fail fast on API outages
  • Metrics collection: Detailed performance tracking

Security Considerations

Message Security

  • Input validation: All job item IDs validated
  • Access control: Handler respects Drupal permissions
  • Data protection: No sensitive data in messages

API Security

  • Credential management: Secure key storage via Key module
  • Rate limiting: Respect Lara API limits
  • Audit logging: All translation attempts logged