Jump to content
Douglas Motta

Gerenciamento de Aplicação Multi Instance/Tenancy em PHP

Recommended Posts

Olá, pessoal.

Desenvolvi uma aplicação em PHP para um segmento específico onde cada empresa terá essa aplicação. 

Minha dúvida é quanto ao gerencimento deste sistema, tendo em vista que cada empresa poderá personalizar a aplicação conforme suas necessidades (locais, legislação, negócio, etc.). Porém a maioria das funcionalidades será comum a todas as empresas (mas nada garante que uma funcionalidade que é comum hoje vai ser comum a todas amanhã). Por questões de segurança cada empresa terá seu banco de dados.

Estou entrando no mercado com o primeiro cliente e estou pensando no momento que tiver 30 clientes como vai ser. Utilizo o Bitbucket para controle de versão.

Qual é a melhor forma de gerenciar o codebase e o codecustom desta aplicação?  Um branch para cada cliente? Submodulos do Git? Pela própria aplicação?

Alguém que já vivenciou algo parecido que pode me ajudar?

Obrigado.

Share this post


Link to post
Share on other sites

No decorrer da minha experiência como dev, eu vi muitas decisões ruins, algumas delas eu mesmo sugeri. Mas, a que mais me impressionava era quanto ao projeto base e suas customizações.


Pensando apenas em code (sem entrar em banco de dados), eu utilizaria uma mescla entre o GIT e o composer.

 

Branch's e divisões

 

No GIT:

  • Dividir as branchs em 5 tipos (serão mais branchs): master, cutom-*, homologacao-*, dev-* e hotfix-*;
  • A branch master será única e exclusivamente para o código base não customizado;
  • A branch custom-*, será específica para o código homologado do projeto customizado para uma empresa (custom-empresa01, custom-empresa02, etc..);
  • A branch homologacao-* será específica para homologar a transição do código de dev para prod, aonde testes de alto nível são aplicados. Cada branch de produção (master/custom) terá sua respectiva branch de homologação (homologacao-master, homologacao-custom-empresa01, etc);
  • A branch dev-* é aonde é realizado o desenvolvimento, ela são livres, aonde o "desenvolvedor não conhece as leis" ou "o filho chora e mãe não vê", etc.. Você deve ter quantas forem necessárias, para cada implementação, cada GAP, cada moficação (dev-master-task-2784, dev-custom-empresa01-task-3500, etc...);
  • A branch de hotfix-* é aquela correção rápida, aquele conflito do merge entre uma entre o código base e uma customização ou que está implicando em parada geral do sistema (aquele bug ou erro de português que passou pela homologação/testes), que não necessariamente deve passar por todos os passos de desenvolvimento/homologação/produção. O que nós normalmente chamamos de "codificar em prod"... Sim, isso existe na realidade de todos já fizeram.

Essa é a definição da árvore do projeto. Os caminhos a serem seguidos:

  • dev-master-* <--> homologacao-master <--> master --> homologacao-custom-* <--> custom-(todas as empresas);
  • dev-custom-* <--> homologacao-custom-* <--> custom-*;
  • hotfix-* <--> para aonde for necessário.

Perceba que no fluxo, uma custom nunca deve levar o seu código para a master. Apenas a master pode levar códigos para a custom. Sempre utilizando a branch de homologação da custom.

 

Fork

 

Uma característica bem interessante dos serviços de controle de versionamento (bitbucket, github, etc...) é a possibilidade de criar um fork de um projeto. O seu projeto CORE será o repositório principal e, para cada cliente, você criará um novo fork. Logo, você terá o projeto master e os forks para cada cliente (master -> cliente-01 -> cliente-02, etc...).

 

A divisão entre branchs, deve ser a mesma exemplificada exclusiva para a master. Ou seja, 4 tipos: master, homologação, dev-* e hotfix-*). A nomenclatura das branchs (mster, homologacao, dev-* e hotfix-*) segue a mesma definição anteriormente explicada.

 

