Architettura del Sistema
Panoramica dell'architettura tecnica di UniMsg, inclusi stack tecnologico, pattern di design e flusso delle richieste.
Stack Tecnologico
| Componente | Tecnologia | Versione |
|---|---|---|
| Framework | CodeIgniter | 4.x |
| Linguaggio | PHP | 8.1+ |
| Database | MySQL | 8.0+ |
| Cache | Redis | 6.x |
| Queue | Redis / Database | - |
| Web Server | Nginx / Apache | - |
Pattern Architetturali
Service Layer Pattern
La logica di business è incapsulata in servizi dedicati, separando controller (che gestiscono HTTP) dalla logica applicativa.
app/
├── Controllers/
│ └── Api/
│ └── V1/
│ ├── SmsController.php
│ ├── WhatsAppController.php
│ └── EmailController.php
├── Services/
│ ├── SmsService.php
│ ├── WhatsAppService.php
│ ├── EmailService.php
│ └── TelegramService.php
└── Models/
├── MessageModel.php
├── ClientModel.php
└── CreditModel.php
Factory Pattern (Gateway)
I gateway di invio sono gestiti tramite Factory pattern, permettendo di switchare provider senza modificare la logica di business.
// GatewayFactory.php
class GatewayFactory
{
public static function create(string $type, string $provider): GatewayInterface
{
return match ($type) {
'sms' => match ($provider) {
'twilio' => new TwilioSmsGateway(),
'nexmo' => new NexmoSmsGateway(),
'skebby' => new SkebbySmsGateway(),
default => throw new InvalidGatewayException()
},
'whatsapp' => new WhatsAppBusinessGateway(),
'email' => match ($provider) {
'sendgrid' => new SendGridGateway(),
'mailgun' => new MailgunGateway(),
default => new SmtpGateway()
},
'telegram' => new TelegramBotGateway(),
default => throw new InvalidChannelException()
};
}
}
Repository Pattern
L'accesso ai dati è astratto tramite repository, facilitando test e manutenzione.
interface MessageRepositoryInterface
{
public function find(string $id): ?Message;
public function findByClient(int $clientId, array $filters): array;
public function create(array $data): Message;
public function updateStatus(string $id, string $status): bool;
}
Flusso Richiesta API
Ecco il flusso completo di una richiesta API per l'invio di un SMS:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Client │────▶│ Nginx │────▶│ PHP-FPM │
└─────────────┘ └─────────────┘ └──────┬──────┘
│
┌──────────────────────────┼──────────────────────────┐
│ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────┐ │
│ │ Filter │──▶│ Controller │──▶│ Service │ │
│ │ (Auth) │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └────┬─────┘ │
│ │ │
│ ┌─────────────┐ ┌─────────────┐ │ │
│ │ Redis │◀──│ Queue │◀──────┘ │
│ │ (Cache) │ │ Job │ │
│ └─────────────┘ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Gateway │ │
│ │ (Twilio) │ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ MySQL │ │
│ │ (Update) │ │
│ └─────────────┘ │
│ │
└───────────────────────────────────────────────────┘
Passi Dettagliati
- Richiesta HTTP - Il client invia POST a
/v1/sms/send - Auth Filter - Valida il Bearer token OAuth2
- Controller - Valida input e chiama il Service
- Service - Verifica crediti, crea record messaggio
- Queue - Il job di invio viene accodato
- Response - Ritorna immediatamente con
status: queued - Worker - Processa il job dalla coda
- Gateway - Invia il messaggio via provider (es: Twilio)
- Update - Aggiorna lo stato in database
- Webhook - Notifica il client (se configurato)
Schema Database
Tabelle principali del sistema:
-- Clients (account API)
CREATE TABLE clients (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
client_id VARCHAR(64) UNIQUE NOT NULL,
client_secret VARCHAR(255) NOT NULL,
credits DECIMAL(10,2) DEFAULT 0,
status ENUM('active', 'suspended') DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- Messages
CREATE TABLE messages (
id VARCHAR(64) PRIMARY KEY,
client_id INT NOT NULL,
channel ENUM('sms', 'whatsapp', 'email', 'telegram') NOT NULL,
recipient VARCHAR(255) NOT NULL,
content TEXT,
status ENUM('queued', 'sent', 'delivered', 'read', 'failed') DEFAULT 'queued',
gateway VARCHAR(64),
gateway_message_id VARCHAR(255),
credits_used DECIMAL(10,2) DEFAULT 0,
error_code VARCHAR(64),
error_message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
sent_at TIMESTAMP NULL,
delivered_at TIMESTAMP NULL,
FOREIGN KEY (client_id) REFERENCES clients(id)
);
-- Access Tokens
CREATE TABLE oauth_access_tokens (
id VARCHAR(64) PRIMARY KEY,
client_id INT NOT NULL,
scopes JSON,
expires_at TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (client_id) REFERENCES clients(id)
);
-- Webhooks Configuration
CREATE TABLE webhooks (
id INT PRIMARY KEY AUTO_INCREMENT,
client_id INT NOT NULL,
url VARCHAR(500) NOT NULL,
secret VARCHAR(255) NOT NULL,
events JSON,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (client_id) REFERENCES clients(id)
);
-- Webhook Logs
CREATE TABLE webhook_logs (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
webhook_id INT NOT NULL,
event VARCHAR(64) NOT NULL,
payload JSON,
response_code INT,
response_body TEXT,
attempts INT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (webhook_id) REFERENCES webhooks(id)
);
Gestione Code
I messaggi vengono processati in modo asincrono tramite job queue:
// SendMessageJob.php
class SendMessageJob extends BaseJob
{
protected string $messageId;
public function handle(): void
{
$message = $this->messageRepository->find($this->messageId);
if (!$message) {
return;
}
try {
$gateway = GatewayFactory::create(
$message->channel,
$message->client->getGatewayFor($message->channel)
);
$result = $gateway->send($message);
$this->messageRepository->updateStatus(
$message->id,
'sent',
['gateway_message_id' => $result->externalId]
);
} catch (GatewayException $e) {
$this->messageRepository->updateStatus(
$message->id,
'failed',
[
'error_code' => $e->getCode(),
'error_message' => $e->getMessage()
]
);
// Refund credits
$this->creditService->refund($message->client_id, $message->credits_used);
}
}
}
Scalabilità
Componenti Scalabili
- API Servers - Horizontal scaling con load balancer
- Queue Workers - Multiple workers per alta throughput
- Database - Read replicas per query intensive
- Cache - Redis cluster per alta disponibilità
Metriche Chiave
| Metrica | Target |
|---|---|
| API Response Time (p95) | < 200ms |
| Message Processing Time | < 5 secondi |
| Uptime | 99.9% |
| Throughput | 1000+ msg/sec |