Arquitetura do Sistema
Mapeamento da arquitetura real: modulos, dependencias, fluxos criticos, riscos e divida tecnica.
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
| Modulo | Entidade principal | Multitenancy | Observacoes |
|---|---|---|---|
| Ofertas | Oferta (30+ props, FullAuditedAggregateRoot) | Sim | Validacao ValidarOrigemDestino() comentada |
| Rotas | Rota (FullAuditedAggregateRoot) | Sim | Validacao inline |
| GalileuCredentials | GalileuCredential | Sim | Armazena login/senha criptografada |
| ApiStatus | ApiStatus | Sim | Status da API Galileu por tenant |
| OfertaRecords | OfertaRecord | Sim | Log de mudancas de ofertas |
| Notifications | Notification | Sim | Push persistidas |
| DeviceTokens | DeviceToken | Sim | Tokens FCM |
| WhatsAppContacts | WhatsAppContact | Sim | Ate 5 numeros por tenant |
| Cidades | Cidade | Nao | Referencia global |
| SeedExecution | SeedExecution | - | Controle de idempotencia |
Violacoes de camada confirmadas
FirebaseAdminnoDomain(cargo_fleet.Domain.csprojlinha 16): infraestrutura de push notification referenciada diretamente na camada de dominio.Application.ContractsreferenciaDomain(cargo_fleet.Application.Contracts.csprojlinha 13): contratos de aplicacao acoplados a entidades de dominio, violando o principio de que contratos devem depender apenas deDomain.Shared.ServiceConfigurationContextinjetado como dependencia runtime (CargaAdicionalService.cslinha 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 OfertaScrapBatchResult7. Riscos de Seguranca
Resumo consolidado por categoria
Exposicao de segredos no repositorio
Cinco classes distintas de segredos foram confirmadas no repositorio Git:
| Segredo | Arquivo | Severidade |
|---|---|---|
| Token Meta/WhatsApp API | appsettings.json linha 25 | Critical |
| Senha PostgreSQL (superusuario) | appsettings.json linha 10 | Critical |
| ClientSecret + Password CargaAdicional | appsettings.json linhas 46,49 | Critical |
| Firebase Web API Key | environment.prod.ts linha 34 | Critical |
| Firebase Android API Key | google-services.json linha 18 | Critical |
| Firebase iOS API Key | GoogleService-Info.plist | Critical |
| Passphrase criptografia Galileu | appsettings.json linhas 30-31 | High |
| ABP License Code | appsettings.secrets.json | Medium |
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
| Problema | Tipo | Severidade |
|---|---|---|
| CORS AllowAnyOrigin() | achado confirmado | Critical |
| DeviceTokenController sem [Authorize] | achado confirmado | High |
| Swagger UI publico | achado confirmado | Medium |
| Health UI sem autenticacao | risco provavel | Medium |
| Endpoints de exportacao com [AllowAnonymous] | achado confirmado | Medium |
LGPD e privacidade
| Problema | Evidencia | Risco |
|---|---|---|
| ShowPII = true em producao | configmap.yaml nao define App__DisablePII | PII em logs |
| Senha Galileu em ETO publicado | GalileuCredentialsAppService.cs linhas 155, 188 | Credenciais em eventos |
| Dados de ofertas em localStorage | cotar.component.ts linhas 83-85 | Dados de negocio sem protecao |
AbpGdprModule nao ativo | Nao encontrado em HttpApi.Host | Sem mecanismo de exclusao de dados |
| OfertaRecords sem politica de retencao visivel | RetentionPolicyWorker generico | Retencao indefinida de dados pessoais |
Email admin@cargofleet.log.br hardcoded | Identificado pelos revisores | Dado 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:
| ID | Severidade | Titulo | Tipo | Arquivo principal |
|---|---|---|---|---|
| SEC2-01 | Critical | Senha Galileu em texto plano nos ETOs | achado confirmado | GalileuCredentialsAppService.cs:145,155 |
| SEC2-02 | Critical | DeviceTokenController sem [Authorize] + mass assignment | achado confirmado | DeviceTokenController.cs:27-35 |
| SEC2-03 | High | Dynamic LINQ sorting sem whitelist (injection) | achado confirmado | EfCoreOfertaRepository.cs:181 (e 5 outros) |
| SEC2-04 | High | Campo Senha exposto no GalileuCredentialDto | achado confirmado | GalileuCredentialDto.cs:11-12 |
| SEC2-05 | High | Autenticacao Galileu usa MD5 | achado confirmado | GalileuApiService.cs:80-82 |
| SEC2-06 | High | Placeholder nao substituido em OfertaRecordController + ausencia [Authorize] | achado confirmado | OfertaRecordController.cs:18 |
| SEC2-07 | High | CotarOfertaAsync com permissao de leitura e sem validacao de range | achado confirmado | OfertasAppService.Extended.cs:31-97 |
| SEC2-08 | Medium | NotificationAppService sem [Authorize] e filtro manual de TenantId | achado confirmado | NotificationAppService.cs:15-16 |
| SEC2-09 | Medium | Log de debug com objeto CurrentTenant completo | achado confirmado | GalileuCredentialsAppService.cs:147 |
| SEC2-10 | Medium | Token de download sem binding ao usuario solicitante | achado confirmado | RotasAppService.cs:225-269 |
| SEC2-11 | Medium | OfertaAtualizacaoPrecoAppService sem [Authorize] | achado confirmado | OfertaAtualizacaoPrecoAppService.cs:13 |
| SEC2-12 | Medium | CORS AllowAnyOrigin hardcoded no AuthServer | achado confirmado | cargo_fleetAuthServerModule.cs:204-215 |
| SEC2-13 | Medium | RequireHttpsMetadata hardcoded como false nos deployments YAML | achado confirmado | .github/kubernetes/production/api.yaml:70-71 |
| SEC2-14 | Medium | Refresh token com vida util de 180 dias para mobile | achado confirmado | cargo_fleetAuthServerModule.cs:104 |
| SEC2-15 | Low | .gitignore incompleto para arquivos sensiveis de backend | achado confirmado | .gitignore |
| SEC2-16 | Low | Dockerfile instala Node.js sem versao fixada | achado confirmado | Dockerfile:4-6 |
| SEC2-17 | Low | TenantUserPasswordPolicyHandler com comparacao case-sensitive | risco provavel | TenantUserPasswordPolicyHandler.cs:40 |
Observacoes LGPD — Segunda Passagem:
GalileuApiService.cslinha 136 loga o payload completo da resposta da API Galileu comLogDebug, que pode conter dados pessoais de motoristas e enderecos. Se o nivel de log em producao incluir Debug, ha violacao LGPD.OfertaCreateDtoeOfertaDtoincluem campos comoEmbarcador, CEP e enderecos que podem ser dados pessoais (nome da empresa ou pessoa fisica embarcadora).MetricasAppServiceusa[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-storagepara armazenar refresh token (iOS Keychain, Android EncryptedSharedPreferences) — ponto positivo confirmado. allowDeviceCredential: trueemBiometricLoginServicepermite 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.ymllinha 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
noevictionretorna erro, ouallkeys-lruremove 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.cslinhas 57-71 —Task.WhenAll(tasks)ondetaskscresce 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
SemaphoreSlimcom 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.cslinhas 263-273Esta e uma subquery correlacionada: para cada linha decsharpvar 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);baseQuery, executa uma segunda query comWHERE 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 emCodigoB100torna a operacao custosa. - Recomendacao: Verificar o SQL gerado via
context.Database.Logou pgaudit. Substituir pela clausulaDISTINCT ON (CodigoB100) ORDER BY CodigoB100, Data DESCnativa 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.cslinhas 62-68csharpvar 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 blocofinally. PreferirMemoryStreampara evitar I/O em disco. AdicionarCancellationTokenpara 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.cslinhas 255-286 —GetAccessTokenAsyncsolicita 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_inda resposta, usandoIDistributedCache(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.cslinhas 66-102 — lock Redis adquirido e liberado (await using var lockHandle) dentro do loop, antes douow.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
uowpermite 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 porbatchSize = 20.
SCALE-H6 — N+1 em MetricasAppService
- Tipo: achado confirmado
- Evidencia:
aspnet-core/src/cargo_fleet.Application/Metricas/MetricasAppService.cslinhas 119-134 — iteracao sobretenantListcomawait _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+IsActiveAsyncpor um intervalo curto (ex: 5 minutos) usandoIDistributedCache.
8.3 Medios
| ID | Problema | Evidencia resumida |
|---|---|---|
| SCALE-M1 | Firebase DispatchManyAsync sequencial | Multiplas chamadas FCM sem batching eficiente |
| SCALE-M2 | WhatsApp calls sem backoff exponencial | WhatsAppMessageService sem retry com jitter |
| SCALE-M3 | Dashboard sem cache | Metricas recalculadas a cada request |
| SCALE-M4 | AppOfertaRecord sem indice em OfertaId | Busca de historico de oferta faz full scan |
| SCALE-M5 | AppApiStatus sem indice em Data e TenantId | Queries de status de API degradam com volume |
| SCALE-M6 | GalileuApiService sem cache de token | Re-autenticacao por request na API Galileu |
| SCALE-M7 | WhatsAppMessageService com HttpClient injetado diretamente | Risco de socket exhaustion sem IHttpClientFactory |
8.4 Infraestrutura de producao
| Componente | Replicas | Estrategia | Problema |
|---|---|---|---|
| cargo-fleet-api | 1 | Recreate | Zero downtime impossivel; sem HPA |
| cargo-fleet-authserver | 1 | Recreate | SPOF de autenticacao |
| cargo-fleet-angular | 1 | Recreate | Sem HPA |
| Redis | 1 | RollingUpdate | Sem persistencia; maxmemory 2MB |
| PostgreSQL | Desconhecido | - | 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
| Achado | Classificacao | Evidencia | Impacto estimado |
|---|---|---|---|
N+1 no export Excel: 1 query por oferta aceita para buscar OfertaRecords | Gargalo atual confirmado | OfertasAppService.cs:429-495 | 500 queries sequenciais para 500 ofertas aceitas |
Query de rota dentro de loop por oferta no RegistrarOfertaService | Gargalo atual confirmado | RegistrarOfertaService.cs:199 | 6 x 100 x 22 tenants = 13.200 queries de rota/ciclo |
.ToHashSet() sincrono em OfertasAtualizacaoProcessor (bloqueia thread) | Gargalo atual confirmado | OfertasAtualizacaoProcessor.cs:101-106 | Bloqueio de thread do pool em cada batch de 200 |
Count() sincrono no OfertasDashboardQueryService | Gargalo atual confirmado | OfertasDashboardQueryService.cs:50-52 | Bloqueio de thread em cada abertura de dashboard |
GetListAsExcelFileAsync carrega todas as ofertas sem limite (int.MaxValue) | Gargalo atual confirmado | OfertasAppService.cs:416 | Dezenas de milhares de registros em memoria |
RegistrarOfertaService: dupla persistencia por oferta (update apos uow.CompleteAsync) | Gargalo atual confirmado | RegistrarOfertaService.cs:670-705 | 2x writes em AppOfertas por oferta processada |
FilterText usa LIKE '%texto%' sem indice GIN/trgm em colunas texto | Risco de crescimento | EfCoreOfertaRepository.cs:594 | Seq scan em 50.000+ registros por busca livre |
Concorrencia e locks
| Achado | Classificacao | Evidencia |
|---|---|---|
QuantidadeCarros-- em memoria sem UPDATE atomico (race condition) | Gargalo atual confirmado — impacto de negocio | RegistrarOfertaService.cs:528-529 |
Lock global por tenant no RegistrarOfertaService serializa TODO o processamento do tenant | Risco de crescimento | RegistrarOfertaService.cs:93 |
Lock Redis dentro de UoW aberta em OfertasAceitePreparationService: ate 600s de transacao aberta | Risco de crescimento | OfertasAceitePreparationService.cs:60-102 |
Task.WhenAll sem throttling em ProcessarOfertasWorker: 150 tasks paralelas esgotam connection pool | Risco de crescimento (critico com 150 tenants) | ProcessarOfertasWorker.cs:57-73 |
Background workers
| Achado | Classificacao | Evidencia |
|---|---|---|
RegistrarOfertasWorker reprocessa 7 dias x 6 intervalos = 924 requisicoes HTTP/ciclo com 22 tenants | Gargalo atual confirmado | RegistrarOfertasWorker.cs:43,137-161 |
AtualizarOfertasWorker processa tenants sequencialmente: ciclo pode levar horas com 150 tenants | Risco de crescimento | AtualizarOfertasWorker.cs:50-68 |
VerificarRotasWorker: N queries Firebase por rota expirada sem rate limiting | Risco de crescimento | VerificarRotasWorker.cs:54-69 |
Duplicacao de logica de limpeza de ApiStatus entre RegistrarOfertasWorker e RetentionPolicyWorker | Hipotese (dependente de timing) | RegistrarOfertasWorker.cs:99 e ApiStatusRetentionTask.cs:25 |
ApiStatusRetentionTask usa HardDeleteAsync sem paginacao — pode deletar centenas de milhares em 1 transacao | Risco de crescimento | ApiStatusRetentionTask.cs:25 |
Indices ausentes no banco
| Tabela | Coluna(s) ausentes | Impacto |
|---|---|---|
AppRotas | Indice composto (Origem, Destino, Carga, Status) — filtro mais frequente do sistema | 13.200 full scans/ciclo no RegistrarOfertaService |
AppOfertas | FilterText em colunas texto sem pg_trgm | Seq scan em buscas livres |
AppApiStatuses | TenantId, Data | Degradacao progressiva com ~288 registros/dia/tenant |
Frontend e Docker
| Achado | Classificacao | Evidencia |
|---|---|---|
| Budget Angular de 2.5MB muito permissivo para PWA mobile; 15+ CSS bundles carregados | Risco de crescimento | angular/angular.json:234-241 |
ngsw-config.json sem dataGroups para cache de respostas de API | Risco de crescimento | angular/ngsw-config.json |
npm install sem --ci no Dockerfile frontend — builds nao deterministicos | Risco de crescimento | angular/Dockerfile:6 |
Backend Dockerfile invalida layer cache do apt-get a cada dist-upgrade | Risco de crescimento | aspnet-core/src/cargo_fleet.HttpApi.Host/Dockerfile:4-6 |
9. Divida Tecnica
9.1 Violacoes de arquitetura
| Violacao | Evidencia | Impacto |
|---|---|---|
FirebaseAdmin no Domain | cargo_fleet.Domain.csproj linha 16 | Domain layer depende de infraestrutura de push |
Application.Contracts referencia Domain | cargo_fleet.Application.Contracts.csproj linha 13 | Contratos acoplados a entidades de dominio |
ServiceConfigurationContext injetado em runtime | CargaAdicionalService.cs linha 27 | Objeto de bootstrap usado como servico de runtime |
| Version mismatch ABP 9.0.0 vs 8.3.3 | package.json vs .csproj | Incompatibilidades de contrato de API entre frontend e backend |
9.2 Codigo comentado e legado
| Item | Localizacao | Risco |
|---|---|---|
ValidarOrigemDestino() comentada | Rota.cs | Validacao de regra de negocio desativada silenciosamente |
RegistrarOfertasWorker — corpo comentado, classe existente | RegistrarOfertasWorker.cs | Codigo legado de 740 linhas (com RegistrarOfertaService) presente mas inativo; confusao de manutencao |
| Event handlers do ScraperOrchestrator comentados | Identificado nos revisores | Servicos registrados mas handlers desativados — comportamento indefinido |
9.3 Qualidade de engenharia
| Item | Observacao |
|---|---|
| Testes nao executados no CI/CD | 4 projetos de teste existem mas nenhum e executado nos pipelines |
OnModelCreating com entidades duplicadas | cargo_fleetDbContext.cs — configuracoes de entidade possivelmente duplicadas |
Timer.Period = 1ms sem documentacao | Workers ProcessarOfertasWorker e AtualizarOfertasWorker — throttle implementado manualmente, nao pelo framework |
| Scryber.Core em versao beta | 8.0.0.1-beta usada em producao para geracao de PDF |
Uso de DateTime.Now em vez de DateTime.UtcNow | ProcessarOfertasWorker.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.yamle acionado automaticamente por push emmaster. Um commit acidental emmasterdispara 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-uiesta potencialmente exposto sem autenticacao.
10.3 Incident response
- Workers silenciosos:
ProcessarOfertasWorkereAtualizarOfertasWorkercapturam todas as excecoes comtry/catchgenerico 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.CreateAsyncpara 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.yamlde 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 comlatestpermitem quekubectl rollout restartpuxe uma versao diferente da que foi validada, se uma nova imagemlatestfoi pushada entre o build e o restart. - Runner
gemini: O pipeline de producao usaruns-on: gemini(runner auto-hospedado). Se este runner e o mesmo k3s de desenvolvimento, ha risco de contaminacao entre ambientes.
Fluxo principal: Clientes → Entrada/Edge → Backend → Dados → Integracoes
Clientes
Entrada
Backend
Dados
Integracoes
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)
cargo-fleet-apiecargo-fleet-authem AWS EKS.Pontos de atencao imediatos desta topologia
emptyDir, alto risco operacional.Background Workers ativos na API (HttpApi.Host):
ProcessarOfertasWorkerTask.WhenAll)AtualizarOfertasWorkerVerificarRotasWorkerRetentionPolicyWorkerIMultiTenantRegistrarOfertasWorker