14-16 Mar 2026

Arquitetura do Sistema

Pass 1–4

Mapeamento da arquitetura real: modulos, dependencias, fluxos criticos, riscos e divida tecnica.

Fluxo principal: Clientes Entrada/Edge Backend Dados Integracoes

Clientes

  • PWA Angular 17 + Ionic 8
  • App Android (Capacitor 7)
  • App iOS (Capacitor 7)

Entrada

  • Cloudflare (CDN/proxy)
  • Ingress Nginx (frontend)
  • Ingress Traefik (API/Auth)

Backend

  • cargo-fleet-api (HttpApi.Host)
  • cargo-fleet-auth (AuthServer)
  • Workers de processamento de ofertas

Dados

  • Redis no cluster (maxmemory 2MB em producao)
  • PostgreSQL fora do cluster

Integracoes

  • Galileu API
  • Meta Graph API (WhatsApp)
  • CargaAdicional API
  • ScraperOrchestrator
  • Firebase FCM

3. Visao Geral da Arquitetura

O sistema segue a estrutura de modulos do ABP Framework, com separacao em camadas bem definidas, parcialmente violada em alguns projetos (detalhado na secao 4).

Camadas principais (visao resumida)

  1. Clientes: PWA Angular/Ionic, app Android e app iOS.
  2. Edge: Cloudflare + ingress Nginx/Traefik.
  3. Backend: cargo-fleet-api e cargo-fleet-auth em AWS EKS.
  4. Dados: Redis (pod K8s, sem persistencia) + PostgreSQL externo.
  5. Integracoes: Galileu, Meta Graph API, CargaAdicional API, ScraperOrchestrator e Firebase FCM.

Pontos de atencao imediatos desta topologia

  • Replicas unicas em producao para API e AuthServer.
  • Redis com maxmemory 2MB e emptyDir, alto risco operacional.
  • Dependencias externas criticas concentradas em workers de processamento.

Background Workers ativos na API (HttpApi.Host):

WorkerTimer.PeriodEstrategia por tenant
ProcessarOfertasWorker1ms (continuo, throttle 5min por tenant)Paralelo (Task.WhenAll)
AtualizarOfertasWorker1ms (continuo, sem throttle documentado)Sequencial
VerificarRotasWorker1 diaSequencial
RetentionPolicyWorker1 diaCross-tenant, desabilita filtro IMultiTenant
RegistrarOfertasWorkerComentado (nao registrado)Legado

4. Mapa de Modulos e Dependencias

Grafo de dependencias entre projetos (.NET)

cargo_fleet.Domain.Shared
  (sem ProjectReference — correto)
  ^
  |
cargo_fleet.Domain
  -> Domain.Shared (correto)
  -> FirebaseAdmin 3.3.0 (VIOLACAO: infra no Domain)
  ^
  |
cargo_fleet.Application.Contracts
  -> Domain.Shared (correto)
  -> Domain (VIOLACAO: Contracts nao deveria referenciar Domain)
  ^
  |
cargo_fleet.Application
  -> Domain (correto)
  -> Application.Contracts (correto)
  -> Scryber.Core, MiniExcel, HttpClient, Polly (implementacoes no Application)
  ^
  |
cargo_fleet.EntityFrameworkCore
  -> Domain (correto)
  ^
  |
cargo_fleet.HttpApi
  -> Application.Contracts (correto)
  ^
  |
cargo_fleet.HttpApi.Host
  -> Application, EntityFrameworkCore, HttpApi
  -> Serilog, Redis, HealthChecks, FirebaseAdmin, Workers
  ^
cargo_fleet.AuthServer -> EntityFrameworkCore
cargo_fleet.DbMigrator -> EntityFrameworkCore + Application.Contracts
cargo_fleet.HttpApi.Client -> Application.Contracts

Modulos de dominio identificados

