This document records the rationale behind key architecture decisions for SoroScan.
Use the Soroban RPC interface through stellar_sdk.SorobanServer instead of relying solely on Horizon event streaming.
SoroScan indexes Soroban contract events from Stellar. Horizon provides general ledger access, but Soroban RPC exposes contract-specific event semantics and better transaction simulation capabilities.
- Pros:
- richer contract event metadata (
type,value,xdr,ledger) - direct support for contract-level filters and event pagination
- stronger compatibility with Soroban contract semantics
- richer contract event metadata (
- Cons:
- dependency on Soroban-enabled RPC nodes
- slightly higher operational complexity than HTTP-only Horizon polling
django-backend/soroscan/ingest/tasks.pyusesSorobanServer.get_events().django-backend/soroscan/ingest/stellar_client.pyuses the Soroban client for contract writes and transaction lookup.
Use PostgreSQL as the primary database for SoroScan.
Event indexing requires durable persistence, query flexibility, and support for relational integrity.
- Pros:
- strong ACID guarantees for event writes
- JSON/JSONB payload support for flexible event storage
- powerful indexing for event queries and time-range filters
- excellent Django ORM and migration support
- Cons:
- not as schemaless as some NoSQL stores, but JSON fields mitigate this.
django-backend/soroscan/settings.pyconfiguresDATABASE_URL.- schema models are defined in
django-backend/soroscan/ingest/models.py.
Use Strawberry GraphQL for schema-driven GraphQL support.
GraphQL is important for developer-friendly event exploration and nested contract queries.
- Pros:
- type-safe schema definition with Python dataclasses
- direct integration with Django models
- easier to extend with query resolvers and custom fields
- Cons:
- implementation complexity compared to pure REST
- requires careful rate limiting and introspection control
django-backend/soroscan/ingest/schema.pydefines the GraphQL API.django-backend/soroscan/graphql_views.pywraps GraphQL view behavior.django-backend/soroscan/asgi.pywires WebSocket subscriptions.
Use exponential backoff as the default retry strategy for failing webhook deliveries.
Webhook subscribers can fail transiently, return HTTP 429, or temporarily be unavailable. Immediate retry loops can overwhelm subscribers and the delivery system.
- Pros:
- reduces replay storm risk
- gives target systems time to recover
- aligns with standard webhook delivery best practices
- Cons:
- longer time to recovery for some intermittent failures compared to constant retry
- default strategy:
WebhookSubscription.BACKOFF_EXPONENTIAL - configurable base delay via
retry_backoff_seconds dispatch_webhookuses Celery retry backoff and jittercalculate_backoff()indjango-backend/soroscan/ingest/tasks.pycomputes delays
Use Redis for caching, rate limiting, Celery broker/result backend, and Channels pub/sub.
SoroScan needs a low-latency shared store for ephemeral data and cross-process coordination.
- Pros:
- centralized rate limit counters and query caches
- single dependency for Celery and Channels
- significant performance gains for repeated query patterns
- Cons:
- Redis now becomes a critical operational dependency
- requires careful sizing for persistence and eviction policies
- Django cache backend in
django-backend/soroscan/settings.py - Channels layer uses Redis in the same settings file
- Celery broker/result backend also points at Redis
- cache utilities in
django-backend/soroscan/ingest/cache_utils.py
Build the backend in Django and the user-facing UI in Next.js.
The backend needs a solid API and data model layer. The frontend needs a modern developer dashboard.
- Pros:
- Django gives rapid backend development with strong database support
- Next.js gives a responsive and extensible React UI
- separation enables independent backend and frontend deployment
- Cons:
- two codebases with separate build and deployment concerns
- backend in
django-backend/ - dashboard and admin UI in
soroscan-frontend/andadmin/
These ADRs are meant to capture the current reasoning for the architectural decisions in SoroScan. For future changes, add a new numbered ADR and update this document.