Pular para o conteúdo principal

Arquitetura do Sistema

Esta página é uma análise detalhada de como o FeedbackPulse SaaS é construído. Entender a arquitetura ajuda a depurar problemas, estender a plataforma e tomar decisões informadas de implantação.


Stack Tecnológico

CamadaTecnologiaFinalidade
BackendLaravel 12 (PHP 8.4+)Framework de aplicação, roteamento, ORM, filas
Banco de DadosMySQL 8.0+ / MariaDB 10.6+Armazenamento persistente de dados
FrontendBlade + Alpine.js + Tailwind CSSUI renderizada no servidor com componentes reativos
PagamentosStripe PHP SDK + PayPal REST APICobrança de assinaturas
IAOpenAI GPT APIAnálise de sentimento, auto-etiquetagem, sugestões de resposta
AuthLaravel SocialiteOAuth com Google e GitHub
E-mailLaravel Mail (SMTP)E-mails transacionais, resumos, relatórios
Tempo RealServer-Sent Events (SSE)Streaming ao vivo de envios
AgendamentoLaravel Scheduler (cron)Resumos, expiração de testes, retenção de dados

Modelo Multi-Tenant

O FeedbackPulse utiliza um modelo de multi-tenancy com banco de dados único e esquema compartilhado. Isso significa:

  • Um banco de dados atende todos os tenants
  • Cada tabela com escopo de tenant possui uma coluna tenant_id
  • Um escopo global (TenantScope) filtra automaticamente todas as consultas para o tenant atual
  • Um trait (BelongsToTenant) preenche automaticamente o tenant_id na criação e aplica o escopo

Como Funciona a Resolução de Tenant

Quando uma requisição chega, o middleware ResolveTenant determina o tenant atual usando esta prioridade:

  1. Subdomínioacme.yourdomain.com → busca o tenant com subdomínio "acme"
  2. Domínio personalizadofeedback.acmecorp.com → busca o domínio verificado na tabela tenant_domains
  3. Usuário autenticado — usa o tenant_id do usuário logado como fallback

Para páginas públicas (como /wall/acme-corp), o tenant é resolvido diretamente pelo slug da URL no controller (ignorando o middleware).

Isolamento de Dados

+------------------------------------------+
| Banco de Dados |
| |
| products (tenant_id = 1) -> Dados Acme |
| products (tenant_id = 2) -> TechCorp |
| products (tenant_id = 3) -> E-Commerce |
| |
| TenantScope garante que Acme veja APENAS|
| linhas onde tenant_id = 1 |
+------------------------------------------+

Segurança: Controllers públicos usam withoutGlobalScopes() para ignorar o escopo de tenant e filtram manualmente pelo ID do tenant. Isso é intencional — páginas públicas precisam exibir dados sem uma sessão autenticada.


Estrutura de Diretórios

feedbackpulse-saas/
+-- app/
| +-- Console/Commands/ # 7 comandos artisan (resumos, retenção, alertas)
| +-- Http/
| | +-- Controllers/
| | | +-- Admin/ # Controllers do painel superadmin
| | | +-- Auth/ # Login, registro, 2FA, OAuth, impersonação
| | | +-- Customer/ # Portal do cliente
| | | +-- Public/ # Páginas públicas (mural, formulário, roadmap, changelog, hub)
| | | +-- Tenant/ # Controllers do painel do tenant
| | | +-- Webhooks/ # Handlers de webhooks Stripe & PayPal
| | +-- Middleware/ # 15 middlewares personalizados
| +-- Mail/ # 7 classes mailable
| +-- Models/ # 28 modelos Eloquent
| +-- Scopes/ # TenantScope (escopo global de consulta)
| +-- Services/ # Lógica de negócio (IA, pagamentos, webhooks, etc.)
| +-- Traits/ # Trait BelongsToTenant
| +-- Providers/ # Service providers
+-- config/ # 11 arquivos de configuração Laravel
+-- database/
| +-- migrations/ # 35+ arquivos de migração
| +-- seeders/ # Seeders de dados de demonstração
+-- public/ # Raiz web (index.php, assets, symlink de storage)
+-- resources/views/ # 86 templates Blade
| +-- admin/ # Views do superadmin
| +-- auth/ # Views de autenticação (login, registro, 2FA)
| +-- tenant/ # Views do painel do tenant
| +-- public/ # Views das páginas públicas
| +-- emails/ # Templates de e-mail
| +-- layouts/ # Templates de layout (admin, tenant, guest, install)
| +-- partials/ # Componentes compartilhados (nav, meta, paleta de comandos)
| +-- install/ # Views do instalador web
| +-- legal/ # Páginas de privacidade, termos, cookies
| +-- errors/ # Páginas de erro (403, 404, 419, 429, 500)
| +-- landing/ # Partials da landing page
+-- routes/
| +-- web.php # 416 linhas de rotas web
| +-- api.php # Rotas da API v2
+-- storage/ # Uploads, cache, sessões, logs
+-- bootstrap/ # Arquivos de bootstrap do framework

