NovaeXis Core — Orquestrador Estadual
Sistema central que conecta municípios ao estado, gerencia consentimentos e fornece dashboards unificados.
2 min de leitura
Propósito
Federação Municipal → Estadual
Hub central que gerencia consentimentos, agregação de dados municipais em métricas estaduais e oferece dashboards unificados aos gestores.
Arquitetura
Camada 1
Citizen Gateway
Interface ao cidadão
ConsentimentosHistóricoExport
Camada 2
Municipality Gateway
Integração CVTech
IngestãoValidaçãoThrottling
Camada 3
State Dashboard
Visão estadual
KPIsComparativosDrill-down
Camada 4
API Gateway (Centralized)
Acesso unificado
OAuth2QuotasAuditoria
Consent Management System
ts
export class ConsentService {
constructor(private repo: ConsentRepository, private audit: AuditService, private clock: Clock) {}
async grant(input: GrantConsentInput): Promise<Consent> {
const expiresAt = this.computeExpiry(input.duration);
const consent = await this.repo.create({
citizenId: input.citizenId,
purpose: input.purpose,
dataDomains: input.dataDomains,
grantedTo: input.recipient,
grantedAt: this.clock.now(),
expiresAt,
status: "active",
});
await this.audit.record({ actor: input.citizenId, action: "consent.grant", consentId: consent.id });
return consent;
}
async revoke(citizenId: string, consentId: string) {
const consent = await this.repo.findOwned(citizenId, consentId);
if (!consent) throw new ForbiddenError();
await this.repo.update(consentId, { status: "revoked", revokedAt: this.clock.now() });
await this.audit.record({ actor: citizenId, action: "consent.revoke", consentId });
}
private computeExpiry(d: ConsentDuration): Date | null {
const map = { "1m": 30, "3m": 90, "6m": 180, "1y": 365, indef: null };
const days = map[d];
if (days === null) return null;
return new Date(this.clock.now().getTime() + days * 86_400_000);
}
}1 mês
3 meses
6 meses
1 ano
Indefinido
Cidadão→
Solicita→
Aprova→
Token gerado→
Acesso liberado
Data Aggregation Service
ts
export class AggregationService {
async aggregate(scope: StateScope, window: TimeWindow): Promise<StateMetrics> {
const municipalities = await this.repo.listMunicipalities(scope.uf);
const partials = await Promise.all(
municipalities.map((m) => this.collectFromMunicipality(m.id, window)),
);
const totals = this.sumByDomain(partials);
const trend = this.computeTrend(totals, window);
return { totals, trend, generatedAt: new Date() };
}
private computeTrend(curr: DomainTotals, w: TimeWindow): TrendMap {
const prev = this.repo.getPrevious(w);
const out: TrendMap = {};
for (const k of Object.keys(curr)) {
const base = prev[k] ?? 1;
out[k] = ((curr[k] - base) / base) * 100;
}
return out;
}
}Agregação Municipal → Estadual
SP-Capital+ Campinas+ … 643 municípios→Estado de SP (anonimizado)
Tendência:
((atual − anterior) / anterior) × 100Modelo de Dados
prisma
model Citizen {
id String @id @default(cuid())
cpfHash String @unique
profile CitizenProfile?
consents Consent[]
createdAt DateTime @default(now())
}
model CitizenProfile {
id String @id @default(cuid())
citizenId String @unique
fullName String
birthDate DateTime
city String
citizen Citizen @relation(fields: [citizenId], references: [id])
}
model Consent {
id String @id @default(cuid())
citizenId String
purpose String
dataDomains String[]
grantedTo String
status ConsentStatus
grantedAt DateTime
expiresAt DateTime?
revokedAt DateTime?
citizen Citizen @relation(fields: [citizenId], references: [id])
}
model Municipality {
id String @id @default(cuid())
ibgeCode String @unique
name String
uf String
aggregates AggregatedData[]
}
model AggregatedData {
id String @id @default(cuid())
municipalityId String
domain String
window String
metrics Json
generatedAt DateTime @default(now())
municipality Municipality @relation(fields: [municipalityId], references: [id])
}
model StateDashboard {
id String @id @default(cuid())
uf String
layout Json
updatedAt DateTime @updatedAt
}
