Esta aplicação não é multi-inquilino
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_ide 16 tabelas de domínio sem essa coluna. - O
user_iddiz quem é dono de uma linha; uma chave de inquilino diz de qual partição ela é. Mesmo formato, consequência diferente quando esquecida. - O
Identity::Currentdeclara 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.
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.