ModuloEntidade principalMultitenancyObservacoes
OfertasOferta (30+ props, FullAuditedAggregateRoot)SimValidacao ValidarOrigemDestino() comentada
RotasRota (FullAuditedAggregateRoot)SimValidacao inline
GalileuCredentialsGalileuCredentialSimArmazena login/senha criptografada
ApiStatusApiStatusSimStatus da API Galileu por tenant
OfertaRecordsOfertaRecordSimLog de mudancas de ofertas
NotificationsNotificationSimPush persistidas
DeviceTokensDeviceTokenSimTokens FCM
WhatsAppContactsWhatsAppContactSimAte 5 numeros por tenant
CidadesCidadeNaoReferencia global
SeedExecutionSeedExecution-Controle de idempotencia

Violacoes de camada confirmadas

  1. FirebaseAdmin no Domain (cargo_fleet.Domain.csproj linha 16): infraestrutura de push notification referenciada diretamente na camada de dominio.
  2. Application.Contracts referencia Domain (cargo_fleet.Application.Contracts.csproj linha 13): contratos de aplicacao acoplados a entidades de dominio, violando o principio de que contratos devem depender apenas de Domain.Shared.
  3. ServiceConfigurationContext injetado como dependencia runtime (CargaAdicionalService.cs linha 27): objeto de configuracao de modulo ABP injetado em servico de aplicacao, uso incorreto do ciclo de vida.

5. Fluxos Criticos do Sistema

5.1 Autenticacao e Login

[Angular] -> POST /connect/token (OpenIddict/AuthServer)
          <- access_token + refresh_token
[Angular] -> GET /api/abp/application-configuration (ABP)
          <- permissoes, configuracoes de tenant

O fluxo de autenticacao usa OAuth2 Authorization Code com PKCE no frontend (oAuthConfig em environment.prod.ts). O AuthServer (OpenIddict Pro) esta em replica unica com estrategia Recreate, sem rollback gracioso.

5.2 Ciclo de processamento de ofertas (Background)

ProcessarOfertasWorker (a cada ~5min por tenant, paralelo)
  -> GalileuApiService.AuthenticateAsync(login, senha_descriptografada)
  -> CriarOfertasJob: busca ofertas na API Galileu -> salva no PostgreSQL
  -> AceitarOfertasJob: avalia regras de aceite automatico
       -> OfertasAceitePreparationService: adquire lock Redis por oferta
       -> OfertasAceiteExecucaoAppService: confirma aceite na API Galileu
       -> NotificationDispatcher: envia FCM via Firebase

AtualizarOfertasWorker (continuo, sequencial por tenant)
  -> Busca ofertas em lotes de 200 na API Galileu
  -> Atualiza status no PostgreSQL
  -> WhatsAppMessageService: dispara mensagem na Graph API Meta

5.3 Cadastro e gestao de credenciais Galileu

[Frontend] -> POST /api/app/galileu-credentials
           -> GalileuCredentialsAppService.CreateAsync()
              -> GalileuApiService.AuthenticateAsync() (valida credenciais)
              -> PasswordEncryptionService.Encrypt(senha) (passphrase fraca)
              -> Salva GalileuCredential no banco
              -> Publica GalileuCredentialCreatedEto com login + senha em texto plano
                 -> CargaAdicionalScraperService.ConfigurarTenantAsync(login, senha_plana)

5.4 Registro de device token (push notifications)

[App Mobile] -> POST /api/app/device-token (SEM autenticacao)
             <- DeviceToken inserido diretamente via IRepository<DeviceToken>
[Backend]    -> FirebaseAdmin.SendMulticastAsync(tokens, payload)

5.5 Consulta de ofertas pelo usuario

[Frontend] -> GET /api/app/ofertas (com JWT Bearer)
           -> EfCoreOfertaRepository.GetListAsync()
              -> Subquery correlacionada O(n²) para deduplicacao por CodigoB100
           <- Lista paginada de ofertas

5.6 Exportacao PDF

[Frontend] -> GET /api/app/printing/... (endpoint com [AllowAnonymous])
           -> PrintingAppService.PrintAsyncApiStatus()
              -> Busca dados no banco (sem cancelation token)
              -> Scryber.Core: gera PDF sincrono em arquivo temporario
              -> File.ReadAllBytesAsync(tmpFile)
              <- byte[] PDF (arquivo temporario NAO e apagado)

