Passa al contenuto principale

Principi di Architettura

L'architettura di Emblema è guidata da principi fondamentali che assicurano scalabilità, affidabilità e manutenibilità nel tempo. Questi principi non sono solo teoria, ma pratiche concrete applicate in ogni componente del sistema.

🎯 Principi Fondamentali

1. Modularità e Separazione delle Responsabilità

Ogni componente di Emblema ha una responsabilità ben definita e opera in modo indipendente.

Loading diagram...

Implementazione pratica:

// ❌ Sbagliato: Logica mista
class DocumentController {
async uploadDocument(file: File) {
// Validazione, elaborazione, storage, notifiche tutto insieme
if (!file.type.includes("pdf")) throw new Error("Invalid");
const processed = await this.processFile(file);
await this.saveToDatabase(processed);
await this.sendNotification();
}
}

// ✅ Corretto: Responsabilità separate
class DocumentController {
constructor(
private validator: DocumentValidator,
private processor: DocumentProcessor,
private storage: DocumentStorage,
private notifier: NotificationService,
) {}

async uploadDocument(file: File) {
await this.validator.validate(file);
const processed = await this.processor.process(file);
const stored = await this.storage.save(processed);
await this.notifier.notify("document.uploaded", stored);
}
}

2. Scalabilità Orizzontale

Il sistema deve poter crescere aggiungendo risorse, non sostituendole.

Loading diagram...

Configurazione Docker Compose reale per scaling:

services:
www-emblema:
image: emblema/www:${EMBLEMA_VERSION:-dev}
restart: always
depends_on:
- document-render
- milvus
- minio
- graphql-engine
- keycloak
- litellm
- background-task
environment:
- MILVUS_API_URL=http://milvus:19530/v2/vectordb
- HASURA_API_URL=http://graphql-engine:8080/v1/graphql
- LITELLM_API_URL=http://litellm:4000/v1
- BACKGROUND_TASK_API_URL=http://background-task
labels:
- "traefik.enable=true"
- "traefik.http.routers.emblema-web.rule=Host(`${EMBLEMA_WEB_HOSTNAME}`)"

background-task:
image: emblema/background-task:${EMBLEMA_VERSION:-dev}
restart: always
environment:
- CELERY_BROKER_URL=${CELERY_BROKER_URL}
- MILVUS_URI=http://milvus:19530
- GRAPHQL_URL=http://graphql-engine:8080/v1/graphql

background-task-worker:
image: emblema/background-task:${EMBLEMA_VERSION:-dev}
command: /app/.venv/bin/celery -A app.celery_app:celery_app worker --concurrency=${CELERY_MAX_CONCURRENCY:-1}
deploy:
resources:
reservations:
devices:
- driver: nvidia
device_ids: ["1"]
capabilities: [gpu]

3. Sicurezza by Design

La sicurezza non è un layer aggiuntivo, ma parte integrante di ogni componente.

Principi applicati:

  • Zero Trust: Nessuna fiducia implicita
  • Least Privilege: Minime autorizzazioni necessarie
  • Defense in Depth: Multiple barriere di sicurezza
// Esempio di middleware di sicurezza
export class SecurityMiddleware {
async authenticate(req: Request): Promise<User> {
// 1. Verifica token JWT
const token = this.extractToken(req);
const payload = await this.verifyToken(token);

// 2. Verifica permessi
const user = await this.loadUser(payload.userId);
if (!user.isActive) throw new UnauthorizedError();

// 3. Rate limiting
await this.checkRateLimit(user.id, req.ip);

// 4. Audit logging
await this.logAccess(user, req);

return user;
}
}

4. Resilienza e Fault Tolerance

Il sistema deve continuare a funzionare anche in presenza di errori.

Loading diagram...

Pattern di resilienza implementati:

Circuit Breaker

class CircuitBreaker {
private failures = 0;
private lastFailTime: Date;
private state: "CLOSED" | "OPEN" | "HALF_OPEN" = "CLOSED";

async call<T>(fn: () => Promise<T>): Promise<T> {
if (this.state === "OPEN") {
if (this.shouldAttemptReset()) {
this.state = "HALF_OPEN";
} else {
throw new Error("Circuit breaker is OPEN");
}
}

try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
}

Retry con Backoff

async function retryWithBackoff<T>(
fn: () => Promise<T>,
maxRetries = 3,
baseDelay = 1000,
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;

const delay = baseDelay * Math.pow(2, i);
await sleep(delay + Math.random() * 1000);
}
}
}

