Skip to main content

Database Strategy

Database choices follow the AWS infrastructure ADR (adr/servicos-serverless-aws.md).
EntityDatabaseRationale
Lead, ChannelIdentity, LeadOrganizationAurora PostgreSQL (via RDS Proxy)Identity resolution requires JOINs, UNIQUE constraints, ACID
EventDynamoDBAppend-only, high volume, key-based access
MemoryDynamoDBKey-value with upsert, access by lead_organization_id
FeatureDynamoDBSingle item, single-digit ms reads

DynamoDB Tables

event

PK:  lead_organization_id  (S)
SK:  occurred_at#id        (S)  → "2026-01-15T14:00:00Z#uuid"

Attributes: source_type, source, event_type, object_id (internal), object_id_ref (external),
            payload, raw_payload, external_event_id, received_at

GSI idempotency-index:
  PK: source (S)  SK: external_event_id (S)

GSI event-type-index:
  PK: lead_organization_id (S)  SK: event_type#occurred_at (S)

memory

PK:  lead_organization_id  (S)
SK:  memory_type#key       (S)  → "purchase#product:whey_1kg"

Attributes: value, confidence, source, source_event_ids (L), created_at, updated_at, expires_at (TTL)

Access patterns:
  GetAll:      Query PK = lead_organization_id
  ByType:      Query PK, SK begins_with("purchase#")
  SingleItem:  GetItem PK + SK
  Upsert:      PutItem (overwrites if exists)

feature

PK:  lead_organization_id  (S)
SK:  — (single item)

Attributes: features (MAP), computed_at

Full Diagram

Ingestion Flow

Lambdas

Event Ingester

PropertyValue
TriggerSQS: lead-ingestion
Reads fromAurora (identity resolution via ChannelIdentity)
Writes toDynamoDB (event), Aurora (creates lead/channel/lead_organization if new)
Publishes toSNS: lead-events

Memory Updater

PropertyValue
TriggerSQS: memory-processing
Reads fromDynamoDB (memory, event)
Writes toDynamoDB (memory)
Contains the derivation logic per event_type. The only component that knows each integration’s data structure.

Feature Computer

PropertyValue
TriggerSQS: feature-processing + EventBridge Scheduler
Reads fromDynamoDB (event, memory)
Writes toDynamoDB (feature)
The Feature Computer has two triggers because features change for different reasons:

SQS: feature-processing (near-realtime)

Triggered when a new event arrives via the lead-events SNS fan-out. Recalculates only the features affected by that specific event. Example: a purchase_completed event arrives → recalculates monetary_total, frequency_purchases_90d, days_since_last_purchase, has_purchased. Other features (like sentiment_avg_30d) are not touched.

EventBridge Scheduler (periodic batch)

Some features change over time even without new events:
FeatureWhy it changes without events
recency_daysYesterday it was 5, today it is 6. No event happened.
days_since_last_purchaseSame — increments daily.
campaign_ignoredIf a lead received a campaign 7 days ago and never engaged, the system needs to detect this.
sentiment_avg_30dOld sentiment events fall out of the 30-day window.
The EventBridge Scheduler runs on a cron (e.g., daily at 06:00 UTC), triggers the same Feature Computer Lambda, and recalculates time-dependent features for all leads that had activity within a recent window (e.g., last 90 days).

Summary

TriggerWhen it runsWhat it recalculates
SQS feature-processingWhen an event arrivesFeatures affected by that event
EventBridge SchedulerPeriodic cron (e.g., daily)Features that depend on elapsed time