Projetando a Próxima Camada da PromoBet: Quotas de Assinatura, PromoGiros e Capacidade Expansível
A etapa mais recente do desenvolvimento da PromoBet tem sido menos sobre adicionar uma funcionalidade isolada
Da Refatoração do UserArea ao Próximo Modelo de Recursos
A etapa mais recente do desenvolvimento da PromoBet tem sido menos sobre adicionar uma funcionalidade isolada e mais sobre garantir que a plataforma esteja estruturalmente preparada para as funcionalidades que virão a seguir.
A recente refatoração do UserArea é um bom exemplo dessa direção. O frontend agora possui uma separação mais clara entre a camada de orquestração client-side e as seções de apresentação que consomem esse estado. O UserAreaClient centraliza o estado interativo e distribui as informações relevantes para seções mais específicas, enquanto as atividades do Global Catalog e dos tenants podem ser tratadas como experiências distintas.
Esse trabalho foi consolidado na PR #34, Refactor/user area new UI new structure, que foi integrada em 7 de setembro de 2026 após 35 commits. A PR introduziu a nova camada UserAreaClient, separou as interações do Global Catalog e dos tenants, reorganizou as interfaces de quota e atividade do usuário e estabeleceu a base para futuras funcionalidades de engajamento e assinatura.
PromoBet — PR #34: Refactor/user area new UI new structure
O próximo passo é utilizar essa estrutura para formalizar algo que já existe implicitamente no backend:
Nem todo recurso de giro representa o mesmo tipo de recurso.
Essa distinção se torna especialmente importante à medida que a PromoBet avança em direção a assinaturas, giros promocionais e, futuramente, capacidade adicional adquirida pelos tenants.
O Modelo Atual de Quotas
Atualmente, os giros normais de um usuário são determinados pela sua assinatura.
Os três planos atuais são:
| Plano | Giros globais | Multiplicador de tenant | Quota semanal de tenant | Quota mensal de tenant |
| -------- | ------------: | ----------------------: | ----------------------: | ---------------------: |
| Free | 10 | 0x | 25 | 100 |
| Premium | 20 | 0,5x | 50 | 200 |
| Premium+ | 30 | 1x | 100 | 400 |Isso cria dois limites relacionados, mas distintos.
O primeiro é a quota normal de giros do usuário.
Para um giro global, a assinatura determina diretamente o limite diário.
Para um giro dentro de um tenant, o limite é composto pela capacidade base do tenant mais o bônus de tenant derivado da assinatura do usuário.
Conceitualmente:
Quota de giro do tenant =
Capacidade da assinatura do tenant
+
Bônus da assinatura do usuárioA segunda restrição está relacionada ao uso acumulado do usuário dentro dos tenants.
O uso em tenants é contabilizado separadamente através de contadores semanais e mensais. Isso impede que um usuário consuma recursos de tenants indefinidamente, mesmo quando sua quota diária de giros ainda permitiria novas execuções.
O fluxo atual de giro, portanto, precisa satisfazer ambas as condições:
Quota diária de giros do usuário
+
Restrição de uso semanal/mensal em tenantsSomente depois que essas verificações são aprovadas o giro é executado.
Essa separação já é bastante útil.
O próximo passo é garantir que ela não seja acidentalmente destruída quando introduzirmos recursos adicionais de giro.
PromoGiros Não São Quota
A primeira distinção arquitetural que precisamos estabelecer é simples:
Um PromoGiro é uma fonte de giros adicionais, e não um aumento da quota renovável do usuário.
Pode parecer uma diferença apenas semântica, mas ela possui consequências importantes.
Uma quota de assinatura é renovável.
Por exemplo:
Premium
20 giros globais normais/diaQuando o dia muda, essa capacidade normal volta a ficar disponível.
Um PromoGiro é diferente:
Usuário recebe:
+5 PromoGirosEsses cinco giros devem permanecer disponíveis até serem consumidos.
Eles não devem desaparecer porque a quota diária normal foi renovada.
Eles não devem fazer parte de spinsToday.
Eles não devem aumentar a quota Premium de 20 para 25.
Em vez disso:
Assinatura
└── 20 giros renováveis
PromoGiros
└── 5 giros adicionais consumíveisO usuário passa a ter acesso a 25 possíveis giros, mas a contabilidade continua separada:
Quota normal:
20 disponíveis
Saldo de PromoGiros:
5 disponíveisEssa distinção mantém os dois recursos observáveis e gerenciáveis de forma independente.
PromoGiros Devem Pertencer ao Usuário
Existe outra propriedade importante dos PromoGiros:
Eles não são específicos de um tenant.
Um PromoGiro deve poder ser utilizado nos dois contextos de giro existentes na plataforma:
Global Catalog
ou
Catalog do TenantIsso significa que os PromoGiros pertencem ao usuário, e não a uma carteira específica associada a um tenant.
Conceitualmente:
USUÁRIO
│
┌─────────┴─────────┐
│ │
Assinatura PromoGiros
│ │
Giros normais Créditos extras de giro
│ │
┌────┴────┐ ┌───┴───┐
│ │ │ │
Global Tenant Global TenantIsso torna os PromoGiros especialmente úteis como mecanismo promocional no futuro.
Eles poderão ser concedidos por diferentes mecanismos sem que o motor de giro precise mudar:
- recompensas de onboarding;
- campanhas;
- conquistas;
- indicações;
- eventos sazonais;
- concessões administrativas;
- campanhas promocionais;
- futuramente, compras.
O sistema de giro precisa apenas entender que o usuário possui um recurso adicional de giro que pode ser consumido.
PromoGiros Devem Ignorar as Restrições de Uso dos Tenants
Essa provavelmente será a interação mais importante entre o novo recurso e o sistema de quotas existente.
Atualmente, giros em tenants estão sujeitos às restrições semanais e mensais de uso do usuário.
Isso é intencional.
Entretanto, um PromoGiro deve fornecer um caminho alternativo quando essas restrições normais forem esgotadas.
Por exemplo:
Usuário Premium
Quota semanal de tenant:
0 restantes
PromoGiros:
3 restantesUm giro normal dentro de um tenant deve ser rejeitado.
Porém, um PromoGiro ainda deve poder ser utilizado.
Portanto:
Giro normal em tenant
↓
Verificar quota da assinatura
↓
Verificar restrição semanal/mensal do tenant
↓
Consumir recurso normalEnquanto:
Giro de PromoGiro em tenant
↓
Verificar saldo de PromoGiros
↓
NÃO consumir quota semanal/mensal
↓
Consumir 1 PromoGiroEsse modelo é muito mais limpo do que simplesmente aumentar os contadores de quota existentes.
A restrição do tenant continua sendo uma restrição sobre o uso normal financiado pela assinatura.
Os PromoGiros fornecem um recurso independente que pode intencionalmente ultrapassar essa restrição.
Cooldown É Diferente de Quota
O fluxo atual de giro também possui outro mecanismo que deve permanecer independente: o cooldown.
Essa distinção é importante.
Quota responde:
Quantos giros este usuário pode consumir?
Cooldown responde:
Com que frequência este usuário pode executar um giro?
São perguntas diferentes.
Os PromoGiros devem ignorar as restrições de quota, mas não devem ignorar o cooldown anti-abuso.
O modelo futuro deve ser:
Giro por assinatura
│
├── Quota normal
├── Restrição de tenant quando aplicável
└── Cooldown
PromoGiro
│
├── Saldo de PromoGiros
├── Não consome quota de tenant
└── CooldownIsso preserva a proteção existente contra requisições repetidas em alta frequência, ao mesmo tempo em que permite que os recursos promocionais cumpram seu propósito.
O Giro Precisa Saber Qual Recurso Financiou Sua Execução
Essa distinção leva naturalmente a uma possível evolução no domínio de spin.
Em vez de tratar todo giro bem-sucedido simplesmente como:
spin = trueo sistema deverá eventualmente saber qual recurso financiou aquele giro.
Por exemplo:
source: "subscription"ou:
source: "promoGiro"Essa informação se torna valiosa por vários motivos.
O histórico do usuário poderá distinguir atividades normais de atividades promocionais.
As métricas poderão responder perguntas como:
Quantos giros vieram de assinaturas?
Quantos vieram de PromoGiros?
Qual foi a efetividade de uma campanha promocional?A plataforma também poderá distinguir outras origens no futuro:
subscription
promoGiro
reward
admin
purchase
campaignO ponto importante é que o giro continua sendo executado pelo mesmo mecanismo.
O que muda é apenas o recurso consumido para financiá-lo.
O Mesmo Princípio Se Aplica aos Tenants
Isso nos leva naturalmente ao segundo recurso futuro:
ExtendableQuotas.
No lado dos tenants existe um problema diferente.
Um tenant possui uma capacidade mensal normal determinada por sua assinatura ou plano.
Essa capacidade existe para proteger a plataforma e tornar previsível o consumo de recursos.
Porém, alguns tenants podem legitimamente possuir um volume de tráfego muito maior.
Para esses tenants, uma restrição mensal absoluta pode acabar sendo contraproducente.
Por exemplo:
Assinatura do tenant:
100 de capacidade mensalO tenant chega a:
100 / 100Nesse momento, o sistema poderia simplesmente impedir novos usos normais.
Mas isso nem sempre é desejável.
Em vez disso, o tenant poderia adquirir capacidade adicional:
Capacidade mensal base:
100
Quota expandida:
+500
Capacidade efetivamente disponível:
600A característica fundamental é que essa capacidade adicional não faz parte da quota mensal renovável.
Ela é um recurso adicional adquirido pelo tenant.
É essencialmente o equivalente, no lado do tenant, aos PromoGiros.
PromoGiros e ExtendableQuotas Formam um Modelo Simétrico
Quando esses conceitos são separados, a arquitetura fica muito mais fácil de compreender.
USUÁRIO
│
├── Assinatura
│ └── Quota renovável de giros
│
└── PromoGiros
└── Giros adicionais consumíveis
TENANT
│
├── Assinatura
│ └── Capacidade renovável
│
└── ExtendableQuotas
└── Capacidade adicional consumívelA diferença está em quem possui o recurso e o que esse recurso representa.
Lado do usuário
PromoGiro
"Este usuário pode executar um giro adicional."
Lado do tenant
ExtendableQuota
"Este tenant pode acomodar atividade adicional além da sua capacidade normal."
Ambos são recursos adicionais.
Nenhum deles deve redefinir a assinatura subjacente.
Por Que Isso Importa Antes do Stripe
À primeira vista, esses conceitos podem parecer funcionalidades de pagamento.
Eventualmente, serão.
Mas eles não deveriam ser implementados como funcionalidades de pagamento.
O provedor de pagamentos deve ser responsável apenas por estabelecer que uma compra ou assinatura ocorreu.
A aplicação deve então transformar esse evento em um recurso interno.
Conceitualmente:
Pagamento
↓
Stripe
↓
Webhook
↓
Aplicação
↓
Conceder recursoPor exemplo:
Assinatura Premium adquirida
↓
subscription = premiumou:
Pacote de PromoGiros adquirido
↓
promoGiros += 10ou:
Pacote de capacidade do tenant adquirido
↓
extendableQuota += 500O motor de giro nunca deveria precisar perguntar ao Stripe:
"Este usuário pode girar?"Ele deve perguntar ao próprio domínio da aplicação:
"Quais recursos este usuário possui disponíveis?"Isso mantém a integração de pagamentos substituível e evita que conceitos de billing contaminem o núcleo da mecânica do jogo.
A Refatoração do Frontend Faz Parte Dessa Arquitetura
É também por isso que a recente refatoração do UserArea é mais importante do que pode parecer inicialmente.
A nova estrutura fornece uma camada dedicada de orquestração client-side, enquanto permite que as seções individuais permaneçam focadas em apresentação e interação.
A PR integrada introduziu o UserAreaClient, reorganizou o UserArea em seções dedicadas, separou atividade do Global Catalog e dos tenants e introduziu as bases de UI para quotas e PromoGiros.
Isso significa que agora existe um local apropriado para expor esses novos conceitos sem tornar o UserArea inteiro dependente de seus detalhes de implementação.
Por exemplo:
UserAreaClient
│
├── Assinatura
│
├── Quota restante
│
├── PromoGiros
│
├── Atividade de Tenant
│
├── Atividade Global
│
└── TroféusCada seção pode evoluir de maneira independente à medida que o modelo de recursos do backend se torna mais completo.
Preparando a Próxima Fronteira entre Server e Client
Existe ainda outro benefício arquitetural na direção atual.
O objetivo não é transformar todas as páginas em Client Components simplesmente porque alguma parte da página precisa de interatividade.
A estrutura atual cria espaço para caminharmos em direção a uma fronteira mais deliberada entre Server Components e Client Components.
A direção futura pode ser:
Server Component
│
├── autenticação/sessão
├── dados iniciais
├── metadata
├── responsabilidades de SSR
│
└── Client Component
│
├── interação
├── estado local
├── animações
└── comportamento exclusivo do browserIsso se torna particularmente útil conforme a PromoBet ganha mais capacidades no nível das páginas.
No futuro, páginas poderão aproveitar geração de metadata no servidor, uma renderização inicial mais eficiente e preparação de dados server-side sem que toda a rota precise ser executada como um Client Component.
A decisão arquitetural importante, portanto, não é simplesmente "usar Server Components".
É:
Manter uma fronteira intencional entre server e client, para que o comportamento interativo exista onde realmente é necessário sem tornar a rota inteira interativa por padrão.
A estrutura atual do UserAreaClient fornece uma fronteira prática para continuarmos nessa direção.
O Próximo Passo de Implementação
Com a arquitetura estabelecida, a próxima implementação não deveria começar pelo Stripe.
Ela deveria começar pelo modelo interno de recursos.
A sequência fica melhor representada assim:
1. Definir o recurso PromoGiro
↓
2. Adicionar a contabilidade de PromoGiros
↓
3. Ensinar o spin a consumir PromoGiros
↓
4. Preservar cooldown e tracking
↓
5. Validar comportamento Global + Tenant
↓
6. Expor PromoGiros no UserArea
↓
7. Definir ExtendableQuota para Tenants
↓
8. Integrar pagamentos posteriormenteIsso permite validar toda a experiência de usuário e tenant antes de introduzir a complexidade do processamento de pagamentos.
Isso é especialmente útil porque a plataforma já pode validar a interação entre:
Usuário
Tenant
Assinatura
Global Catalog
Catalog de Tenant
Quotas Normais
Restrições de Tenant
PromoGiros
Cooldowns
Histórico
Estatísticassem tornar o Stripe uma dependência do processo de desenvolvimento.
Separação Proposta das Branches
Essa abordagem também fornece uma separação natural para o próximo trabalho.
O frontend pode evoluir em torno de:
UI de PromoGiros
Estado da assinatura do usuário
Recursos restantes
Apresentação do UserArea
Visibilidade da origem do giroEnquanto o backend pode se concentrar em:
Domínio de PromoGiros
Contabilidade de PromoGiros
Consumo de recursos pelo spin
Resolução de quotas
Restrições de tenant
Futuro domínio de ExtendableQuotaEssa separação acompanha a arquitetura que já está sendo estabelecida, em vez de criar outra funcionalidade transversal.
O Panorama Maior
O que inicialmente parece ser apenas um sistema de assinaturas está se tornando algo um pouco mais interessante.
A PromoBet está caminhando para um modelo baseado em recursos, no qual as assinaturas estabelecem direitos renováveis, enquanto compras adicionais e recompensas fornecem recursos consumíveis independentes.
Essa distinção oferece muito mais flexibilidade para a plataforma.
Uma assinatura pode definir:
"O que este usuário normalmente recebe?"Um PromoGiro pode definir:
"Qual atividade adicional foi concedida a este usuário?"A assinatura de um tenant pode definir:
"Qual nível de capacidade a plataforma normalmente oferece a este tenant?"E uma ExtendableQuota pode definir:
"Qual capacidade adicional este tenant adquiriu?"O motor do jogo passa então a ser o consumidor comum desses recursos, em vez de ser o lugar onde lógica de assinatura, promoção e pagamento são misturadas.
Essa é a direção que vale a pena estabelecer antes de implementar as próximas branches.
O objetivo da próxima fase, portanto, ainda não é construir pagamentos.
É garantir que as experiências de usuário e tenant funcionem corretamente quando diferentes tipos de recursos coexistirem.
Quando esse modelo estiver estável, o Stripe poderá ser conectado como uma camada de integração sobre um domínio que já está bem definido, em vez de ser algo que dita como o próprio domínio deve funcionar.
Próximo Marco
O marco imediato é:
Implementar PromoGiros como um recurso de giro independente, pertencente ao usuário e consumível.
Eles devem:
- funcionar tanto no Global Catalog quanto nos catálogos de tenants;
- sobreviver ao reset normal da quota diária;
- não aumentar nem modificar a quota renovável da assinatura;
- não consumir a restrição semanal/mensal do usuário em tenants;
- continuar respeitando o cooldown normal de giro;
- ser registrados como uma origem distinta de giro;
- ser exibidos através do UserArea.
Depois que esse comportamento estiver validado, os mesmos princípios poderão ser aplicados às ExtendableQuotas dos tenants.
Somente depois disso fará sentido conectar esses recursos aos fluxos reais de pagamento.
O objetivo arquitetural é simples:
Assinaturas definem acesso renovável. Recursos promocionais e adquiridos fornecem capacidade adicional. O motor de giro consome recursos sem precisar saber de onde eles vieram.
Essa separação dá espaço para a PromoBet crescer sem transformar o núcleo do jogo em um sistema de pagamentos.