5.7 Verificacao de cargas adicionais

ProcessarOfertasWorker -> CargaAdicionalService.VerificarCargasAdicionaisEmLoteAsync()
  -> GetAccessTokenAsync(): solicita token OAuth2 (password grant, sem cache)
  -> POST cargaadicional-api-prd/api/app/oferta-scraps/custom-batch
  <- Lista de OfertaScrapBatchResult

7. Riscos de Seguranca

Resumo consolidado por categoria

Exposicao de segredos no repositorio

Cinco classes distintas de segredos foram confirmadas no repositorio Git:

SegredoArquivoSeveridade
Token Meta/WhatsApp APIappsettings.json linha 25Critical
Senha PostgreSQL (superusuario)appsettings.json linha 10Critical
ClientSecret + Password CargaAdicionalappsettings.json linhas 46,49Critical
Firebase Web API Keyenvironment.prod.ts linha 34Critical
Firebase Android API Keygoogle-services.json linha 18Critical
Firebase iOS API KeyGoogleService-Info.plistCritical
Passphrase criptografia Galileuappsettings.json linhas 30-31High
ABP License Codeappsettings.secrets.jsonMedium

O .gitignore raiz nao inclui appsettings.json, environment.prod.ts, google-services.json, ou GoogleService-Info.plist. Somente firebase-credentials.json (service account) e os arquivos env.yml dos ambientes estao no .gitignore.

Controle de acesso

ProblemaTipoSeveridade
CORS AllowAnyOrigin()achado confirmadoCritical
DeviceTokenController sem [Authorize]achado confirmadoHigh
Swagger UI publicoachado confirmadoMedium
Health UI sem autenticacaorisco provavelMedium
Endpoints de exportacao com [AllowAnonymous]achado confirmadoMedium

LGPD e privacidade

ProblemaEvidenciaRisco
ShowPII = true em producaoconfigmap.yaml nao define App__DisablePIIPII em logs
Senha Galileu em ETO publicadoGalileuCredentialsAppService.cs linhas 155, 188Credenciais em eventos
Dados de ofertas em localStoragecotar.component.ts linhas 83-85Dados de negocio sem protecao
AbpGdprModule nao ativoNao encontrado em HttpApi.HostSem mecanismo de exclusao de dados
OfertaRecords sem politica de retencao visivelRetentionPolicyWorker genericoRetencao indefinida de dados pessoais
Email admin@cargofleet.log.br hardcodedIdentificado pelos revisoresDado pessoal hardcoded

7.2 Achados de Seguranca — Segunda Passagem

Consulte a secao 6.2 para o detalhamento completo de cada achado SEC2-01 a SEC2-17. Tabela resumo:

IDSeveridadeTituloTipoArquivo principal
SEC2-01CriticalSenha Galileu em texto plano nos ETOsachado confirmadoGalileuCredentialsAppService.cs:145,155
SEC2-02CriticalDeviceTokenController sem [Authorize] + mass assignmentachado confirmadoDeviceTokenController.cs:27-35
SEC2-03HighDynamic LINQ sorting sem whitelist (injection)achado confirmadoEfCoreOfertaRepository.cs:181 (e 5 outros)
SEC2-04HighCampo Senha exposto no GalileuCredentialDtoachado confirmadoGalileuCredentialDto.cs:11-12
SEC2-05HighAutenticacao Galileu usa MD5achado confirmadoGalileuApiService.cs:80-82
SEC2-06HighPlaceholder nao substituido em OfertaRecordController + ausencia [Authorize]achado confirmadoOfertaRecordController.cs:18
SEC2-07HighCotarOfertaAsync com permissao de leitura e sem validacao de rangeachado confirmadoOfertasAppService.Extended.cs:31-97
SEC2-08MediumNotificationAppService sem [Authorize] e filtro manual de TenantIdachado confirmadoNotificationAppService.cs:15-16
SEC2-09MediumLog de debug com objeto CurrentTenant completoachado confirmadoGalileuCredentialsAppService.cs:147
SEC2-10MediumToken de download sem binding ao usuario solicitanteachado confirmadoRotasAppService.cs:225-269
SEC2-11MediumOfertaAtualizacaoPrecoAppService sem [Authorize]achado confirmadoOfertaAtualizacaoPrecoAppService.cs:13
SEC2-12MediumCORS AllowAnyOrigin hardcoded no AuthServerachado confirmadocargo_fleetAuthServerModule.cs:204-215
SEC2-13MediumRequireHttpsMetadata hardcoded como false nos deployments YAMLachado confirmado.github/kubernetes/production/api.yaml:70-71
SEC2-14MediumRefresh token com vida util de 180 dias para mobileachado confirmadocargo_fleetAuthServerModule.cs:104
SEC2-15Low.gitignore incompleto para arquivos sensiveis de backendachado confirmado.gitignore
SEC2-16LowDockerfile instala Node.js sem versao fixadaachado confirmadoDockerfile:4-6
SEC2-17LowTenantUserPasswordPolicyHandler com comparacao case-sensitiverisco provavelTenantUserPasswordPolicyHandler.cs:40

