Waha
Advanced WhatsApp integration architecture and implementation guide for developers.
Waha Module Architecture Documentation
Overview
The Waha module is a comprehensive WhatsApp HTTP API integration built as a Laravel HMVC module. This documentation focuses on the architecture, class structure, and data flow to help developers understand the system design and implementation patterns.
Architecture Diagram

The architecture diagram illustrates the complete data flow through the Waha WhatsApp integration system, showing how external webhooks are processed through controllers, services, event handlers, and stored in the database.
Core Architecture Patterns
1. HMVC Module Structure
Modules/Waha/
├── app/
│ ├── Controllers/ # HTTP request handlers
│ ├── Models/ # Database entities
│ ├── Services/ # Business logic layer
│ ├── Events/ # Event handlers for webhooks
│ ├── DTOs/ # Data transfer objects
│ └── Resources/ # API response formatters
├── database/
│ └── migrations/ # Database schema
└── routes/
└── api.php # Route definitions
2. Data Flow Architecture
Webhook Processing Flow
External Waha Server → WebhookController → WebhookEventHandlerFactory
↓
Event Handler (MessageHandler/CallHandler/etc.) → Service Layer → Models → Database
API Request Flow
Client Request → Controller → Service → Model → Database
↓
Resource → JSON Response
Core Components
Models & Database Layer
WahaSession Model
class WahaSession extends Model
{
// Represents WhatsApp account sessions
// Relationships: hasMany(WahaMessage, WahaChat, WahaCall)
// Key fields: name, status, config, user_id
}
WahaMessage Model
class WahaMessage extends Model
{
// Stores individual WhatsApp messages
// Relationships: belongsTo(WahaSession, WahaChat)
// Key fields: message_id, type, content, media_url, status
}
WahaCall Model
class WahaCall extends Model
{
// Logs call events and statistics
// Relationships: belongsTo(WahaSession)
// Key fields: call_id, from, to, type
}
Service Layer Design
Service Responsibilities
- WahaSessionServices: Session lifecycle, status management
- WahaMessageServices: Message processing, media handling
- WahaChatServices: Chat organization, read status
- WahaCallsServices: Call logging, statistics aggregation
Service Pattern Example
class WahaMessageServices
{
public function processIncomingMessage(array $webhookData): WahaMessage
{
// 1. Validate webhook data
// 2. Find or create session/chat
// 3. Store message with metadata
// 4. Handle media files if present
// 5. Update read status
}
}
Event Handler System
WebhookEventHandlerFactory Pattern
class WebhookEventHandlerFactory
{
public static function getHandler(string $event): ?EventHandlerInterface
{
return match($event) {
'message.any' => app(MessageHandler::class),
'call.received' => app(CallHandler::class),
'session.status' => app(SessionStatusHandler::class),
default => null,
};
}
}
Handler Implementation
class MessageHandler implements EventHandlerInterface
{
public function handle(array $data): void
{
// Extract message data
// Process through service layer
// Handle any business logic
// Store in database
}
}
Key Design Patterns
Repository Pattern Implementation
The module uses service classes as a repository pattern implementation:
// Service acts as repository for business logic
class WahaSessionServices
{
public function findActiveSession(string $name): ?WahaSession
{
return WahaSession::where('name', $name)
->where('status', 'working')
->first();
}
public function createSession(array $data): WahaSession
{
// Business logic for session creation
// Validation, external API calls, etc.
}
}
Factory Pattern for Event Handlers
Dynamic event handler resolution using factory pattern:
class WebhookEventHandlerFactory
{
private static array $handlers = [
'message.any' => MessageHandler::class,
'call.received' => CallHandler::class,
'call.accepted' => CallHandler::class,
'call.rejected' => CallHandler::class,
'session.status' => SessionStatusHandler::class,
];
public static function getHandler(string $event): ?EventHandlerInterface
{
if (!isset(self::$handlers[$event])) {
return null;
}
return app(self::$handlers[$event]);
}
}
DTO Pattern for Data Transfer
Structured data transfer between layers:
class CallDataDto
{
public function __construct(
public readonly string $sessionName,
public readonly string $callId,
public readonly string $from,
public readonly string $to,
public readonly string $type
) {}
public static function fromWebhookData(array $data): self
{
return new self(
sessionName: $data['session'],
callId: $data['payload']['id'],
from: $data['payload']['from'],
to: $data['payload']['to'],
type: $data['event']
);
}
}
Database Design Principles
Relationship Structure
-- Central session entity
waha_sessions (id, name, user_id, status, config)
|
├── waha_messages (session_id, chat_id, message_id, type, content)
├── waha_chats (session_id, chat_id, name, last_message_at)
├── waha_calls (session_id, call_id, from, to, type)
└── user_waha (user_id, session_id) -- Many-to-many pivot
Index Strategy
-- Performance indexes for common queries
INDEX idx_sessions_status ON waha_sessions(status)
INDEX idx_messages_session_chat ON waha_messages(session_id, chat_id)
INDEX idx_calls_session_type ON waha_calls(session_id, type)
INDEX idx_chats_session_updated ON waha_chats(session_id, updated_at)
Data Integrity Patterns
// Model relationships ensure referential integrity
class WahaMessage extends Model
{
public function session(): BelongsTo
{
return $this->belongsTo(WahaSession::class);
}
public function chat(): BelongsTo
{
return $this->belongsTo(WahaChat::class);
}
}
Security Architecture
HMAC Webhook Validation
Cryptographic validation of incoming webhooks:
class WebhookController extends Controller
{
private function validateSignature(Request $request): bool
{
$signature = $request->header('X-Webhook-Signature');
$payload = $request->getContent();
$secret = config('waha.webhook_secret');
$expectedSignature = hash_hmac('sha256', $payload, $secret);
return hash_equals($expectedSignature, $signature);
}
public function handle(Request $request)
{
if (!$this->validateSignature($request)) {
return response()->json(['error' => 'Invalid signature'], 401);
}
// Process webhook...
}
}
Access Control Layer
User-session isolation through pivot table:
class WahaSessionServices
{
public function getUserSessions(int $userId): Collection
{
return WahaSession::whereHas('users', function ($query) use ($userId) {
$query->where('user_id', $userId);
})->get();
}
public function canUserAccessSession(int $userId, int $sessionId): bool
{
return UserWaha::where('user_id', $userId)
->where('session_id', $sessionId)
->exists();
}
}
Performance Considerations
Lazy Loading Strategy
Prevent N+1 queries with strategic eager loading:
class WahaMessageServices
{
public function getSessionMessages(int $sessionId): Collection
{
return WahaMessage::with(['session', 'chat'])
->where('session_id', $sessionId)
->orderBy('created_at', 'desc')
->paginate(50);
}
}
Caching Layer
Cache frequently accessed data:
class WahaSessionServices
{
public function getActiveSession(string $name): ?WahaSession
{
return Cache::remember("session.{$name}", 300, function () use ($name) {
return WahaSession::where('name', $name)
->where('status', 'working')
->first();
});
}
}
Background Job Processing
Async processing for heavy operations:
class ProcessWebhookJob implements ShouldQueue
{
public function handle(array $webhookData): void
{
$handler = WebhookEventHandlerFactory::getHandler($webhookData['event']);
if ($handler) {
$handler->handle($webhookData);
}
}
}
Extensibility Patterns
Event Handler Interface
Standardized interface for extending webhook handling:
interface EventHandlerInterface
{
public function handle(array $data): void;
}
// Custom handler implementation
class CustomMessageHandler implements EventHandlerInterface
{
public function handle(array $data): void
{
// Custom business logic
// Integration with other systems
// Custom validation/processing
}
}
Service Provider Registration
Register custom handlers through service providers:
class WahaServiceProvider extends ServiceProvider
{
public function register(): void
{
// Register custom event handlers
$this->app->bind(CustomEventHandler::class);
// Override default handlers if needed
$this->app->bind(MessageHandler::class, CustomMessageHandler::class);
}
}
Configuration-Driven Behavior
Environment-based feature toggles:
class WahaMessageServices
{
public function processMessage(array $data): void
{
// Core processing
$message = $this->storeMessage($data);
// Optional features based on config
if (config('waha.enable_analytics')) {
$this->recordAnalytics($message);
}
if (config('waha.enable_notifications')) {
$this->sendNotification($message);
}
}
}
Testing Architecture
Unit Testing Strategy
Test individual components in isolation:
class WahaSessionServicesTest extends TestCase
{
public function test_creates_session_with_valid_data()
{
$service = new WahaSessionServices();
$data = ['name' => 'test-session', 'config' => []];
$session = $service->createSession($data);
$this->assertInstanceOf(WahaSession::class, $session);
$this->assertEquals('test-session', $session->name);
}
}
Integration Testing
Test component interactions:
class WebhookIntegrationTest extends TestCase
{
public function test_processes_incoming_message_webhook()
{
// Arrange: Create session and webhook payload
$session = WahaSession::factory()->create();
$payload = ['event' => 'message.any', 'session' => $session->name];
// Act: Send webhook request
$response = $this->postJson('/api/waha/webhook', $payload);
// Assert: Message created and response correct
$response->assertOk();
$this->assertDatabaseHas('waha_messages', ['session_id' => $session->id]);
}
}
Monitoring & Observability
Logging Strategy
Structured logging for operations tracking:
class MessageHandler implements EventHandlerInterface
{
public function handle(array $data): void
{
Log::info('Processing webhook message', [
'event' => $data['event'],
'session' => $data['session'],
'message_id' => $data['payload']['id'] ?? null
]);
try {
// Process message...
Log::info('Message processed successfully');
} catch (\Exception $e) {
Log::error('Failed to process message', [
'error' => $e->getMessage(),
'data' => $data
]);
throw $e;
}
}
}
Metrics Collection
Track key performance indicators:
class WahaMetricsService
{
public function recordMessageProcessed(string $sessionName, string $type): void
{
// Record metrics for monitoring
Cache::increment("metrics.messages.{$sessionName}.{$type}");
Cache::increment("metrics.messages.total");
}
public function getSessionStats(string $sessionName): array
{
return [
'total_messages' => Cache::get("metrics.messages.{$sessionName}.total", 0),
'text_messages' => Cache::get("metrics.messages.{$sessionName}.text", 0),
'media_messages' => Cache::get("metrics.messages.{$sessionName}.media", 0),
];
}
}
Deployment Considerations
Environment Configuration
Key configuration points for different environments:
// config/waha.php
return [
'webhook_secret' => env('WAHA_WEBHOOK_SECRET'),
'api_url' => env('WAHA_API_URL', 'http://localhost:3000'),
'timeout' => env('WAHA_TIMEOUT', 30),
'enable_analytics' => env('WAHA_ENABLE_ANALYTICS', false),
'enable_caching' => env('WAHA_ENABLE_CACHING', true),
'queue_webhooks' => env('WAHA_QUEUE_WEBHOOKS', true),
];
Queue Configuration
Async processing setup:
// config/queue.php - Add Waha-specific queue
'connections' => [
'waha' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => 'waha-webhooks',
'retry_after' => 90,
'block_for' => null,
],
];
Database Optimization
Production database considerations:
-- Partitioning for large message tables
CREATE TABLE waha_messages_2024 PARTITION OF waha_messages
FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');
-- Archival strategy for old data
CREATE TABLE waha_messages_archive AS
SELECT * FROM waha_messages
WHERE created_at < NOW() - INTERVAL '6 months';
API Integration
For complete API endpoints and interactive testing:
- Swagger Documentation:
/docs/admin#tag/Waha - Postman Collection: Export available from Swagger UI
- Authentication: Bearer token required for all endpoints
Development Workflow
Module Structure Best Practices
- Follow HMVC pattern consistently
- Separate concerns: Controllers → Services → Models
- Use DTOs for complex data transfer
- Implement interfaces for extensibility
- Write comprehensive tests
Code Standards
- PSR-12 coding standards
- Laravel naming conventions
- PHPDoc documentation
- Type declarations
- Error handling with try-catch blocks
This architecture documentation provides the foundation for understanding and extending the Waha module's functionality within the larger HMVC application structure.