WordPress Plugin Patterns — Arquitetura Reutilizavel
Padroes comprovados extraidos do middag-account v4. Claude Code: aplique estes padroes ao trabalhar no plugin middag-account.
Companion Rules
| Rule | Foco |
|---|---|
wp-plugin-best-practices.md | Checklist de qualidade (15 secoes) |
wp-plugin-workflow.md | Ciclo de desenvolvimento (setup ate ship) |
wp-plugin-testing-strategy.md | Piramide de testes e cobertura |
wp-plugin-security-checklist.md | Deep-dive de seguranca |
1. DDD Light + Symfony DI Container
Pattern: Domain logic isolada do WordPress. Symfony DI Container 7.4 para autowiring.
src/
├── Core/ # Container, Kernel, ServiceProvider
├── Domain/{Entity}/ # Pure PHP — ZERO WordPress deps (15 dominios)
├── Api/ # REST controllers (~81 endpoints)
├── Integration/ # HubSpot, Stripe, ISSNet, Banco Inter, Cloudflare
├── UI/ # InertiaAdapter, PageControllers
└── WordPress/ # CMS abstraction layerRules:
Domain/NUNCA importa funcoes WP/WC- Direcao:
WordPress/ -> Domain/, nunca o inverso - Constructor injection everywhere, zero static service calls
- Auto-discovery por sufixo de classe: Service, Repository, Controller, Handler, Hooks, Registrar
Integration/acessaDomain/via interfaces, nunca direto
15 Dominios v4: Organization, Collaborator, Order, Invoice, TaxInvoice, License, Contract, Document, Entitlement, Environment, Service, ServiceRequest, Quote, Affiliate, Download
Porque funciona: Domain testavel sem WP bootstrap. WP API changes isoladas na camada WordPress/. Integracoes isoladas em Integration/.
2. HookInterface Auto-Discovery
Pattern: Todos WordPress hooks registrados via classes implementando HookInterface.
interface HookInterface {
public function register(): void;
}HookRegistrar escaneia WordPress/Hook/ por arquivos *Hooks, instancia via Container, chama register().
Beneficios: Sem add_action() espalhados. Cada hook class e single-responsibility. Container injeta dependencias.
Prefixo padrao: middag/ para todos hooks customizados (ex: middag/entitlement/created, middag/order/status_changed).
3. Inertia.js Full Admin App
Pattern: Admin UI completa via Inertia.js v2 + React 19. Nao React Islands.
// PHP: InertiaAdapter renderiza pagina completa
class InertiaAdapter {
public function render(string $component, array $props = []): void {
// Renderiza o shell HTML com dados Inertia inline
// React 19 hydrata a pagina completa no client
}
}// PHP: PageController usa Inertia para resposta
class OrganizationPageController {
public function index(): void {
$this->inertia->render('Organization/Index', [
'organizations' => $this->service->list(),
'filters' => request()->query(),
]);
}
}// React: pagina Inertia recebe props server-side
export default function OrganizationIndex({ organizations, filters }) {
return (
<Layout>
<OrganizationTable data={organizations} filters={filters} />
</Layout>
);
}Build: Vite 6 -> IIFE single bundle (assets/dist/app.js). Tailwind v4. Assets apenas em telas relevantes.
Diferenca chave do React Islands: Inertia gerencia navegacao SPA completa. Nao ha multiplos mount points — ha um unico app React que recebe props do servidor via protocolo Inertia.
4. Repository Bridge (Domain <-> wp_posts)
Pattern: Domain entity e pure PHP. Repository mapeia para/de wp_posts via CPT.
Domain/Organization/OrganizationEntity.php # Pure PHP, no WP
Domain/Organization/OrganizationRepositoryInterface.php # Contract
WordPress/Repository/OrganizationRepository.php # Maps wp_posts <-> OrganizationEntity
WordPress/Repository/CctOrganizationRepository.php # Maps JetEngine CCT <-> OrganizationEntityDual Repository Pattern (migracao):
// Toggle alterna entre CCT e wp_posts
if (get_option('middag_migration_complete')) {
// Usa OrganizationRepository (wp_posts)
} else {
// Usa CctOrganizationRepository (JetEngine CCT)
}Domain nunca toca WP_Query ou get_post_meta(). Repository faz todo o mapeamento.
5. Custom Post Type Registration
Pattern: Cada dominio registra seu CPT com prefixo middag_.
class OrganizationCptRegistrar {
public function register(): void {
register_post_type('middag_organization', [
'labels' => [...],
'public' => false,
'show_ui' => false, // UI via Inertia, nao WP admin
'supports' => ['title', 'custom-fields'],
'capability_type' => 'middag_organization',
'map_meta_cap' => true,
]);
}
}Rules:
- CPT slugs:
middag_{domain}(max 20 chars) show_ui => false— toda UI via Inertia admin app, nao WP admincapability_typecustom para controle granular de permissoes- Meta keys mapeados no Repository class correspondente
- Rewrite rules flush na ativacao/desativacao
6. Activation/Deactivation/Uninstall Triad
| Hook | Arquivo | Responsabilidades |
|---|---|---|
| Activation | Kernel::onActivate() | Criar tables (dbDelta), registrar CPTs, add capabilities, flush rewrites |
| Deactivation | Kernel::onDeactivate() | Unschedule crons, remover capabilities, flush rewrites |
| Uninstall | uninstall.php | Drop tables, deletar options, limpar transients, remover caps, CPT data |
Rule: Activation/deactivation reversiveis. Uninstall destrutivo (apenas no delete do plugin).
7. Entitlement Aggregator (Hub Central)
Pattern: Entitlement e o dominio agregador que conecta todos os outros dominios. Modelo inspirado no Atlassian SEN.
Codigo: {CLASSE}-{ANO}{MES}{SEQ:4d}
Exemplo: PLG-20260401427 Classes de Entitlement:
| Classe | Codigo | Dominio Conectado |
|---|---|---|
| Plugin | PLG | License, Download |
| Ambiente | ENV | Environment |
| Labs | LAB | Service (tipo Labs) |
| Dev | DEV | Service (tipo Dev) |
| Pedido | ORD | Order, Invoice |
| Treinamento | TRN | (futuro) |
| Afiliado | AFL | Affiliate |
Fluxo:
HubSpot -> Quote -> aceite -> pagamento -> Entitlement auto-criado
-> License criada (se PLG)
-> Environment criado (se ENV)
-> Service criado (se LAB/DEV)Rule: Todo recurso provisionado para cliente passa pelo Entitlement. E o ponto unico de rastreabilidade.
8. Multi-Integration Pattern (HubSpot/Stripe Dual-Account)
Pattern: Integracoes externas isoladas em Integration/ com interface abstrata.
src/Integration/
├── HubSpot/
│ ├── HubSpotClientInterface.php # Contract
│ ├── HubSpotClient.php # HTTP client
│ ├── HubSpotWebhookHandler.php # Processa webhooks
│ └── HubSpotSyncService.php # Sincronizacao bidirecional
├── Stripe/
│ ├── StripeClientInterface.php
│ ├── StripeClient.php
│ ├── StripeWebhookHandler.php # Signature validation
│ └── StripeDualAccountService.php # Live + Test accounts
├── BancoInter/
│ ├── BancoInterGateway.php # Override pagador com CNPJ Org
│ └── BancoInterWebhookHandler.php
├── ISSNet/
│ └── ISSNetService.php # Nota fiscal de servico
└── Cloudflare/
└── CloudflareService.php # DNS/SSL para environmentsStripe Dual-Account:
// Suporte a contas live e test simultaneas
class StripeDualAccountService {
public function getClient(string $mode = 'live'): StripeClient {
return match($mode) {
'live' => new StripeClient($this->config->liveKey()),
'test' => new StripeClient($this->config->testKey()),
};
}
}Webhook Security: Cada provider valida assinatura do webhook antes de processar (ver wp-plugin-security-checklist.md).
9. Template System (Theme-Overridable Blocks)
Pattern: Templates em templates/. Theme overrides em themes/child-theme/middag-account/.
Block system: Blocos reordenaveis renderizados por TemplateRenderer. Ordem configuravel via filter. Before/after hooks por bloco.
10. CI/CD Pipeline
develop -> Quality Gate (test + style + stan)
main -> Quality Gate + Manual Release
tags/** -> Build Assets + ZIP + Deploy
pull-requests -> Quality GateDistribuicao: composer install --no-dev + Vite build -> ZIP. .distignore + .gitattributes alinhados.
11. i18n English-First
Source strings sempre em ingles. Traducoes via .po/.mo. Scripts: composer i18n:pot + composer i18n:mo.
12. REST API v4 Conventions
Namespace: middagaccount/v4
Auth tripla:
- WP Nonce (usuarios logados via admin)
- JWT RS256 (API externa — portal NextJS, mobile)
- WC Consumer Keys (integracoes legacy)
Estrutura de rotas por dominio:
/wp-json/middagaccount/v4/organizations
/wp-json/middagaccount/v4/organizations/{id}/collaborators
/wp-json/middagaccount/v4/entitlements
/wp-json/middagaccount/v4/orders
/wp-json/middagaccount/v4/invoices
/wp-json/middagaccount/v4/licenses
/wp-json/middagaccount/v4/environments
/wp-json/middagaccount/v4/services
/wp-json/middagaccount/v4/service-requests
/wp-json/middagaccount/v4/quotes
/wp-json/middagaccount/v4/documents
/wp-json/middagaccount/v4/contracts
/wp-json/middagaccount/v4/affiliates
/wp-json/middagaccount/v4/downloads
/wp-json/middagaccount/v4/tax-invoicesOrganization-scoped: Toda request inclui X-Middag-Organization header para multi-tenant isolation.
Response envelope: RestResponse helper garante formato consistente em todos ~81 endpoints.
13. Capability Hierarchy
Capabilities customizadas com user_has_cap filter expansion. Registered na ativacao, removidas na desativacao. Organization-scoped para multi-tenant.
14. Cron with Domain Delegation
WP Cron agenda evento. Handler e thin wrapper que resolve service do DI Container. Business logic em domain service testavel.
Replication Checklist
Ao criar novo dominio no middag-account:
| Arquivo | Proposito |
|---|---|
src/Domain/{Name}/{Name}Entity.php | Entidade pura PHP |
src/Domain/{Name}/{Name}RepositoryInterface.php | Contrato do repositorio |
src/Domain/{Name}/{Name}Service.php | Logica de negocio |
src/WordPress/Repository/{Name}Repository.php | Implementacao wp_posts |
src/WordPress/Repository/Cct{Name}Repository.php | Implementacao JetEngine CCT |
src/WordPress/Hook/{Name}Hooks.php | Hooks WordPress |
src/Api/{Name}Controller.php | REST API controller |
src/UI/{Name}PageController.php | Inertia page controller |
ui/src/Pages/{Name}/Index.tsx | React page component |
tests/Unit/Domain/{Name}/{Name}EntityTest.php | Unit test |
Extraido de: middag-account v4 — MIDDAG, Abril 2026