Observacoes LGPD — Segunda Passagem:

  • GalileuApiService.cs linha 136 loga o payload completo da resposta da API Galileu com LogDebug, que pode conter dados pessoais de motoristas e enderecos. Se o nivel de log em producao incluir Debug, ha violacao LGPD.
  • OfertaCreateDto e OfertaDto incluem campos como Embarcador, CEP e enderecos que podem ser dados pessoais (nome da empresa ou pessoa fisica embarcadora).
  • MetricasAppService usa [DisableAuditing] em operacao que acessa dados de todos os tenants — ausencia de trilha de auditoria para acesso cross-tenant.

Seguranca mobile — Segunda Passagem:

  • Uso correto de @aparajita/capacitor-secure-storage para armazenar refresh token (iOS Keychain, Android EncryptedSharedPreferences) — ponto positivo confirmado.
  • allowDeviceCredential: true em BiometricLoginService permite fallback para PIN/padrao do dispositivo, reduzindo garantia de "usuario presente" para "dispositivo desbloqueado".

8. Gargalos de Escalabilidade e Performance

8.1 Criticos (risco de indisponibilidade)


SCALE-C1 — Redis maxmemory 2MB em producao

  • Tipo: achado confirmado
  • Evidencia: .github/kubernetes/production/redis.yml linha 7 — maxmemory: 2mb
  • Impacto tecnico: Com apenas 2 MB, o Redis sera incapaz de armazenar DataProtection keys, distributed locks e cache de sessao simultaneamente para multiplos tenants. Quando o limite e atingido, o Redis inicia eviction de chaves (politica padrao noeviction retorna erro, ou allkeys-lru remove chaves ativas). Perda de DataProtection keys causa logout em massa. Perda de locks causa condicoes de corrida.
  • Recomendacao: Aumentar para pelo menos 256 MB como medida imediata. Migrar para AWS ElastiCache com replicacao como solucao definitiva.

SCALE-C2 — ProcessarOfertasWorker sem limite de concorrencia entre tenants

  • Tipo: achado confirmado
  • Evidencia: ProcessarOfertasWorker.cs linhas 57-71 — Task.WhenAll(tasks) onde tasks cresce linearmente com o numero de tenants. Timer.Period = 1 (milissegundo).
  • Impacto tecnico: Com N tenants ativos, N threads simultaneas fazem requests para a API Galileu, abrem conexoes com o PostgreSQL e competem por locks Redis. Sem pool de threads limitado, o crescimento linear de tenants degrada exponencialmente a performance.
  • Recomendacao: Implementar SemaphoreSlim com limite configuravel (ex: max 5 tenants simultaneos). Considerar usar Hangfire ou ABP Background Jobs com fila para controle de throughput.

8.2 Altos (degradacao progressiva)


