Architectural Overview
The NextNonce backend is engineered using a sophisticated, modular architecture built on the NestJS framework. The design prioritizes scalability, maintainability, and high performance by adhering to established software engineering principles, including a strict separation of concerns, dependency injection, and a robust provider-based model for external interactions. This structure ensures that the system remains flexible and adaptable to future technological changes and feature requirements.
This document provides a high-level view of the system's architecture, focusing on the interaction between its core components.
Core Architectural Pillars
The entire system is built upon several key architectural pillars that define its structure and behavior.
-
The Provider Pattern (Inversion of Control): This is the most critical architectural concept in the backend. For every piece of logic that involves an external service (like an authentication provider or a data API) or complex, interchangeable logic (like caching), a formal
interface
is defined. Services within the application depend only on these interfaces, not on concrete implementations. The specific implementation—theprovider
—is determined at runtime via NestJS's dependency injection system. This results in:-
Decoupling & Interchangeability: The application is not tied to any specific vendor. For example, the
AuthProvider
can be implemented bySupabaseAuthProvider
today and a different provider tomorrow with zero changes to the consumingAuthService
. -
High Testability: During unit testing, it is trivial to provide a mock implementation of any provider interface. This allows services to be tested in complete isolation without making real database queries or network calls.
-
Clean Code: Vendor-specific logic (API keys, request signing, data transformation) is completely encapsulated within the provider, keeping the core business logic in the services clean and focused.
-
-
Layered & Modular Design: The application is organized into distinct feature modules (
AuthModule
,PortfolioModule
,BalanceModule
, etc.), each containing its own controllers, services, and providers. Within these modules, a clear layered architecture is enforced:-
Controllers: A thin layer responsible only for handling HTTP requests, validating inputs via DTOs, and delegating tasks to the service layer.
-
Services: The core of the application where all business logic resides. Services orchestrate data flow, interact with the database, manage caching, and coordinate with other services and providers.
-
-
Centralized, Multi-Layered Caching: Performance is a key concern, addressed through an aggressive and intelligent caching strategy powered by Redis.
-
A central
CacheService
abstracts all caching operations, providing a consistent API for the entire application. -
The system employs the Cache-Aside Pattern: services always check the cache first. Only on a "cache miss" is the more expensive operation (a database query or external API call) performed, with the result being stored in the cache to accelerate subsequent requests.
-
Caching is often multi-layered. For instance, the
AuthService
caches theAuthUserDto
, while theUserService
separately caches the internalUser
object. Similarly, theBalanceService
can construct portfolio balances from the cached balances of individual wallets.
-
-
Data Flow and Transformation (DTOs): The backend maintains a strict boundary between its internal data models (defined by Prisma) and the data it exposes to the outside world. Services operate on the rich internal models, but controllers always return sanitized Data Transfer Objects (DTOs). This ensures a stable API contract and prevents accidental leakage of sensitive or internal-only fields.
Component Breakdown
The architecture is composed of several types of components, each with a specific role.
Foundational Services
These services provide the core infrastructure and utilities used by all other parts of the application.
-
DatabaseService
: A direct extension of thePrismaClient
, managing the database connection lifecycle and serving as the single gateway for all database operations. -
CacheService
: The high-level interface to the caching system. It uses aCacheProvider
to interact with Redis and provides a standardized method for generating cache keys to prevent collisions. -
RedisClientService
: A low-level service that manages theioredis
client instance, handling connection, error, and retry logic. -
AppLoggerService
: A custom logger that extends the NestJSConsoleLogger
to provide persistent, file-based logging for different severity levels. -
RateLimiterService
: A generic service that orchestrates aRateLimiterProvider
to enforce rate limits on external API calls. Crucially, this service is consumed by the Providers (e.g.,AlchemyWalletTypeProvider
,DuneTokenMetadataProvider
) to ensure that calls to external APIs are globally synchronized and rate-limited across all running instances of the backend, using Redis as the shared state.
Business Logic Services & Utilities
These services contain the application's core features and business rules.
-
AuthService
: Orchestrates user authentication by coordinating with theAuthProvider
and caching the results. -
UserService
: Manages the user lifecycle, including creation (which also triggers the creation of a default portfolio), retrieval, and deletion. -
ChainService
&TokenService
: These are in-memory lookup services. On application startup, they load all chain and unified token data into a map for instantaneous, synchronous access, eliminating repetitive database queries for this static data. -
WalletService
: Responsible for creating and retrieving wallet entities. It relies on injectable utility classes:AddressUtils
for normalizing and validating addresses, andWalletTypeUtils
to determine if a wallet is aSIMPLE
EOA or aSMART
contract. -
WalletTypeUtils
: A utility class that, in turn, uses theWalletTypeProvider
interface to perform the external check for a smart contract. -
PortfolioService
andPortfolioWalletService
: Together, these manage user-specific portfolios. They handle access control, CRUD operations, and the linking/unlinking of wallets to portfolios. -
MetadataService
: A complex orchestration service that fetches token metadata (name, symbol, logo). It uses aTokenMetadataProvider
to get data from an external source, intelligently checks a lookup cache, and saves new token information to the database when first encountered. -
PriceService
: Manages all token price data. It efficiently updates the latest prices and historical daily snapshots in both the cache and the database, and provides a method to calculate 24-hour price changes by reading exclusively from the cache. -
BalanceService
: The most critical orchestration service. It synthesizes data from nearly every other service (MetadataService
,PriceService
,TokenService
,BalanceProvider
) to compute and aggregate the final, USD-valued balances for a wallet or an entire portfolio, including the logic for the "unified token" feature.
Providers: The Concrete Implementations
Providers are the concrete classes that implement the abstract interfaces and contain all vendor-specific logic.
-
RedisCacheProvider
: Implements theCacheProvider
interface usingioredis
, handling the serialization and deserialization of cached objects. -
SupabaseAuthProvider
: Implements theAuthProvider
interface by making API calls to the Supabase service to validate JWTs. -
OkxDexBalanceProvider
&DuneTokenMetadataProvider
: These implement theBalanceProvider
andTokenMetadataProvider
interfaces, respectively. They contain all the logic for authenticating with, signing requests for, and parsing responses from the OKX DEX and Sim Dune APIs. -
AlchemyWalletTypeProvider
: Implements theWalletTypeProvider
interface to determine if a wallet address is a smart contract by querying the Alchemy API. -
RedisRateLimiterProvider
: Implements theRateLimiterProvider
using therate-limiter-flexible
library backed by a dedicated Redis client.
High-Level Interaction Diagram
This diagram illustrates the dependencies between the system's major components, focusing on the flow of logic between services, utilities, and providers. Core infrastructure services like DatabaseService
,CacheService
and AppLoggerService
are used by most services.