A parte de fluxo será o que está exemplificado abaixo:

  • dev-* <--> homologacao <--> master;
  • hotfix-* <--> para onde for necessário.

 

O cuidado, vem na hora de sincronizar os forks. O BitBucket, por exemplo, lhe sugere um fork automático entre as branchs masters de cada repositório. Entretanto, para um melhor controle e validação do novo código, a sincronização deve ocorrer entre a branch master, do projeto master, para a branch homologacao do projeto customizado. Ou seja:

 

  • Master:master --> Cliente-01:homologacao;

 

Dessa forma, você pode homologar, corrigir e enviar para a produção (master) da customização de clientes.

 

No BitBucket, para realizar tal feito, você pode ir no menu compare, escolha  a branch da qual está o código (parte de baixo) e a branch na qual você quer enviar o novo código (branch de cima). Compare as diferenças e, no canto direito superior, o BitBucket lhe exibirá o botão merge. Feito o merge, o processo é exatamente o mesmo que utilizando branchs.

 

Módulos

 

Aqui é que entra a questão de módulos. Caso você implemente partes do sistema como módulos, você pode usar tanto o git-submodule ou o composer. Com ambos você poderá utilizar repositórios privados para baixar as suas dependências. Para cada módulo, você deverá ter o repositório separado e, caso o repositório separado seja customizado, poderá seguir a estruturação acima mencionada.

 

Entretanto, no caso de módulos personalizados, acredito que com o git submodule não seja possível separar entre versões (branch/tags) específicas para cada projeto/branch. No caso do composer é possível, pois, cada branch/projeto, terá seu próprio composer que pode ser personalizado.

 

Banco de dados

 

Nesse caso, vai depender muito do que você irá utilizar para gerenciar o SGBD. Eu utilizei por muito tempo o MySQL WorkBench. Ele funciona perfeitamente quando o assunto é modelagem e sincronização. Entretanto, o formato de arquivo gerado por ele não é aberto e, em consequência, não pode ser versionado. Logo, quando uma tabela precisar ser diferente em algum projeto, você terá dois arquivos distintos.

 

Há, também, a possibilidade de tratar tudo na forma de SQL scripts. Mas ficaria sem uma ferramenta de apoio. Talvez alguém conheça alguma ferramenta melhor para a situação.

 

A contra-partida

 

Quando você separa pequenas partes de um projeto em módulos e os adiciona como dependências do seu projeto, você pode encontrar uma situação denominada  Dependency Hell. Esse termo também pode ser utilizado para a manutenção do código entre as branchs, pois, todas as suas customizações necessitam estarem atualizadas com a branch master. E se você almeja 30 empresas, pense que uma mudança no CORE precisa ser homologada para as 30 empresas.

 

Considerações

 

Sendo dicas como elas são, apenas dicas, você pode considerar demasiada estrutura e simplista ou exagerada demais e partir para uma abordagem meio termo ou mais robusta. Tudo dependerá de onde você está e para onde gostaria de chegar.

 

Existem outras ferramentas e abordagens que pode lhe ajudar. Uma delas seria a integração contínua, o qual incluí N outras ferramentas de auxílio.

 

Update

 

Um sistema que gerencia suas próprias customizações acaba sendo limitado a campos e novas informações. Eu, particularmente, não gosto muito da prática. Entretanto, para evitar o problema com o SGBD, pode-se utilizar tabelas de cadastros par atributo-valor para cruzar informações dinâmicas e reduzir a necessidade de criar novas colunas e tabelas. Mas, é uma questão bem limitada.

 

Update 2

 

Com a nova parte de forks (que eu não havia me lembrado anteriormente), seria a sugestão mais indicada. Pois, se você possuir uma equipe de desenvolvimento específica para cada cliente, poderá gerenciar muito melhor as permissões e acessos. O controle é bem superior a de um repositório apenas.

Edited by Gabriel Heming
explicação sobre fork e novo posicionamento
  • +1 4

Share this post