SCALE-H1 — Subquery correlacionada O(n²) na deduplicacao de ofertas

  • Tipo: achado confirmado
  • Evidencia: aspnet-core/src/cargo_fleet.EntityFrameworkCore/Ofertas/EfCoreOfertaRepository.cs linhas 263-273
    csharp
    var deduplicatedQuery = baseQuery.Select(o => new {
        Oferta = o,
        MaxData = baseQuery.Where(x => x.CodigoB100 == o.CodigoB100).Max(x => x.Data)
    }).Where(x => x.Oferta.Data == x.MaxData).Select(x => x.Oferta);
    Esta e uma subquery correlacionada: para cada linha de baseQuery, executa uma segunda query com WHERE CodigoB100 = ?. Com EF Core, isso pode ou nao ser traduzido para uma subquery SQL eficiente dependendo da versao e do provider.
  • Impacto tecnico: Se traduzido como N+1 no PostgreSQL, uma listagem de 1.000 ofertas gera 1.001 queries. Mesmo com traducao correta para LATERAL JOIN, a ausencia de indice em CodigoB100 torna a operacao custosa.
  • Recomendacao: Verificar o SQL gerado via context.Database.Log ou pgaudit. Substituir pela clausula DISTINCT ON (CodigoB100) ORDER BY CodigoB100, Data DESC nativa do PostgreSQL, que e O(n log n) com indice.

SCALE-H2 — Tabela AppOfertas sem indices criticos

  • Tipo: achado confirmado
  • Evidencia: Migration 20250827133229_CargaAdicionalAndIndexes.cs — indices presentes: IsCargaAdicional, OrigemCidadeId, DestinoCidadeId, PontoTnfCidadeId. Indices ausentes: CogProgColeta, TryAceite, Status, CodigoB100, Data.
  • Impacto tecnico: Queries de filtragem por status (WHERE Status = 'ACEITA'), por data de processamento, e por codigo de oferta fazem full table scan. Com volume crescente de ofertas por tenant, o tempo de resposta degrada linearmente.
  • Recomendacao: Criar indices compostos baseados nos padroes de query mais frequentes: (TenantId, Status), (TenantId, CogProgColeta), (TenantId, CodigoB100, Data DESC), (TenantId, TryAceite, Status).

SCALE-H3 — Geracao de PDF sincrona com arquivo temporario nao limpo

  • Tipo: achado confirmado
  • Evidencia: aspnet-core/src/cargo_fleet.Application/Scryber/PrintingAppService.cs linhas 62-68
    csharp
    var targetFilename = System.IO.Path.Combine(tmpDir, name);
    doc.SaveAsPDF(targetFilename, FileMode.Create);
    return await File.ReadAllBytesAsync(targetFilename);
    // arquivo nunca e deletado
  • Impacto tecnico: Cada geracao de PDF cria um arquivo temporario que nunca e removido. Em ambiente containerizado, o filesystem do container se enche progressivamente. A geracao e sincrona, bloqueando a thread da requisicao durante toda a renderizacao (CPU intensivo).
  • Recomendacao: Usar File.Delete(targetFilename) em bloco finally. Preferir MemoryStream para evitar I/O em disco. Adicionar CancellationToken para respeitar timeout de requisicao.

SCALE-H4 — GetAccessTokenAsync sem cache (OAuth2 token por requisicao)

  • Tipo: achado confirmado
  • Evidencia: aspnet-core/src/cargo_fleet.Application/CargasAdicionais/CargaAdicionalService.cs linhas 255-286 — GetAccessTokenAsync solicita novo token a cada chamada sem nenhum mecanismo de cache.
  • Impacto tecnico: Cada verificacao de carga adicional gera uma requisicao extra de autenticacao. Em lotes com centenas de ofertas, isso multiplica o numero de chamadas HTTP desnecessarias.
  • Recomendacao: Implementar cache do access token com expiracao baseada no campo expires_in da resposta, usando IDistributedCache (Redis).

