Ir para o conteúdo
DedicatedPHP Contato

Como projetar limites de confiança entre módulos PHP sem duplicar regras

Defina o que cada módulo PHP pode assumir, o que deve validar ao receber dados e como testar se a autorização e a propriedade dos dados são mantidas.

Diagrama conceitual de módulos PHP separados por contratos e controles de autorização

Em uma aplicação PHP modular, uma fronteira não é apenas uma separação de pastas, classes ou namespaces. Ela também determina quais dados um módulo pode aceitar, quais operações pode executar e a quem cabe verificar cada condição. Se essas decisões ficarem implícitas, os módulos poderão confiar demais uns nos outros, duplicar validações ou expor dados que não deveriam controlar.

Projetar limites de confiança entre módulos PHP significa explicitar essas responsabilidades e mantê-las verificáveis à medida que o sistema muda. Isso não implica tratar cada chamada interna como tráfego hostil nem repetir todas as regras em cada camada. Significa identificar onde a autoridade muda, definir contratos claros e validar cada operação no módulo competente para executá-la.

Por que uma fronteira entre módulos também é uma fronteira de confiança

Por que uma fronteira entre módulos também é uma fronteira de confiança — guía visual de DedicatedPHP

Um módulo pode consumir dados de outro por meio de uma interface interna, de um evento, de uma API ou de uma consulta compartilhada. Em todos os casos, convém perguntar quais garantias o emissor oferece e quais o receptor precisa verificar. A localização do código não basta para responder: um método PHP chamado diretamente pode receber dados derivados de uma requisição externa, de uma tarefa em segundo plano ou de outro módulo que evolui separadamente.

Diferencie validação de formato, invariantes de negócio e autorização. A validação de formato verifica se um identificador ou uma data tem uma representação aceitável. Uma invariante garante, por exemplo, que uma operação não tenha um valor negativo. A autorização determina se o ator pode realizar essa operação sobre esse recurso. São controles relacionados, mas não intercambiáveis.

O limite de confiança fica especialmente evidente quando mudam o ator, a propriedade do dado ou o canal de entrada. Por exemplo, um módulo de faturamento pode receber um pedido de outro módulo, mas não deve deduzir que a pessoa que iniciou a requisição tem permissão para alterar qualquer fatura. A identidade e o contexto devem ser fornecidos de forma explícita e verificável.

Inventariar os dados e as operações que cruzam cada fronteira

Comece mapeando as interações relevantes, em vez de redesenhar toda a aplicação. Para cada fronteira, documente quem chama, quem é responsável pela operação, quais dados são trocados e se a chamada é síncrona ou assíncrona. Inclua os caminhos menos visíveis: comandos de console, processos agendados, consumidores de filas e tarefas administrativas.

  • Dados: identifique os campos transferidos, sua origem e se contêm informações pessoais, sensíveis ou pertencentes a outro domínio.
  • Operações: registre quais alterações são solicitadas e qual módulo deve aplicá-las.
  • Contexto: determine qual ator, organização, tenant ou finalidade deve acompanhar a solicitação.
  • Resultados: especifique quais informações o módulo pode retornar e quais erros pode comunicar.

Esse inventário ajuda a detectar sinais de risco: um módulo consulta tabelas alheias para evitar uma interface; recebe um identificador de recurso sem verificar a quem pertence; ou aceita um objeto de domínio mutável criado por outro módulo. Também revela duplicações: vários consumidores implementam suas próprias interpretações de uma regra cuja autoridade pertence a um único domínio.

Definir contratos explícitos: formato, invariantes e compatibilidade

Um contrato descreve o que o receptor precisa para executar uma operação e quais garantias oferece ao responder. Em PHP, ele pode ser expresso por DTOs imutáveis, interfaces, tipos bem definidos ou esquemas para mensagens, de acordo com a arquitetura. A forma concreta importa menos que a clareza: evite passar entidades completas quando o consumidor precisa apenas de alguns valores.

O contrato deve especificar campos obrigatórios, limites e significado. Um customerId, por exemplo, não indica por si só se identifica um cliente global, um cliente pertencente a uma organização ou um cliente visível para um usuário específico. Se o contexto condiciona a operação, inclua-o ou defina como o módulo responsável o obtém.

Defina também erros distinguíveis. Uma entrada malformada não é o mesmo que uma operação não autorizada, um recurso inexistente ou um conflito com o estado atual. Os detalhes expostos a quem faz a chamada devem ser suficientes para tratar o resultado, sem revelar dados que facilitem enumerar recursos ou obter informações de terceiros.

Os contratos devem evoluir com compatibilidade planejada. Adicionar um campo opcional pode ser menos disruptivo do que alterar o significado de um campo existente, mas, ainda assim, requer revisar produtores e consumidores. Em mensagens assíncronas, considere que uma versão antiga pode continuar circulando depois da implantação de código novo. Não confunda a implantação do código com a ativação de um novo comportamento: use uma transição controlada quando produtores e consumidores não mudarem ao mesmo tempo.

