Refatoração User-Centric com MembershipSettings
Data: 2026-01-XX
Status: 📋 Em Planejamento
📋 Contexto
No Arah, o modelo atual mistura identidade pessoal, vínculo territorial, verificação, participação econômica e permissões operacionais. Isso gerou ambiguidades e dificuldade de evolução.
Este trabalho refatora o modelo para recentralizar o User e separar corretamente:
- Identidade pessoal
- Vínculo territorial
- Processos de verificação
- Configurações do membro no território
- Capacidades operacionais
As decisões abaixo são definitivas e não devem ser reinterpretadas.
🎯 Princípio Estrutural (Não Violar)
User é a pessoa.
Membership é o vínculo territorial.
Verificação é confiança.
Settings são escolhas.
Capacidades são poderes operacionais.
📐 MODELO FINAL DE DOMÍNIO
1) User — Núcleo do Sistema
O User representa uma pessoa única e global.
Responsabilidades do User
- Identidade pessoal
- Confiabilidade global
- Autenticação e segurança
- Papel técnico global (Admin, se existir)
- Identidade verificada (global, obrigatória para features sensíveis)
Criar ou ajustar no User
UserIdentityVerificationStatus (enum)
Unverified- Identidade não verificadaPending- Verificação pendenteVerified- Identidade verificadaRejected- Verificação rejeitada (opcional)
Campos auxiliares:
IdentityVerifiedAtUtc?- Timestamp da verificação
Regra
Identidade verificada pertence exclusivamente ao User.
- Nunca pertence ao Membership nem ao Marketplace.
- É global e única por pessoa.
2) Territory — Disponibilidade de Funcionalidades
O Territory define o que existe naquele território.
Feature Flags
Usar feature flags existentes (ou criar se não houver):
Territory.FeatureFlags.MarketplaceEnabled = true|false
FeatureFlag enum deve incluir:
AlertPosts(existente)EventPosts(existente)MarketplaceEnabled(novo)
3) Membership — Vínculo Territorial (Contrato)
Membership representa o vínculo User ↔ Territory.
3.1 Papel Territorial
MembershipRole (enum existente)
Visitor- Visitante do territórioResident- Morador do território
3.2 Verificação de Residência (Processo Territorial)
ResidencyVerificationStatus (renomear de ResidencyVerification)
Unverified- Sem verificaçãoGeoVerified- Verificado por geolocalizaçãoDocumentVerified- Verificado por comprovante documental
Campos auxiliares:
LastGeoVerifiedAtUtc?LastDocumentVerifiedAtUtc?
3.3 Regra Estrutural de Residência
Um User comum pode ter apenas 1 Membership como Resident em todo o sistema.
- Admin global é exceção.
- Essa regra deve ser validada na camada de aplicação, não por constraint de banco.
- Um User pode ter múltiplos Memberships como Visitor.
4) MembershipSettings — Escolhas do Membro no Território
Esta entidade é obrigatória no modelo.
MembershipSettings representa configurações e opt-ins do membro dentro de um território.
Estrutura
public sealed class MembershipSettings
{
public Guid MembershipId { get; } // 1:1 com Membership
public bool MarketplaceOptIn { get; private set; }
public DateTime CreatedAtUtc { get; }
public DateTime UpdatedAtUtc { get; private set; }
}
Regras
- Criado automaticamente ao criar um Membership
- Relacionamento 1:1 com Membership
- Futuras configurações devem ser adicionadas aqui, não no Membership
5) Marketplace — Regra Composta (Sem Papel Novo)
Marketplace não é papel, não é identidade e não é verificação.
Condições para permitir ações de marketplace
Criar Store / Item: Exigir todas:
Territory.FeatureFlags.MarketplaceEnabled == trueMembershipSettings.MarketplaceOptIn == trueMembership.Role == ResidentMembership.ResidencyVerificationStatus != Unverified
Operar plenamente (publicar / responder interesse, se aplicável):
- Tudo acima +
User.IdentityVerificationStatus == Verified
Regra de Erro
- Negação por regra de domínio retorna HTTP 403 explícito.
- Não usar
Forbid()para regras de negócio.
6) Curador e Moderador — Capacidades Territoriais
Curador e Moderador não são papéis sociais nem verificações.
São capacidades operacionais, atribuídas ao Membership.
Modelo Fixo
Criar entidade:
public sealed class MembershipCapability
{
public Guid Id { get; }
public Guid MembershipId { get; }
public MembershipCapabilityType CapabilityType { get; }
public DateTime GrantedAtUtc { get; }
public DateTime? RevokedAtUtc { get; private set; }
public Guid? GrantedByUserId { get; }
public Guid? GrantedByMembershipId { get; }
public string? Reason { get; }
}
MembershipCapabilityType (enum)
Curator- Capacidade de curadoriaModerator- Capacidade de moderação
Regras
- Curador/Moderator atuam apenas no território do Membership
- Não alteram papel
- Não criam regras
- Não governam o território
- São capacidades empilháveis (um Membership pode ter múltiplas)
🔄 OPERAÇÕES MÍNIMAS A IMPLEMENTAR / AJUSTAR
1. Criar automaticamente MembershipSettings
- Ao criar um Membership, criar automaticamente MembershipSettings com valores padrão
2. Endpoint para atualizar MembershipSettings
- Ex.:
PUT /api/v1/memberships/{id}/settings - Permitir atualizar
MarketplaceOptIn
3. Ajustar endpoints de marketplace
- Usar:
- Territory feature flag
- Membership
- MembershipSettings
- User identity verification
4. Criar operações administrativas
- Conceder/remover capabilities
POST /api/v1/memberships/{id}/capabilitiesDELETE /api/v1/memberships/{id}/capabilities/{capabilityId}
5. Garantir autorização
- Sempre baseada no Membership ativo
- Ajustar
AccessEvaluatorpara usarMembershipCapabilityao invés deUserRole.Curator
6. Ajustar testes e retornos HTTP
- HTTP 403 explícito para regras de negócio negadas
✅ Critérios de Sucesso
- User é fonte única de identidade verificada
- Membership contém apenas vínculo e residência
- MembershipSettings concentra escolhas do membro
- Marketplace funciona por composição de regras
- Curador/Moderator são capacidades empilháveis
- Modelo permanece simples, extensível e sem ambiguidades
- Build e testes passam
📊 Diagrama de Relacionamentos
User (1) ──< (N) Membership (1) ──< (1) MembershipSettings
User (1) ──< (N) Membership (1) ──< (N) MembershipCapability
Territory (1) ──< (N) Membership
Territory (1) ──< (N) FeatureFlag
🔍 Validação do Modelo
Pontos de Validação
-
Separação de Responsabilidades
- ✅ User: identidade global
- ✅ Membership: vínculo territorial
- ✅ MembershipSettings: escolhas do membro
- ✅ MembershipCapability: poderes operacionais
-
Verificações
- ✅ UserIdentityVerificationStatus: global, no User
- ✅ ResidencyVerificationStatus: territorial, no Membership
-
Marketplace
- ✅ Regras compostas (não é papel)
- ✅ Depende de Territory, Membership, MembershipSettings e User
-
Capacidades
- ✅ Territoriais (não globais)
- ✅ Empilháveis
- ✅ Não alteram papel social
-
Extensibilidade
- ✅ Novas configurações em MembershipSettings
- ✅ Novas capacidades em MembershipCapability
- ✅ Novos feature flags em Territory
📝 Mudanças no Código
Domínio
-
User.cs
- Adicionar
UserIdentityVerificationStatus IdentityVerificationStatus - Adicionar
DateTime? IdentityVerifiedAtUtc - Método
UpdateIdentityVerification()
- Adicionar
-
TerritoryMembership.cs
- Renomear
ResidencyVerificationparaResidencyVerificationStatus(apenas nome interno, enum permanece)
- Renomear
-
Novas Entidades
MembershipSettings.csMembershipCapability.csMembershipCapabilityType.cs(enum)
-
FeatureFlag.cs
- Adicionar
MarketplaceEnabled = 3
- Adicionar
Infraestrutura
-
Tabelas
membership_settings(1:1 comterritory_memberships)membership_capabilities(N:1 comterritory_memberships)- Adicionar colunas em
users:identity_verification_status,identity_verified_at_utc
-
Repositórios
IMembershipSettingsRepositoryIMembershipCapabilityRepository
Aplicação
-
MembershipService
- Criar
MembershipSettingsautomaticamente ao criarMembership
- Criar
-
MembershipAccessRules
- Atualizar regras de marketplace para usar novo modelo
-
AccessEvaluator
- Substituir
IsCurator(User)porHasCapability(Membership, CapabilityType)
- Substituir
-
StoreService
- Usar novas regras de marketplace
🚀 Plano de Implementação
Fase 1: Domínio
- Criar enums e entidades de domínio
- Atualizar User
- Validar modelo conceitual
Fase 2: Infraestrutura
- Criar migrations
- Criar repositórios
- Atualizar mappers
Fase 3: Aplicação
- Atualizar services
- Atualizar regras de acesso
- Criar endpoints de settings
Fase 4: Testes
- Testes unitários
- Testes de integração
- Validação de regras de negócio
⚠️ Notas Importantes
- Não violar o princípio estrutural: User é pessoa, Membership é vínculo, Settings são escolhas, Capacidades são poderes.
- Regra de 1 Resident por User: Validar na aplicação, não no banco.
- HTTP 403 explícito: Para regras de negócio negadas.
- Extensibilidade: Novas configurações em MembershipSettings, novas capacidades em MembershipCapability.
🔄 Pontos de Atenção na Migração
1. UserRole.Curator → MembershipCapability
Impacto: 29 ocorrências encontradas no código.
Locais que precisam ser atualizados:
AccessEvaluator.IsCurator()→HasCapability(Membership, CapabilityType)UserRepository.ListUserIdsByRoleAsync(UserRole.Curator)→ Buscar por capabilities- Controllers: AlertsController, AssetsController, FeaturesController, MapController, ModerationController, PlatformFeesController, JoinRequestsController
- Services: StoreService, StoreItemService, EventsService, JoinRequestService
- Handlers: ReportCreatedNotificationHandler
Estratégia de migração:
- Criar
MembershipCapabilitypara cada User comUserRole.Curatorem todos os seus Memberships - Manter
UserRole.Curatortemporariamente para compatibilidade - Atualizar código gradualmente
- Remover
UserRole.Curatorapós migração completa
2. ResidencyVerification
Status: Enum já existe e está correto.
Nota: O enum ResidencyVerification permanece com o nome atual. Internamente, pode ser referenciado como "status de verificação de residência" mas o nome do enum não precisa mudar.
3. Verificação de Identidade Global
Novo: UserIdentityVerificationStatus é completamente novo.
Migração: Todos os Users existentes devem iniciar com Unverified.
4. MembershipSettings
Novo: Entidade completamente nova.
Migração: Criar MembershipSettings para todos os TerritoryMembership existentes com valores padrão:
MarketplaceOptIn = false(padrão conservador)