Pular para o conteúdo principal

Framework backend · Em uso

Archon Framework

A fundação backend em .NET sobre a qual todo o ecossistema é construído. Em vez de cada API reimplementar tenant, autenticação, autorização e auditoria, o Archon resolve essas decisões uma vez e as entrega prontas para quem nasce em cima dele.

Diagrama das quatro camadas do Archon Framework, Api, Infrastructure, Application e Core, com a dependência apontando para dentro, e a lista de capacidades transversais resolvidas uma vez.
Stack
.NET · C# · Clean Architecture · EF Core · FluentMigrator · JWT e OIDC · Multi-tenant · OpenTelemetry
Status
Em uso

Em resumo

O Archon Framework é a fundação backend em .NET do ecossistema. Ele padroniza, num único lugar, tudo o que é transversal a uma API multi-tenant: identidade, autorização, persistência, auditoria e contrato de resposta, de forma que sistemas novos nascem consistentes e os existentes evoluem sem divergir.

É o projeto que melhor mostra a diferença entre construir aplicações e construir a plataforma que sustenta aplicações, porque uma decisão errada aqui se propaga para todos os sistemas que vivem em cima dele.

Diferenciais

  1. Clean Architecture com direção de dependência preservada, domínio isolado da infraestrutura
  2. Multi-tenant por conexão com banco próprio por tenant (PostgreSQL, SQL Server ou MySQL) e resolver plugável
  3. Tenant resolvido só depois da autenticação, a partir de uma claim validada, nunca de um token decodificado sem checagem
  4. Autenticação JWT validada com autoridade e chaves resolvidas em runtime via discovery OIDC e JWKS, sem chave fixa por app
  5. Autorização por capacidade de módulo (ver, editar, excluir), com usuário e serviço no mesmo atributo e resposta 402 quando a assinatura está bloqueada
  6. Auditoria automática na mesma transação da mudança, com rollback se a auditoria falhar, e eventos de domínio só depois do commit
  7. Migrations por tenant com FluentMigrator, aplicadas no bootstrap e no startup, que falha se algum tenant divergir
  8. Observabilidade com OpenTelemetry (traces e métricas via OTLP) e 131 casos de teste cobrindo pipeline, auditoria e autorização

A decisão por trás do projeto

Toda API de um ecossistema interno acaba repetindo os mesmos problemas: descobrir o tenant da requisição e sua conexão, validar o usuário e a sessão, expor endpoints protegidos sem duplicar roteamento, devolver respostas no mesmo formato e auditar mudanças no banco. Resolver isso caso a caso significa segurança inconsistente, manutenção cara e plataforma lenta para evoluir.

O Archon é a resposta a isso: uma fundação única que centraliza essas decisões. Um sistema novo não começa do zero, começa com multi-tenant, auth, autorização, auditoria e contrato de API já resolvidos. Identity Management e Integration Platform são os dois maiores exemplos disso, ambos construídos sobre ele.

Arquitetura em camadas

A solução segue Clean Architecture com a direção de dependência apontando para dentro: Core (tipos fundamentais, sem dependência de framework) ← Application (contratos e interfaces) ← Infrastructure (EF Core, migrations, clientes de identidade) ← Api (pipeline HTTP, middlewares, atributos). A camada Api referencia tudo; a Core não referencia nada.

Essa direção é o que mantém o domínio isolado da infraestrutura: dá para trocar o resolver de tenant, o provider de banco ou o cliente de identidade sem tocar nas regras centrais.

Multi-tenant por conexão

O tenant é resolvido por requisição, depois da autenticação, a partir de uma claim validada (tenant_id ou contract_id do token já verificado, nunca de um payload decodificado sem checagem de assinatura), e populado num contexto que toda a infraestrutura subsequente consulta. Cada tenant pode viver num provedor de banco diferente (PostgreSQL, SQL Server ou MySQL), configurado por tenant.

A resolução é um ponto de extensão: um resolver lê os tenants da configuração (com cache por TTL e semáforo para evitar leitura concorrente do mesmo tenant), outro busca os tenants de um catálogo externo de identidade, com fallback para um snapshot local se o catálogo estiver fora do ar. O efeito prático é que um sistema consumidor escreve seu domínio como se fosse single-tenant; o framework garante que cada requisição opere na conexão certa, mesmo quando a fonte de verdade está temporariamente indisponível.

Autenticação JWT dinâmica