Validar autorização e propriedade onde a operação é executada

A validação sintática pode ocorrer perto da entrada, mas a autorização deve ser verificada no contexto que conhece o recurso e rege a ação. O módulo proprietário de uma fatura, por exemplo, tem as informações necessárias para decidir se ela pode ser emitida ou alterada. Não deve delegar essa decisão a um módulo chamador que conhece apenas a identidade do usuário.

Uma verificação útil relaciona ator, ação, recurso e contexto: quem solicita qual operação, sobre qual recurso e dentro de qual organização ou tenant. Não presuma que um identificador recebido equivale a uma permissão. Verifique a propriedade no limite que executa a operação, mesmo que a requisição venha de código interno considerado confiável. Isso evita que uma rota alternativa ou uma mudança posterior de arquitetura contorne uma autorização aplicada somente na interface web.

Evite, contudo, replicar a política completa em todos os módulos. Centralize a decisão no módulo competente e exponha uma operação ou serviço com um contrato claro. Os módulos consumidores podem fazer verificações por conveniência para melhorar a experiência, mas não devem se tornar a única barreira de segurança.

Evitar que entidades compartilhadas criem acesso privilegiado

Compartilhar entidades ORM entre módulos parece prático, mas pode criar dependências ocultas. Se vários módulos carregarem e alterarem a mesma entidade, qualquer um deles poderá contornar as operações que deveriam proteger suas invariantes. Além disso, alterações de esquema ou de comportamento afetam consumidores que não controlam o modelo.

Prefira transferir os dados necessários por meio de comandos, consultas ou DTOs. O módulo proprietário mantém a responsabilidade de persistir e validar suas invariantes; o consumidor não precisa de acesso geral a seus repositórios ou tabelas. Se uma leitura for necessária, projete uma consulta que retorne o conjunto mínimo de dados e respeite as permissões e o escopo definidos.

Um banco de dados compartilhado não obriga a compartilhar autoridade. Mesmo antes de separar fisicamente os dados, é possível estabelecer que apenas um módulo escreve em determinadas tabelas e que os demais utilizam interfaces documentadas. Essa regra reduz o custo de uma separação futura e facilita detectar acessos fora do contrato.

Testar se o isolamento é mantido quando o sistema muda

Os testes devem cobrir tanto o caminho válido quanto as tentativas de cruzar uma fronteira sem permissão. Os testes unitários verificam as regras do módulo proprietário; os testes de integração verificam contratos, persistência e configuração de autorização entre componentes. Inclua casos em que um ator válido tenta operar sobre um recurso de outro tenant, um recurso inexistente e um contexto incompleto.

  • Verifique que um identificador válido, por si só, não concede acesso.
  • Confirme que erros de autorização não retornam dados do recurso protegido.
  • Teste se os consumidores não podem alterar invariantes por meio de entidades compartilhadas ou acesso direto a repositórios.
  • Execute testes de contrato quando produtores e consumidores forem implantados de forma independente.

Os testes devem refletir a política real, não apenas verificar se uma exceção é lançada. Garanta que a alteração não seja persistida, que nenhum evento indevido seja publicado e que a repetição da operação não produza efeitos parciais. Para fluxos com várias etapas, defina o que acontece se uma etapa falhar e como o estado é reconciliado.

Revisar as fronteiras ao extrair um serviço ou alterar a propriedade

Revisar as fronteiras ao extrair um serviço ou alterar a propriedade — guía visual de DedicatedPHP

A extração para um serviço separado torna visíveis pressupostos que antes ficavam ocultos na memória: identidade, permissões, latência, novas tentativas, duplicatas e disponibilidade. O contrato deve transportar contexto suficiente e considerar falhas de rede. Não transforme uma chamada remota em uma cópia de uma chamada local sem revisar timeouts, idempotência e comportamento diante de erros.

Revise também as fronteiras quando mudar quem é responsável por um dado, quando um tenant for incorporado, quando um novo canal for adicionado ou quando surgirem novos consumidores. Pergunte quem pode escrever, quem pode ler, qual módulo decide a autorização e quais garantias são mantidas durante a migração. Mantenha essas respostas junto à documentação do contrato e aos testes correspondentes.

Um limite de confiança eficaz não depende de todos os módulos permanecerem no mesmo processo nem de a equipe se lembrar de convenções informais. Ele é reconhecível porque a autoridade está atribuída, os dados que cruzam a fronteira têm escopo limitado, as permissões são verificadas onde a operação é executada e os testes detectam acessos não autorizados. Assim, o isolamento é protegido sem transformar cada módulo em uma réplica das regras dos demais.

Deseja aplicar essas ideias ao seu projeto?Vamos discutir sua plataforma PHP.
Veja os serviços relacionados