Esta aplicação não é multi-inquilino

Prévia grátis 5 min de leitura

Multi-inquilino é mais fácil de entender a partir de uma aplicação que não é, porque a diferença dá para medir.

A medição

tabelas: 33

com user_id (13):
  bookmarks, community_comments, enrollments, experience_points,
  identity_authentications, lesson_completions, lesson_notes,
  partner_referrals, quiz_attempts, solution_reveals, submissions,
  user_badges, user_preferences

sem user_id (20):
  action_text_rich_texts, active_storage_attachments,
  active_storage_blobs, active_storage_variant_records, badges,
  categories, challenges, courses, lessons, levels,
  partner_api_tokens, partner_course_sponsorships,
  partner_handoff_nonces, partner_inquiries, partner_roles,
  partners, quiz_options, quiz_questions, quizzes, users

É o db/schema.rb em 2026-10-05. Quatro das vinte são infraestrutura do Rails (Action Text e Active Storage), então o formato real é vinte e nove tabelas de domínio: treze com um user_id e dezesseis sem.

Por que user_id não é uma chave de inquilino

O assunto inteiro se apoia nessa distinção, e as duas parecem idênticas num esquema.

O user_id responde "quem é dono desta linha?". Uma conclusão de lição pertence a quem estuda. A conclusão de outra pessoa é outra linha, e as duas são dados comuns numa tabela compartilhada.

O account_id responde "de qual partição de dados é isto?". Duas contas nunca podem ver as linhas uma da outra. Uma consulta que devolve os dados de uma conta para outra é um vazamento de dados, não um bug numa funcionalidade.

A diferença está no que acontece quando alguém esquece o escopo.

Esqueça o user_id numa consulta de conclusões de lição e alguém vê o número de progresso errado, o que é constrangedor. Esqueça o account_id na mesma consulta numa aplicação multi-inquilino e um cliente vê os dados de outro, o que é um incidente com consequências jurídicas.

flowchart TB
  U["user_id: quem é dono desta linha"] --> U1["esquecido: um número errado numa página"]
  A["account_id: de qual partição isto é"] --> A1["esquecido: um cliente vê os dados de outro"]
  U1 --> D["mesmo formato de coluna, consequência diferente"]
  A1 --> D

O que falta

O Identity::Current tem um atributo:

module Identity
  class Current < ActiveSupport::CurrentAttributes
    attribute :user
  end
end

Numa aplicação multi-inquilino, é aqui que o inquilino fica, porque é o único lugar que toda camada consegue ler sem que ele passe por toda assinatura de método. Não existe attribute :account, e não há nada que justifique um.

As dezesseis tabelas de domínio sem user_id se dividem em grupos. Nove são o catálogo compartilhado (courses, lessons, challenges, quizzes, quiz_questions, quiz_options, categories, levels, badges), e estão corretas como estão: toda pessoa que estuda vê os mesmos cursos, de propósito. Quatro pertencem a um parceiro por meio do partner_id (partner_api_tokens, partner_course_sponsorships, partner_handoff_nonces, partner_roles), que é o mais perto que esta aplicação tem de uma chave de inquilino: os tokens de API de um parceiro nunca podem ser vistos por outro. O resto são os próprios donos (partners, users) e os contatos recebidos de parceiros.

Esse é o fato que importa. Uma versão multi-inquilino desta aplicação não acrescentaria uma chave de inquilino a vinte e nove tabelas, porque o catálogo é compartilhado. A lição 3 trata de como uma tabela faz jus à chave ou não.

Quando isso chegaria

Uma frase muda tudo: "uma empresa quer comprar cinquenta licenças e ver o progresso do time dela."

Isso não é um pedido de relatório. Isso cria uma entidade acima da pessoa usuária. Com ela vêm um admin que vê as conclusões de cinquenta pessoas e de mais ninguém, um fluxo de convite e uma relação de cobrança. E uma regra: os dados de uma empresa nunca aparecem nos de outra.

Quem estuda continua dividindo o catálogo. Então a resposta não é "pôr escopo de inquilino em tudo". É uma decisão tabela por tabela, e é por isso que a medição acima é o ponto de partida certo.

Por que aprender isso antes que chegue

Porque a escolha é cara de mudar e barata de preparar.

Os padrões da lição 2 diferem em custo de migração, isolamento e trabalho operacional, e trocar de um para outro depois significa uma migração de dados em todos os inquilinos. Já a preparação sai quase de graça. Saiba quais tabelas seriam compartilhadas, mantenha o escopo por dono explícito em vez de implícito e não crie um índice único global que teria de virar um índice por inquilino.

A lição 6 é essa lista de preparação, e nada nela exige decidir algo hoje.

Pontos-chave

  • Medido em 2026-10-05: 33 tabelas, 13 com user_id e 16 tabelas de domínio sem essa coluna.
  • O user_id diz quem é dono de uma linha; uma chave de inquilino diz de qual partição ela é. Mesmo formato, consequência diferente quando esquecida.
  • O Identity::Current declara um atributo, e um inquilino ficaria ali.
  • As tabelas do catálogo são compartilhadas de propósito, e as de parceiros já têm uma chave de parceiro; a chave de inquilino é uma decisão tabela por tabela.
  • A escolha é cara de mudar e quase de graça de preparar.
Aula 1 de 7 · grátis

Continue com a aula 2

Três padrões

Compara os níveis de linha, esquema e banco em isolamento, custo de migração, conexões e forma de vazamento.

Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.

Digite para buscar...