SCALE-H5 — Distributed lock adquirido por oferta dentro de Unit of Work

  • Tipo: achado confirmado
  • Evidencia: aspnet-core/src/cargo_fleet.Application/Ofertas/Aceite/OfertasAceitePreparationService.cs linhas 66-102 — lock Redis adquirido e liberado (await using var lockHandle) dentro do loop, antes do uow.CompleteAsync(). O lock e liberado antes do commit da transacao.
  • Impacto tecnico: A janela de tempo entre a liberacao do lock e o commit do uow permite que outro worker adquira o lock e leia a oferta com o estado ainda nao comitado. Condicao de corrida potencial em deployments com multiplos replicas (atualmente 1 replica, mas e um risco latente).
  • Recomendacao: Manter o lock ate apos o uow.CompleteAsync(). Avaliar se o lock por oferta e realmente necessario dado que o acesso e serializado por batchSize = 20.

SCALE-H6 — N+1 em MetricasAppService

  • Tipo: achado confirmado
  • Evidencia: aspnet-core/src/cargo_fleet.Application/Metricas/MetricasAppService.cs linhas 119-134 — iteracao sobre tenantList com await _tenantManager.IsActiveAsync(tenant) por tenant, gerando uma query de banco por tenant por chamada.
  • Impacto tecnico: O dashboard de metricas gera N queries para N tenants ativos a cada carregamento.
  • Recomendacao: Cachear o resultado de GetListAsync + IsActiveAsync por um intervalo curto (ex: 5 minutos) usando IDistributedCache.

8.3 Medios

IDProblemaEvidencia resumida
SCALE-M1Firebase DispatchManyAsync sequencialMultiplas chamadas FCM sem batching eficiente
SCALE-M2WhatsApp calls sem backoff exponencialWhatsAppMessageService sem retry com jitter
SCALE-M3Dashboard sem cacheMetricas recalculadas a cada request
SCALE-M4AppOfertaRecord sem indice em OfertaIdBusca de historico de oferta faz full scan
SCALE-M5AppApiStatus sem indice em Data e TenantIdQueries de status de API degradam com volume
SCALE-M6GalileuApiService sem cache de tokenRe-autenticacao por request na API Galileu
SCALE-M7WhatsAppMessageService com HttpClient injetado diretamenteRisco de socket exhaustion sem IHttpClientFactory

8.4 Infraestrutura de producao

ComponenteReplicasEstrategiaProblema
cargo-fleet-api1RecreateZero downtime impossivel; sem HPA
cargo-fleet-authserver1RecreateSPOF de autenticacao
cargo-fleet-angular1RecreateSem HPA
Redis1RollingUpdateSem persistencia; maxmemory 2MB
PostgreSQLDesconhecido-Nao encontrado nos manifests auditados

A estrategia Recreate garante downtime a cada deploy. O pipeline atual (kubectl rollout restart) nao implementa blue/green, canary, ou qualquer mecanismo de rollback automatico.


8.2 Gargalos — Segunda Passagem

Esta secao consolida os achados de performance e escalabilidade identificados na segunda passagem. Os achados com classificacao "Gargalo atual confirmado" ja impactam o sistema com 22 tenants. Os classificados como "Risco de crescimento" tornam-se criticos com 150 tenants.

N+1 e queries ineficientes

AchadoClassificacaoEvidenciaImpacto estimado
N+1 no export Excel: 1 query por oferta aceita para buscar OfertaRecordsGargalo atual confirmadoOfertasAppService.cs:429-495500 queries sequenciais para 500 ofertas aceitas
Query de rota dentro de loop por oferta no RegistrarOfertaServiceGargalo atual confirmadoRegistrarOfertaService.cs:1996 x 100 x 22 tenants = 13.200 queries de rota/ciclo
.ToHashSet() sincrono em OfertasAtualizacaoProcessor (bloqueia thread)Gargalo atual confirmadoOfertasAtualizacaoProcessor.cs:101-106Bloqueio de thread do pool em cada batch de 200
Count() sincrono no OfertasDashboardQueryServiceGargalo atual confirmadoOfertasDashboardQueryService.cs:50-52Bloqueio de thread em cada abertura de dashboard
GetListAsExcelFileAsync carrega todas as ofertas sem limite (int.MaxValue)Gargalo atual confirmadoOfertasAppService.cs:416Dezenas de milhares de registros em memoria
RegistrarOfertaService: dupla persistencia por oferta (update apos uow.CompleteAsync)Gargalo atual confirmadoRegistrarOfertaService.cs:670-7052x writes em AppOfertas por oferta processada
FilterText usa LIKE '%texto%' sem indice GIN/trgm em colunas textoRisco de crescimentoEfCoreOfertaRepository.cs:594Seq scan em 50.000+ registros por busca livre

