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
Related Documentation
- Message Handler Details - Handler implementation specifics
- Configuration Guide - Setup options
- Operations Guide - Production deployment
- Troubleshooting Guide - Issue resolution