# Advisor Radar

The same signal, seen from both sides of the conversation.

[HTML](https://leohteixeira.com/en/projects/advisor-radar) · Leonardo Teixeira

## The product

Advisor Radar turns account events and client messages into prioritized work for a financial advisory team. Enter as a fictional client to generate signals, or as an advisor to handle the queue, review uncertain classifications and follow cases with segment-specific SLAs.

Stack: Go, React, TypeScript, PostgreSQL, RabbitMQ, Elasticsearch, gRPC, Jev, OpenTelemetry

### Proactive alerts

Deterministic rules flag withdrawals, portfolio drops, large deposits, segment changes, clients without contact and purchases above their investor profile.

### Message triage

Intent, frustration, churn risk and requests for human help. Confidence below 0.85 goes to human review; model failure uses a marked heuristic fallback.

### Cases with SLAs

Open, in progress, waiting for the client, resolved. Segment-specific SLAs escalate through RabbitMQ TTL and dead-letter exchanges.

### Server-driven screens

The backend selects each section variant for the client’s current context. The frontend renders a fixed component catalog.

## The main flow

1. **A client sends a complaint** — The React app sends versioned HTTP/JSON to the BFF, which validates and forwards the command.

2. **The BFF calls account-sim** — gRPC carries a deadline and tracing context to the account service.

3. **Message and event commit together** — PostgreSQL saves the message and transactional outbox record in the same transaction. A broker outage leaves the event available for delivery.

4. **The relay publishes to RabbitMQ** — The event carries event_id, occurred_at, customer_id and schema_version for event correlation. RabbitMQ trace-context propagation is not implemented.

5. **Triage classifies the message** — An idempotent inbox deduplicates event_id. Jev classifies intent, frustration, churn risk and requests for human help. A marked heuristic fallback handles model unavailability; confidence below 0.85 goes to human review.

6. **An alert opens a case with an SLA** — Deterministic advisory rules create alerts. Cases use the client segment for their SLA; RabbitMQ TTL and dead-letter exchanges handle escalation.

7. **The client timeline updates** — The timeline-indexer writes events, classified messages and cases to Elasticsearch for the client’s searchable 360-degree view.

8. **The advisor queue receives the alert** — The BFF consumes alert.raised and sends an SSE update to the browser without a page reload.

## Architecture

```text
React → HTTP/JSON → BFF → gRPC → account-sim → PostgreSQL + outbox
Outbox relay → RabbitMQ → triage / advisory / cases / timeline-indexer
Jev → typed classification · Elasticsearch → client timeline · BFF → SSE → React
```

### Six Go services

account-sim, triage, advisory, cases, timeline-indexer and the stateless BFF have explicit responsibilities. PostgreSQL belongs to each stateful service; RabbitMQ carries events.

### Server-driven client screens

The BFF returns a typed page/section/component schema for the client’s current context. React renders a fixed catalog of components. The embedded showcase retains three client tabs, the live JSON disclosure, component rules, scrolling and retry. The phone preview is intentionally read-only.

## Availability and limits

Technical demonstration with fictional clients, names, brands and account activity. It has no affiliation with a real financial institution and sends no messages to real clients. The interactive showcase requires JavaScript and a reachable BFF; this explanation does not. A latency goal is not a measured result.

[Open demo](https://leohteixeira.com/advisor-radar/fila)

[Source on GitHub](https://github.com/leohteixeira/advisor-radar)