Concorrencia e locks

AchadoClassificacaoEvidencia
QuantidadeCarros-- em memoria sem UPDATE atomico (race condition)Gargalo atual confirmado — impacto de negocioRegistrarOfertaService.cs:528-529
Lock global por tenant no RegistrarOfertaService serializa TODO o processamento do tenantRisco de crescimentoRegistrarOfertaService.cs:93
Lock Redis dentro de UoW aberta em OfertasAceitePreparationService: ate 600s de transacao abertaRisco de crescimentoOfertasAceitePreparationService.cs:60-102
Task.WhenAll sem throttling em ProcessarOfertasWorker: 150 tasks paralelas esgotam connection poolRisco de crescimento (critico com 150 tenants)ProcessarOfertasWorker.cs:57-73

Background workers

AchadoClassificacaoEvidencia
RegistrarOfertasWorker reprocessa 7 dias x 6 intervalos = 924 requisicoes HTTP/ciclo com 22 tenantsGargalo atual confirmadoRegistrarOfertasWorker.cs:43,137-161
AtualizarOfertasWorker processa tenants sequencialmente: ciclo pode levar horas com 150 tenantsRisco de crescimentoAtualizarOfertasWorker.cs:50-68
VerificarRotasWorker: N queries Firebase por rota expirada sem rate limitingRisco de crescimentoVerificarRotasWorker.cs:54-69
Duplicacao de logica de limpeza de ApiStatus entre RegistrarOfertasWorker e RetentionPolicyWorkerHipotese (dependente de timing)RegistrarOfertasWorker.cs:99 e ApiStatusRetentionTask.cs:25
ApiStatusRetentionTask usa HardDeleteAsync sem paginacao — pode deletar centenas de milhares em 1 transacaoRisco de crescimentoApiStatusRetentionTask.cs:25

Indices ausentes no banco

TabelaColuna(s) ausentesImpacto
AppRotasIndice composto (Origem, Destino, Carga, Status) — filtro mais frequente do sistema13.200 full scans/ciclo no RegistrarOfertaService
AppOfertasFilterText em colunas texto sem pg_trgmSeq scan em buscas livres
AppApiStatusesTenantId, DataDegradacao progressiva com ~288 registros/dia/tenant

Frontend e Docker

AchadoClassificacaoEvidencia
Budget Angular de 2.5MB muito permissivo para PWA mobile; 15+ CSS bundles carregadosRisco de crescimentoangular/angular.json:234-241
ngsw-config.json sem dataGroups para cache de respostas de APIRisco de crescimentoangular/ngsw-config.json
npm install sem --ci no Dockerfile frontend — builds nao deterministicosRisco de crescimentoangular/Dockerfile:6
Backend Dockerfile invalida layer cache do apt-get a cada dist-upgradeRisco de crescimentoaspnet-core/src/cargo_fleet.HttpApi.Host/Dockerfile:4-6

9. Divida Tecnica

9.1 Violacoes de arquitetura

ViolacaoEvidenciaImpacto
FirebaseAdmin no Domaincargo_fleet.Domain.csproj linha 16Domain layer depende de infraestrutura de push
Application.Contracts referencia Domaincargo_fleet.Application.Contracts.csproj linha 13Contratos acoplados a entidades de dominio
ServiceConfigurationContext injetado em runtimeCargaAdicionalService.cs linha 27Objeto de bootstrap usado como servico de runtime
Version mismatch ABP 9.0.0 vs 8.3.3package.json vs .csprojIncompatibilidades de contrato de API entre frontend e backend

9.2 Codigo comentado e legado