5. Osservabilità

Ogni componente deve essere monitorabile e tracciabile.

Loading diagram...

Implementazione delle metriche:

import { Counter, Histogram, register } from "prom-client";

// Metriche custom
const httpRequestDuration = new Histogram({
name: "http_request_duration_seconds",
help: "Duration of HTTP requests in seconds",
labelNames: ["method", "route", "status"],
});

const documentsProcessed = new Counter({
name: "documents_processed_total",
help: "Total number of documents processed",
labelNames: ["type", "status"],
});

// Middleware per tracking
export function metricsMiddleware(req, res, next) {
const start = Date.now();

res.on("finish", () => {
const duration = (Date.now() - start) / 1000;
httpRequestDuration
.labels(req.method, req.route?.path || "unknown", res.statusCode)
.observe(duration);
});

next();
}

6. Performance First

Le performance non sono un afterthought ma una caratteristica fondamentale.

Strategie implementate:

Caching Multi-livello

class CacheStrategy {
constructor(
private l1: MemoryCache, // In-process cache
private l2: RedisCache, // Distributed cache
private l3: DatabaseCache, // Persistent cache
) {}

async get<T>(key: string): Promise<T | null> {
// L1: Memory (microseconds)
let value = await this.l1.get(key);
if (value) return value;

// L2: Redis (milliseconds)
value = await this.l2.get(key);
if (value) {
await this.l1.set(key, value, 60); // 1 minute
return value;
}

// L3: Database (tens of milliseconds)
value = await this.l3.get(key);
if (value) {
await this.l2.set(key, value, 3600); // 1 hour
await this.l1.set(key, value, 60);
return value;
}

return null;
}
}

Query Optimization

-- Indici ottimizzati per ricerche comuni
CREATE INDEX idx_documents_user_created
ON documents(user_id, created_at DESC)
WHERE deleted_at IS NULL;

-- Materialized view per aggregazioni costose
CREATE MATERIALIZED VIEW document_stats AS
SELECT
user_id,
COUNT(*) as total_documents,
SUM(file_size) as total_size,
AVG(processing_time) as avg_processing_time
FROM documents
WHERE deleted_at IS NULL
GROUP BY user_id;

-- Refresh periodico
CREATE OR REPLACE FUNCTION refresh_document_stats()
RETURNS void AS $$
BEGIN
REFRESH MATERIALIZED VIEW CONCURRENTLY document_stats;
END;
$$ LANGUAGE plpgsql;

7. Automazione e DevOps

Tutto ciò che può essere automatizzato, deve esserlo.

# CI/CD Pipeline esempio
name: Deploy Production

on:
push:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run tests
run: |
docker-compose -f docker-compose.test.yml up --abort-on-container-exit

build:
needs: test
runs-on: ubuntu-latest
steps:
- name: Build and push images
run: |
docker buildx build --platform linux/amd64,linux/arm64 \
-t emblema/app:${{ github.sha }} \
--push .

deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Deploy to Kubernetes
run: |
kubectl set image deployment/web-app \
app=emblema/app:${{ github.sha }} \
--record

🏛️ Architettura Decisionale

Trade-offs Accettati

  1. Complessità vs Scalabilità

    • Accettiamo la complessità dei microservizi per avere scalabilità indipendente
  2. Consistenza vs Performance

    • Usiamo eventual consistency dove possibile per migliorare le performance
  3. Costo vs Affidabilità

    • Investiamo in ridondanza per garantire uptime 99.9%

Anti-Pattern da Evitare

Distributed Monolith: Microservizi troppo accoppiati ❌ Chatty Services: Troppe chiamate tra servizi ❌ Shared Database: Database condiviso tra servizi ❌ Synchronous Everything: Tutto sincrono senza code

📊 Metriche di Successo

Per verificare l'aderenza ai principi, monitoriamo:

  • Tempo di deploy: < 10 minuti
  • Tempo di recovery: < 5 minuti
  • Test coverage: > 80%
  • API latency p99: < 200ms
  • Uptime: > 99.9%

🔮 Evoluzione Futura

I principi architetturali evolvono con la tecnologia:

  1. Edge Computing: Elaborazione più vicina all'utente
  2. Serverless: Per workload variabili
  3. AI-Driven Ops: Automazione intelligente
  4. Zero-Trust Architecture: Sicurezza perimetrale zero

🏗️ Prossimo passo: Approfondisci l'High-Level Design per vedere come questi principi si traducono in architettura concreta.

Questa pagina ti è stata utile?