php conference 2015: construindo e mantendo aplicações multi-tenant (multi-cliente)
TRANSCRIPT
Construindo e mantendo aplicações multi-tenant (multi-
cliente)Aryel Tupinambá
PHP Conference 2015
Sobre o palestrante
Co-fundador e CTO da LQDI DigitalProjetos para empresas como Porto Seguro, Nestlé, Garoto, Editora FTD, Tishman Speyer e Ambev
12 anos trabalhando com PHPDesde a época que o PHPClasses era a onda :)
Aryel Tupinambá
Single tenant- Um único "cliente" para a
aplicação
- Forma tradicional, instalada no
servidor do cliente
- Faz sentido quando a aplicação
é um PROJETO
Agora com mais um cliente… e mais outro...- Seu PROJETO se tornou um
PRODUTO
- Diversos clientes usando a mesma
aplicação
- Estruturas separadas para cada novo
cliente
- Nada é compartilhado; tudo é isolado
E agora, como faço...… atualizações de segurança?
… correção de bugs?
… features novas para todo mundo?
E o espaço que toda essa galera ocupa?
E o custo que tudo isso gera?
Arquitetura multi-tenant- Tenant = inquilino
- Uma única aplicação para vários
clientes
- Uma estrutura comum entre os clientes
- As benfeitorias servem para todos
- A customização de um cliente não
interfere nos demais, nem previne que
ele utilize da estrutura comum
Principais desafios
DATA STORAGE CODEBASE CUSTOMIZAÇÃO
Como segmentar a base de dados para cada cliente?
Como segmentar os arquivos enviados?
Como fica o código fonte da aplicação?
Como ficam as customizações?
Como lidamos com updates e novas features?
Como podemos customizar as funcionalidades da aplicação?
Como permitir que atualizações e novas features não sejam impedidas por customizações?
Não existe escolha certa ou erradaO que existe são escolhas que fazem mais sentido para determinados cenários.
Data storage
Três formatos mais comuns
Data storage
Instâncias separadas
Bancos de dados separadas
Banco de dados compartilhado
Cada cliente possui uma instância do seu SGDB (MySQL, Postgres, Mongo, etc), com seus dados isolados.
Mais isolado
Mais compartilhado
Cada cliente tem um banco de dados (ou schema) diferente, na mesma instância
Todos os clientes estão na mesma base de dados; o cliente é identificado por meio de uma coluna nas tabelas (tenantID)
Referência / diagramas: Microsoft - https://msdn.microsoft.com/en-us/library/aa479086.aspx
Três formatos mais comuns
Data storage
Instâncias separadas
Bancos de dados separadas
Banco de dados compartilhado
Mais isolado
Mais compartilhado
Todos os clientes estão na mesma base de dados; o cliente é identificado por meio de uma coluna nas tabelas (tenantID)
Referência / diagramas: Microsoft - https://msdn.microsoft.com/en-us/library/aa479086.aspx
Três formatos mais comuns
Data storage
Instâncias separadas
Bancos de dados separadas
Banco de dados compartilhado
Mais isolado
Mais compartilhado
Cada cliente tem um banco de dados (ou schema) diferente, na mesma instância
Referência / diagramas: Microsoft - https://msdn.microsoft.com/en-us/library/aa479086.aspx
Três formatos mais comuns
Data storage
Instâncias separadas
Bancos de dados separadas
Banco de dados compartilhado
Mais isolado
Mais compartilhado
Cada cliente possui uma instância do seu SGDB (MySQL, Postgres, Mongo, etc), com seus dados isolados.
Referência / diagramas: Microsoft - https://msdn.microsoft.com/en-us/library/aa479086.aspx
Referência / diagramas: Microsoft - https://msdn.microsoft.com/en-us/library/aa479086.aspx
Referência / diagramas: Microsoft - https://msdn.microsoft.com/en-us/library/aa479086.aspx
Três formatos mais comuns
Data storage
Instâncias separadas
Bancos de dados separadas
Banco de dados compartilhado
Sugestão de uso: - Aplicações com pouca ou nenhuma variabilidade /
customização- Aplicações em que a principal customização é visual- Aplicações com alto número de usuários (500k+)
Mais isolado
Mais compartilhado
Três formatos mais comuns
Data storage
Instâncias separadas
Bancos de dados separadas
Banco de dados compartilhado
Sugestão de uso: - Aplicações em que deve haver maior flexibilidade para
customização- Aplicações em que a infra-estrutura sempre será
responsabilidade sua- Planos baseados em volume de consumo ou
performance
Mais isolado
Mais compartilhado
Três formatos mais comuns
Data storage
Instâncias separadas
Bancos de dados separadas
Banco de dados compartilhado
Sugestão de uso: - Projetos em que a separação física do hardware se faz
necessária (instalações on-premises, compliance corporativo ou governamental, decisões de performance);
- Alta disparidade no volume de dados ou de acessos entre clientes (quando alguns clientes consomem demais)
- Quando você precisa monitorar o consumo individual de hardware para cada cliente (e seu SGDB não faz isso por database/schema)
Mais isolado
Mais compartilhado
- Em regra geral, a FLEXIBILIDADE necessária deve ser o fator decisório
- Customizar funcionalidades pode ser um desafio quando você tem um modelo de
dados fixo; alterar o modelo de dados de toda uma aplicação só para atender um
cliente / uma feature pode gerar inconsistência e dificultar a manutenção
- Use o pattern de "migrations" e "seeds" para criar e manter o database schema;
muito importante criar migrations que sejam, na medida do possível, 100%
reversíveis.
- Você não necessariamente precisa escolhar um único modelo para todos os dados
da aplicação; se sua aplicação tem interações entre os clientes mas é altamente
customizável, por exemplo, faz sentido segmentar o que faz parte do core em um
único database, e o que faz parte da customização em um database separado
Data storage
Relembrando: não existe escolha certa ou errada
Data storage: na prática
Data storage: na práticaModelo compartilhado (via tenant ID)
Data storage: na práticaModelo isolado (instâncias diferentes)
Codebase
Codebase- Dois cenários: uma instância para todos, ou uma instância por tenant
app
código fonte
cliente1.app.com
cliente2.app.com
cliente3.app.com
config 1
config 2
config 3
cliente1.app.com
código fonte
config 1
cliente2.app.com
código fonte
config 2
cliente3.app.com
código fonte
config 3
custom code
Uma instância para todos
Uma instância por cliente
Codebase- Uma instância para todos
- Atualizações são sincronizadas sem custo ou esforço
- Nenhum ou pouquíssimo "housekeeping" necessário
- Não é muito flexível para customizações
- Mais fácil de escalar quando o padrão de consumo de todos os clientes é semelhante
app
código fonte
cliente1.app.com
cliente2.app.com
bigcorp.app.com
config 1
config 2
config 3
Codebase- Uma instância para todos
- Atualizações são sincronizadas sem custo ou esforço
- Nenhum ou pouquíssimo "housekeeping" necessário
- Não é muito flexível para customizações
- Mais fácil de escalar quando o padrão de consumo de todos os clientes é semelhante
app
código fonte
cliente1.app.com
cliente2.app.com
bigcorp.app.com
config 1
config 2
config 3
Nginx sitesvia DB, cachedGit repository
Codebase- Uma instância por cliente
- Atualizações não são sincronizadas, exceto se automatizadas
- Necessário um trabalho de "housekeeping" para manutenção das instâncias
- Bastante flexível para customizações
- Qualquer cliente pode ter sua versão da aplicação "congelada"
- O código do próprio "core" da aplicação pode ser forkado e alterado a qualquer momento (se em
algum momento isso fizer sentido para o negócio)
bigcorp.app.com
código fonte
config 1
cliente1.app.com
código fonte
config 2
cliente2.app.com
código fonte
config 3
custom code
Codebase- Uma instância por cliente
- Atualizações não são sincronizadas, exceto se automatizadas
- Necessário um trabalho de "housekeeping" para manutenção das instâncias
- Bastante flexível para customizações
- Qualquer cliente pode ter sua versão da aplicação "congelada"
- O código do próprio "core" da aplicação pode ser forkado e alterado a qualquer momento (se em
algum momento isso fizer sentido para o negócio)
bigcorp.app.com
código fonte
config 1
cliente1.app.com
código fonte
config 2
cliente2.app.com
código fonte
config 3
Git repo
.env files
custom code
Codebase- Uma instância por cliente
- Atualizações não são sincronizadas, exceto se automatizadas
- Necessário um trabalho de "housekeeping" para manutenção das instâncias
- Bastante flexível para customizações
- Qualquer cliente pode ter sua versão da aplicação "congelada"
- O código do próprio "core" da aplicação pode ser forkado e alterado a qualquer momento (se em
algum momento isso fizer sentido para o negócio)
bigcorp.app.com
código fonte
config 1
cliente1.app.com
código fonte
config 2
cliente2.app.com
código fonte
config 3
Nginx sites + PHP-FPM pools
v1.4.0 v1.4.1 v1.4.0
custom code
Codebase- De novo, o maior fator decisório aqui deve ser a FLEXIBILIDADE: o quanto você
espera que o código da aplicação se altere com customizações de clientes?
- A escalabilidade, embora seja um desafio (e mereceria uma palestra a parte), não
deve ser dificultada em nenhum dos cenários, DESDE QUE você automatize
todos os processos e fique de olho na consistência
- Instâncias isoladas permitem que você escale clientes mais "barulhentos" (alto
volume) para hardware melhor
- Você pode também trabalhar com instâncias segmentadas por tier ou plano, e
"empacotar" menos clientes por instâncua em tiers mais elevados/caros.
Variabilidade e customização
Variabilidade e customização- O calcanhar de aquiles da arquitetura multi-tenant
- Requer bastante meditação sobre os requisitos
- Aqui é um dos melhores lugares onde design patterns e uma arquitetura sã e
consistente brilham de verdade
- Vou falar sobre algumas técnicas e formatos interessantes
Relembrando: não existe escolha certa ou errada
Variabilidade e customização- Para configuração:
- Para instâncias de código isoladas: um arquivo de configuração (biblioteca DotEnv)
- Para instâncias unificadas: parâmetros de configuração no banco/repositório de tenant data
.env
Variabilidade e customização- Para o data storage:
- Migrations: funcionam como um controle
de versão do seu modelo de dados
- A maioria dos frameworks/ORMs possuem
suporte built-in (Laravel, CakePHP,
Doctrine), e há libs standalone para outros
formatos.
- Sempre criar migrations "reversíveis", ou
seja, que possam sofrer rollback
Variabilidade e customização- Para o código / funcionalidade: EVENTS
Transação de negócio (ex: novo pedido realizado)
Gerenciador de eventos (via Framework, biblioteca ou hand-made)
Envio de e-mail para o usuário
Registro no log de atividade do usuário
Sub-serviços do core app
Módulo customizado: NF-E
Geração de NF-E
Módulo customizado: Tracking
Registrar nova carga à entregar
dispatchEvent('order_created', $order);
- Para o código / funcionalidade: EVENTS
Variabilidade e customização
Transação de negócio (ex: novo pedido realizado)
Gerenciador de eventos (via Framework, biblioteca ou hand-made)
Envio de e-mail para o usuário
Registro no log de atividade do usuário
Sub-serviços do core app
Módulo customizado: NF-E
Geração de NF-E
Módulo customizado: Tracking
Registrar nova carga à entregar
dispatchEvent($order);
CORE APP
CUSTOM CODE
return [['Entregas','abc.delivery.index'],['Configurar Frete','abc.delivery.config']
];
- Para o código / funcionalidade: HOOKS
Variabilidade e customização
Renderizar menu principal Gerenciador de hooks triggerHook('render_menu');
return [['Pedidos','core.orders.index']];
return [['Estoque','core.inventory.index']];
Módulo de Pedidos
Módulo de Estoque
Módulos do core app
Módulo de Entregas
Custom code do cliente ABC
return [...];
Variabilidade e customização- Para o código / funcionalidade: DEPENDENCY INJECTION
Order
BillingGatewayContract
PayPalBillingGateway
implementaDI Container
registra
recebe PayPalBillingGateway
pede BillingGatewayContract
Variabilidade e customização- Para o código / funcionalidade: DEPENDENCY INJECTION
Order
BillingGatewayContract
PayPalBillingGateway
Core app
Order
BillingGatewayContract
PayPalBillingGateway
Core app
Order
BillingGatewayContract
PayPalBillingGateway
Core app
TENANT 1 TENANT 2 TENANT 3
PagSeguroBillingGateway
Variabilidade e customização- Para o código / funcionalidade: DEPENDENCY INJECTION
Order
BillingGatewayContract
PayPalBillingGateway
Core app
Order
BillingGatewayContract
PayPalBillingGateway
Core app
Order
BillingGatewayContract
PayPalBillingGateway
Core app
TENANT 1 TENANT 2 TENANT 3
Custom code
Variabilidade e customização- Para o frontend / interface:
- Dê preferência para usar uma engine de templates (Blade, Twig, etc);
Variabilidade e customização- Para o frontend / interface:
- Use Sass ou outro pré-compilador de CSS
- Você pode gerar o config via PHP, e então rodar
o compilador (automático ou manual)
Variabilidade e customização- Para infraestrutura / deploy:
Variabilidade e customização- Para infraestrutura / deploy:
Variabilidade e customização- Para infraestrutura / deploy:
http://forge.laravel.com
http://deployhq.comhttp://envoyer.io
Use ferramentas ou serviços de automatização de deploy(imagine atualizar manualmente a versão de 600 clientes)
Variabilidade e customização- Para infraestrutura / deploy:
Use o Composer para gerenciar core app e código customizável
Variabilidade e customização- Para infraestrutura / deploy:
Automatize (se aplicável) o processo de provisionamento para novos clientes
Novo cliente cadastrado
Script / microservice de PROVISIONAMENTO
Criar pasta no servidor e site no Nginx
Criar banco de dados
Gerar .env de configuração
Criar registro no DB de tenants
Cadastrar no Envoyer e realizar primeiro deploy
Gerar composer.json com Core + Custom code
Disparar e-mail para o cliente
Gerar config do Sass e compilar CSS final
Variabilidade e customização- Para infraestrutura / deploy:
Automatize (se aplicável) o processo de provisionamento para novos clientes
Novo cliente cadastrado
Script / microservice de PROVISIONAMENTO
Criar pasta no servidor e site no Nginx
Criar banco de dados
Gerar .env de configuração
Criar registro no DB de tenants
Cadastrar no Envoyer e realizar primeiro deploy
Gerar composer.json com Core + Custom code
Disparar e-mail para o cliente
Gerar config do Sass e compilar CSS final
Variabilidade e customização- Para infraestrutura / deploy:
- O custo e tempo para provisionamento interfere diretamente em um KPI de negócio,
o CAC (Custo de Aquisição de Cliente)
- Provavelmente toda sua infra-estrutura pode ser administrada via API; use isso a seu favor
- Amazon EC2 e DigitalOcean tem APIs para provisionamento de servidores
- Amazon S3 e Rackspace Cloud tem APIs para criação de novos "buckets" de arquivos
- Amazon Route 51 e DigitalOcean tem APIs para gerenciar o DNS (domínios customizáveis)
TL;DR- Oriente sua arquitetura principalmente pela sua
necessidade de flexibilidade e customização
- Mais customizável -> mais isolada
- Mais uniforme -> mais compartilhada
- Use as melhores práticas de SOLID, principalmente no
que diz respeito a Inversão de Dependência; a
arquitetura multitenant é provavelmente o melhor use-
case de substituição de implementações de uma
interface em runtime
TL;DR- Automatize TUDO que diz respeito a provisionamento,
deploy e housekeeping de infraestrutura: manutenção
manual de instâncias é uma eterna dívida técnica que se
acumula
- TDD é PRIMORDIAL; lembre-se que em um ambiente
sincronizado, uma atualização impacta TODOS os seus
clientes
Perguntas?
A está contratando!Procuramos desenvolvedores front-end e back-end, de TODOS os níveis de experiência,
apaixonados pelo que fazem e a fim de aprender e ensinar
Ambiente de trabalho bacana, descontraído, com remuneração competitiva, horários flexíveis e bastante abertura para novas idéias. Sem melindres, sem preciosismo e puxação de saco :D
Plano de carreira sólido e flexível, com espaço para crescimento em gestão e especialização, e programas de feedback contínuo entre a equipe e os gestores.
Manda um e-mail pra [email protected] e cite a palestra da PHP ConferenceOu me chame pessoalmente agora para trocarmos uma idéia
Começe 2016 de trampo novo!
Obrigado!
E-mail / Hangouts: [email protected]: http://facebook.com/aryel.tupinambaTwitter: http://twitter.com/DfKimeraLinkedIn: http://linkedin.com/in/aryeltupinamba
Slides da palestra: http://slideshare.net/aryeltupinamba
http://lqdi.net