Link to post
Share on other sites

Olá Gabriel. Muito obrigado pela resposta.

 

Você detalhou bastante e tirou as dúvidas que eu tinha em minha cabeça com relação à esse gerenciamento. Poderia tirar uma útlima dúvida? Com relação ao projeto, a estrutura de pastas, etc. Eu uso Symfony e gostaria de saber se para cada novo cliente eu crio um projeto novo no PhpStorm e clono o Core ou eu consigo gerenciar tudo isso com apenas um projeto no PhpStorm? Tem uma estrutura de pastas para esta arquitetura?

 

Valeu mesmo.

Share this post


Link to post
Share on other sites

Apesar de, estruturalmente, tudo estar em um mesmo projeto. Seria necessário instalar as dependências, pelo composer, para cada projeto. Isso seria necessário sempre que mudar de uma branch de um projeto para outro.

 

Então, sim, é melhor ter um projeto separado para cada cliente.

 

Outro detalhe, que eu não comentei anteriormente é sobre o fork. Para cada cliente pode ser criado um fork do projeto (vou atualizar a resposta anterior).

  • +1 1

Share this post


Link to post
Share on other sites

Atualizei o post principal. E, nesse momento, acredito que a solução entre forks do projeto seja a mais indicada.

  • +1 1

Share this post


Link to post
Share on other sites
1 hora atrás, Douglas Motta disse:

Tem uma estrutura de pastas para esta arquitetura?

 

Não, apenas que exista uma estrutura definida e com coerência (e não seja uma bagunça).

 

