# Padrões

Um plugin que segue o contrato *funciona*. Um plugin que segue os **padrões** compõe: convive com os outros plugins, aceita um backend real sem reescrita e é promovível a oficial. Esta página são as convenções que fazem essa diferença.

## Factory, não instância

Um plugin é exportado como uma **factory** — uma função que devolve o `PluginManifest` — não como um objeto pronto. Isso deixa quem monta o app injetar opções (um provider, uma posição de menu, um vertical) sem editar o plugin:

```ts
export function createFidelidadePlugin(options?: FidelidadePluginOptions): PluginManifest {
  // ...
}
```

Ligar o plugin é chamar a factory no array de plugins. Configurar é passar opções. Nunca é editar o interior do plugin.

## Provider-first: o dado é um contrato

A UI do plugin nunca fala com um banco direto. Ela fala com um **`DataProvider`** — uma interface que todo backend implementa. O plugin define o contrato em `data/types.ts`, entrega um `mock` e um `supabase`, e escolhe entre eles com `createSafeDataProvider`:

```ts
const provider =
  options?.dataProvider ??
  createSafeDataProvider(
    () => createSupabaseFidelidadeProvider(), // usado quando há backend
    () => createMockFidelidadeProvider(),     // fallback sem banco
  )
```

O ganho: o plugin roda no dia zero (mock), e trocar para dados reais não toca em nenhuma tela — só troca o provider. É o mesmo padrão que faz um plugin ser testável sem infra (veja [Testar e debugar](/pt-BR/docs/testar-e-debugar)).

## O boundary de provider: fale com o Fayz, não com o provedor

A regra que o `fayz doctor` fiscaliza: um app ou plugin **não** importa SDKs de provedor direto (`@supabase/supabase-js`, `mercadopago`, `stripe`, `googleapis`, …). Ele obtém acesso pelo boundary do Fayz — `getSupabaseClientOptional()` / a interface `DataProvider` — ou pelo spine de conectores.

```ts
import { getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core'
// ✔ passa pelo boundary do Fayz, escopa por tenant

// import { createClient } from '@supabase/supabase-js'
// -> o doctor acusaria isto como quebra de boundary
```

Existe uma exceção deliberada: um plugin app-local **pode** ser dono de um conector + Edge Function para um provedor que o Fayz ainda não suporta — porque a credencial fica no servidor e o app ainda chama o *próprio* boundary, não o provedor do navegador. Essa exceção é declarada com um marcador na linha do import, não casual.

## A superfície suportada

Um app só depende de pacotes da superfície pública suportada — `@fayz-ai/sdk`, `core`, `saas`, `ui`, `auth`, `db`, `storefront`, `shop` e os `@fayz-ai/plugin-*`. Importar um subpath interno ou um pacote fora dessa lista faz o doctor avisar (`off-surface-import` / `off-surface-dependency`). A regra existe para o upgrade ficar seguro: o que não está na superfície pode mudar sem aviso.

## Tabelas `plg_` e reúso do core

Por convenção, um plugin instala as **suas próprias tabelas** com o prefixo **`plg_`** (é o que shop e courses fazem — `plg_shop_products`, `plg_courses_*`) para deixar a posse óbvia. A convenção é **recomendada, não universal**: nem todo módulo a cumpre — o conector de openbanking, por exemplo, cria `bank_integrations` sem prefixo. O que o plugin **não** cria, ele **reutiliza**: referencia as tabelas da biblioteca [core](/pt-BR/docs/dados/modelo) que precisa em vez de copiá-las. Um plugin jamais redefine uma entidade do core; se precisa saber "quem marcou", referencia `public.people`. Uma fonte de verdade por entidade.

As migrations que o plugin embarca seguem regras que fazem o `fayz db apply` ser reproduzível:

- **ordenadas** — a sequência de aplicação é determinística.
- **versionadas** — cada migration tem versão; o runner sabe o que já rodou.
- **fix-forward** — migrations são append-only: nunca edite uma já aplicada, escreva a próxima que corrige. Não há `.down.sql`.
- **idempotentes** — rodar de novo não quebra nem duplica.

## Isolamento sempre

Toda tabela `plg_` que o plugin possui carrega `tenant_id` e RLS na forma canônica. Isso não é opcional nem "depois" — é a condição para a tabela existir no modelo. O detalhe está em [RLS e multi-tenant](/pt-BR/docs/dados/rls).

{% callout type="info" %}
A fiscalização é **soft** por design: o `fayz doctor` reporta essas violações como *avisos*, não falha o build. A ideia é visibilidade sem atrapalhar o DX de quem está experimentando. Mas um plugin que quer graduar a oficial precisa estar com o doctor limpo.
{% /callout %}

---

Próximo: exercite o app e pegue quebras cedo em [Testar e debugar](/pt-BR/docs/testar-e-debugar).