ItemLocalizacaoRisco
ValidarOrigemDestino() comentadaRota.csValidacao de regra de negocio desativada silenciosamente
RegistrarOfertasWorker — corpo comentado, classe existenteRegistrarOfertasWorker.csCodigo legado de 740 linhas (com RegistrarOfertaService) presente mas inativo; confusao de manutencao
Event handlers do ScraperOrchestrator comentadosIdentificado nos revisoresServicos registrados mas handlers desativados — comportamento indefinido

9.3 Qualidade de engenharia

ItemObservacao
Testes nao executados no CI/CD4 projetos de teste existem mas nenhum e executado nos pipelines
OnModelCreating com entidades duplicadascargo_fleetDbContext.cs — configuracoes de entidade possivelmente duplicadas
Timer.Period = 1ms sem documentacaoWorkers ProcessarOfertasWorker e AtualizarOfertasWorker — throttle implementado manualmente, nao pelo framework
Scryber.Core em versao beta8.0.0.1-beta usada em producao para geracao de PDF
Uso de DateTime.Now em vez de DateTime.UtcNowProcessarOfertasWorker.cs linha 96 — risco em ambiente com timezone configurado incorretamente

10. Riscos Operacionais e de Producao

10.1 Deploy e rollback

  • Sem approval gate: O pipeline Production.yaml e acionado automaticamente por push em master. Um commit acidental em master dispara deploy de producao imediato.
  • Estrategia Recreate: Todos os componentes usam type: Recreate, garantindo downtime a cada deploy. Para o AuthServer, isso significa logout de todos os usuarios ativos.
  • Sem rollback automatico: O pipeline nao implementa health check pos-deploy nem rollback automatico em caso de falha. O rollback manual requer acesso ao cluster e conhecimento do SHA anterior.
  • DbMigrator como Job Kubernetes: O migrador de banco roda como Job Kubernetes a cada deploy (dbmigrator.yml). Migrations mal testadas sao aplicadas diretamente em producao.
  • Pipeline sem separacao de responsabilidades: Build, push, e deploy sao etapas do mesmo job, rodando no runner gemini (provavelmente o mesmo k3s de desenvolvimento — hipotese, nao confirmado).

10.2 Observabilidade

  • Logging: Serilog configurado, mas sem confirmacao de destino centralizado (CloudWatch, Elasticsearch, Datadog). Nao encontrado Serilog.Sinks.* alem do AspNetCore sink nos manifests.
  • Metricas: Sem evidencia de integracao com Prometheus/Grafana ou CloudWatch Metrics.
  • Tracing distribuido: Sem evidencia de OpenTelemetry ou AWS X-Ray.
  • Alertas: Sem evidencia de alertas configurados para falha de workers, erros de autenticacao Galileu, ou uso de Redis.
  • Health checks: Implementados (CargaAdicionalApiHealthCheck, cargo_fleetDatabaseCheck), mas o endpoint /health-ui esta potencialmente exposto sem autenticacao.

10.3 Incident response

  • Workers silenciosos: ProcessarOfertasWorker e AtualizarOfertasWorker capturam todas as excecoes com try/catch generico e apenas logam. Uma falha sistemica (ex: Redis indisponivel) resulta em loop silencioso de erros sem alarme.
  • ApiStatus como proxy de saude: O sistema usa ApiStatusManager.CreateAsync para registrar status Online/Offline da API Galileu, o que e uma forma manual de health check que nao se integra com alertas Kubernetes.
  • Nao ha runbook identificado: Nenhum arquivo de procedimentos operacionais encontrado no repositorio.

10.4 Seguranca operacional

  • Secrets no ConfigMap: O configmap.yaml de producao nao contem secrets diretamente, mas o mecanismo de injecao de secrets (secretRef: cargofleet-secrets) depende de Kubernetes Secrets configurados manualmente fora do repositorio. Nao ha evidencia de rotacao automatica.
  • ECR com imagens latest: Imagens tagueadas com latest permitem que kubectl rollout restart puxe uma versao diferente da que foi validada, se uma nova imagem latest foi pushada entre o build e o restart.
  • Runner gemini: O pipeline de producao usa runs-on: gemini (runner auto-hospedado). Se este runner e o mesmo k3s de desenvolvimento, ha risco de contaminacao entre ambientes.