A que um framework, como Symfony, lhe oferece estará de bom tamanho.

  • +1 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Similar Content

    • By Guilherme Scarpatto
      Estou fazendo um website de perguntas e respostas, na qual o usuário adiciona sua pergunta e outros usuários podem respondê-las, basicamente como esse. Eu consegui fazer até a parte que o usuário cadastra a pergunta no banco, agora eu empaquei na parte de mostrar esssas perguntas na tela inicial do site para que outros usuários possam respondê-las. Queria uma ajuda de como posso fazer isso, qualquer coisa posso postar os cógidos aqui depois.
    • By cleriston.melo
      Olá pessoal,
       
      Estou com a seguinte dúvida:
      Preciso impimir as informações de um array criado a partir de uma consulta ao banco de dados, mas preciso imprimir essas informações "quebrada".
      Ex: Nas informações contidas no array, tem cod_exp, controle, serie, cod_prod e quant_prod.
      Eu preciso imprimir as informações por serie.tipo:
       
      Série: 1
      (cod_prod)                     quant_prod
      Produto 1                          10
      Produto 2                          30
      Produto 3                          20
      Produto 4                          50
      ---------------
      Série: 2
      (cod_prod)                     quant_prod
      Produto 1                          10
      Produto 2                          30
      Produto 3                          20
      Produto 4                          50
      ---------------
      etc...
      ----------------------------------------
      O código é esse:
       
      <?php
                      $query_prod_exp = "select * from estoque_expositor where est_exp_cod_exp=$cod_exp and est_exp_ativo='S' order by est_exp_cod_exp";
                      $result_prod_exp = mysqli_query($con, $query_prod_exp) or die(mysqli_error());
                      $row_prod_exp = mysqli_fetch_array($result_prod_exp);
                      
                      $controle = $row_prod_exp['est_exp_controle'];
                      $serie = $row_prod_exp['est_exp_controle'];
                      $cod_prod = $row_prod_exp['est_exp_cod_prod'];
                      $saldo_atual = $row_prod_exp['est_exp_quant_prod'];
                      
                          // ESCREVE UMA LINHA NA TABELA
                          echo "<tr>";
                      
                          // ESCREVE UMA COLUNA NA TABELA E EXIBE O NOME DO PRODUTO
                          echo "<td>";
                          $query_prod = "select * from produtos where prod_id=$cod_prod";
                          $result_prod = mysqli_query($con, $query_prod) or die(mysqli_error());
                          while ($row_prod = mysqli_fetch_array($result_prod)) {
                              $prod = $row_prod['prod_nome'];
                              echo $prod."</td>";
                          }
                          echo "<td>" . $saldo_atual . "</td>";
                          echo "</tr>";
                  ?>
       
      Obrigado a todos pela ajuda...
    • By rohilario_
      Galera, boa tarde!
      Estou desenvolvendo um sistema com PHP, Mysql, JS e Bootstrap, que gerencia sorteios conforme cadastro.
      Tudo funcionando perfeitamente, contudo atualmente fazemos impressões em um layout e colocamos em uma urna, na qual acontece o sorteio manual..
      Gostaria, de alguma forma, criar uma tela de sorteio, onde ele buscaria as informações do banco (coluna cartela) e sorteasse um número de cartela qualquer com alguns efeitos de "suspense".
       
      O grande problema para mim até o momento: Fraco em JS apesar de fazer algumas "coisinhas"; Não faço ideia nem por onde começar, comecei tão somente lendo alguns efeitos com jquery.
      https://api.jquery.com/category/effects/
      Não sei como pegar as informações do banco mysql com JS.
      Sugeri o JS por conta das libs disponíveis, mas poderia ser com o proprio PHP...
       
      Agradeço as colaborações desde já!
    • By flarique
      iae pessoal, então eu estou querendo fazer o envio de um email do meu formulário por intermédio de um php que estou testando em um servidor ativo!
      este é o formulário e o meu problema é explicitamente com a passagem de informação do bootstrap 4 escrito em html para o php.
      este é o formulário.
      <!-- Formulario --> <div class="container"> <div class="row"> <div class="col-12 text-center my-5"> <h1 class="display-4 text-danger"><i class="fa fa-paper-plane text-danger" aria-hidden="true"></i>Enviar e-mail</h1> </div> </div> <div class="row justify-content-center mb-5"> <div class="col-sm-12 col-md-10 col-lg-8"> <form action="../Teste.php" method="post" role="form"> <div class="form-row"> <div class="form-group col-sm-6"> <label class="text-dark" for="InputName" >Nome completo</label> <input type="text" class="form-control" id="InputName" name="nome" required placeholder="Nome"> </div> </div> <div class="form-row"> <div class="form-group col-sm-12"> <label class="text-dark" for="inputEnd">Email</label> <input type="email" class="form-control" id="inputEnd" nome="email" required placeholder="Endereço completo"> </div> </div> <div class="form-row"> <div class="form-group col-sm-6"> <label class="text-dark" for="inputCidade">Sua cidade</label> <input type="text" class="form-control" id="inputCidade" name="cidade" required placeholder="Cidade"> </div> </div> <div class="form-row"> <div class="col-sm-12"> <button type="submit" class="btn btn-primary">Enviar</button> <a tabindex="0" class="btn btn-secondary ml-2" role="button" data-toggle="popover" data-placement="right"data-trigger="focus" title="Ajuda!" data-content="Preença todas as caixas.">Ajuda</a> </div> </div> </form> </div> </div> </div> e este é o php que deve receber as informações do formulario pelo method="post" e o action="../Teste.php" mas as variaveis do php não mudam.
      este é o php.
      <?php $name = $_POST['name']; $email = $_POST['email']; $cidade = $_POST['cidade']; $email_from = 'Teste'; $email_subject = 'Teste2'; $email_body = "Name: $name.\n". "Email: $email.\n". "Cidade: $cidade.\n"; $to = "enriboliveira@gmail.com"; $headers = "From $email_from \r\n"; $headers .= "Reply-to: $email \r\n"; echo "nome : $name"; ?>  
    • By Nipo
      Estou com dúvida se tem como fazer um código em php para fazer upload de uma imagem de um diretório fixo(em vez de abrir uma janela para fazer upload) para um diretório para o servidor.
       
×

Important Information

Ao usar o fórum, você concorda com nossos Terms of Use.