Funções de Usuário

O FeedbackPulse possui quatro funções de usuário, armazenadas na coluna users.role:

FunçãoAcessoURL de Login
superadminControle total da plataforma (/admin/*)/login
tenant_adminControle total do tenant (/dashboard, /settings/*)/login
tenant_staffAcesso limitado ao tenant (sem faturamento, sem exclusão)/login
customerSomente portal do cliente (/customer/dashboard)/customer/login

Hierarquia de Funções

superadmin
+-- Pode impersonar qualquer tenant_admin
+-- tenant_admin
+-- Pode convidar tenant_staff
+-- Pode gerenciar faturamento, configurações, equipe
+-- tenant_staff
+-- Pode gerenciar envios, campanhas
+-- customer
+-- Pode ver seu próprio feedback

Ciclo de Vida da Requisição

Veja o que acontece quando uma requisição chega ao FeedbackPulse:

Requisição do Navegador
|
v
public/index.php
|
v
Laravel Kernel (pilha de middlewares)
|
+-- EnsureInstalled -> Redireciona para /install se não configurado
+-- SecurityHeaders -> Adiciona HSTS, CSP, X-Frame-Options
+-- VerifyCsrfToken -> Verifica token CSRF (exceto webhooks)
+-- ResolveTenant -> Determina o tenant atual
+-- Authenticate -> Verifica se o usuário está logado
+-- EnsureTenantAccess -> Verifica se o usuário pertence ao tenant
+-- EnsureTwoFactorVerified -> Verifica 2FA se habilitado
+-- CheckPlanLimit -> Impõe limites de recursos do plano
|
v
Controller (processa a requisição)
|
v
Blade View (renderiza HTML)
|
v
Resposta -> Navegador

Arquitetura do Banco de Dados

Tabelas Principais

TabelaEscopoFinalidade
tenantsPlataformaContas multi-tenant
usersPlataformaTodas as contas de usuário (todas as funções)
plansPlataformaPlanos de assinatura
platform_settingsPlataformaConfiguração global chave-valor
productsTenantProdutos de feedback
feedback_campaignsTenantConfigurações de formulários de feedback
feedback_submissionsTenantEntradas individuais de feedback
feedback_tagsTenantTags (many-to-many com envios)
roadmap_itemsTenantItens do kanban de roadmap
roadmap_votesTenantVotos anônimos em itens do roadmap
feature_requestsTenantSugestões de funcionalidades da comunidade
changelog_entriesTenantNotas de versão do produto
team_membersTenantRegistros de membros da equipe
team_invitationsTenantConvites pendentes
api_keysTenantChaves de acesso à API
audit_logsPlataformaTrilha de auditoria de ações
notificationsPlataformaNotificações no aplicativo
payment_eventsPlataformaEventos de webhook Stripe/PayPal
webhook_logsTenantLogs de entrega de webhooks de saída
data_deletion_requestsTenantSolicitações de exclusão GDPR
landing_pagesPlataformaDados do construtor de landing page
tenant_domainsTenantMapeamentos de domínios personalizados
referral_codesTenantCódigos de indicação
referral_conversionsPlataformaRastreamento de conversões de indicação
cron_logsPlataformaLogs de execução de tarefas agendadas

Índices-Chave

Todas as tabelas com escopo de tenant são indexadas em (tenant_id, created_at) para desempenho otimizado de consultas. A tabela feedback_submissions possui índices adicionais em status, product_id, campaign_id e sentiment_label.


Arquitetura de Segurança

CamadaProteção
TransporteHTTPS imposto, cabeçalhos HSTS
AutenticaçãoBcrypt (12 rounds), 2FA opcional (TOTP)
AutorizaçãoMiddleware baseado em função + classes de política
CSRFTokens CSRF do Laravel em todos os formulários
XSSAuto-escape do Blade ({{ }})
Injeção SQLConsultas parametrizadas do Eloquent
Limitação de TaxaThrottling por rota (5-120 req/min)
Segurança da APIChaves de API com hash SHA256, limites de taxa por tenant
Segurança de WebhookVerificação de assinatura HMAC (Stripe), proteção SSRF
DadosConfigurações sensíveis criptografadas em repouso
SessõesCookies criptografados, HTTP-only, seguros
CabeçalhosCSP, X-Frame-Options, X-Content-Type-Options

Próximos Passos