Em vez de fixar a chave de validação na configuração de cada aplicação, o framework resolve a autoridade global (do Identity Management) em runtime, via discovery OpenID e jwks.json, com as chaves em cache por alguns minutos. A assinatura do token é validada contra essas chaves, junto com issuer, audience e tempo de vida, com tolerância de um minuto de clock skew. Conexões via SignalR levam o token pela query string, já que o cabeçalho Authorization não está disponível no handshake.

A confiança é descoberta em tempo de requisição, não codificada. Trocar uma chave no centro de identidade se propaga sem redeploy das APIs consumidoras.

Autorização por capacidade, num atributo só

A autorização inteira passa por um único atributo, RequireAccess, que aceita dois mundos ao mesmo tempo:

  • usuário: Bearer JWT, autorizado por root=true, por uma capacidade de módulo (modulo.ver, modulo.editar, modulo.excluir, inferida do verbo HTTP) ou pela permissão legada controller.action;
  • serviço: segredo de integração via Basic Auth (tenantId:apiKey) ou header X-Api-Key, com comparação em tempo constante.

Quando a assinatura do tenant está bloqueada por inadimplência, o atributo devolve 402 por padrão, a menos que o endpoint esteja marcado com [AllowWhenSubscriptionBlocked]. Unificar os dois mundos num atributo eliminou a necessidade de um atributo separado para integração entre serviços; quando a autorização vem por segredo, o próprio atributo já resolve e popula o tenant da requisição.

Auditoria automática

A auditoria não é código espalhado por entidade: ela acontece dentro do SaveChangesAsync do contexto base. Antes do commit, o framework preenche os timestamps, tira um snapshot do que foi inserido, alterado e excluído (propriedade a propriedade) e persiste os registros de auditoria na mesma transação da mudança de negócio, de forma que uma falha na auditoria desfaz a mudança inteira. Só depois do commit são despachados os eventos de domínio acumulados.

Cada registro carrega quem alterou (resolvido do usuário autenticado), o tenant e o id de correlação da requisição (o mesmo trace id). Qualquer entidade do ecossistema ganha auditoria completa só por herdar da entidade base.

Persistência por convenção

A base de dados aplica convenções automáticas ao mapear as entidades (chave long, timestamps preenchidos sozinhos, tamanho padrão de string, precisão padrão de decimal, DateTimeOffset convertido para UTC, exclusão restritiva, identificadores em minúsculo), e configurações manuais continuam podendo sobrescrever quando preciso. Cada tenant roda suas próprias migrations com FluentMigrator; se a migration de qualquer tenant falhar, o startup da API falha junto, em vez de deixar um tenant com schema desatualizado passar despercebido.

Contrato de API padronizado

Toda resposta sai no mesmo envelope (mensagem, dados, erros, paginação, com campos nulos omitidos), e os controllers base já entregam isso pronto: helpers de contexto da requisição, retornos HTTP tipados, envio de arquivos (PDF, Excel, CSV) e um controller CRUD genérico. As exceções são tipadas e viram resposta padronizada e localizada, sempre com um trace id no log.

Esse contrato único é o que permite ao frontend compartilhado consumir qualquer API do ecossistema sem adaptação, e o que mantém a experiência homogênea entre sistemas diferentes.

Descoberta automática

Duas coisas se montam sozinhas no startup. Os serviços são registrados por convenção a partir do assembly (toda classe terminada em Service dentro de um namespace Services), sem fiação manual de DI. E o catálogo de acesso se alimenta sozinho: depois que a aplicação sobe, uma tarefa em segundo plano varre os endpoints, identifica os que têm RequireAccess, monta o catálogo de módulos e capacidades (com rótulos em português) a partir dos atributos [AccessModule] e [AccessCapability], e sincroniza tudo com o Identity Management, sem derrubar a API se o sync falhar.

É o que faz o catálogo de permissões do ecossistema refletir o código real, em vez de uma lista mantida à mão.

Qualidade e observabilidade

O framework carrega OpenTelemetry para traces e métricas, exportados via OTLP, e uma suíte de 131 casos de teste (NUnit e Moq) cobrindo pipeline HTTP, resolução de tenant, autorização, auditoria e sincronização de acesso. É essa base de testes que permite evoluir o framework com confiança de que uma mudança não quebra silenciosamente todos os sistemas que dependem dele.

Outros projetos

Ver todos
  1. Archon UI Framework frontend
  2. Identity Management Sistema de identidade
  3. Integration Platform Plataforma de integrações
  4. Mainstay Produto SaaS

Quer saber mais sobre como isso foi construído?

Posso detalhar decisões de arquitetura, trade-offs e o que faria diferente hoje. É só chamar.