Rust API Architecture: A Summary¶
This document outlines a robust, layered architecture for building scalable and maintainable APIs in Rust. The design prioritizes separation of concerns, type safety, and reusability.
Core Architecture: A Layered Approach¶
The architecture is divided into distinct layers, each with a single responsibility. This decouples the core business logic from the delivery mechanism (e.g., HTTP), allowing for greater flexibility and testability.
graph TD
subgraph "External Interfaces"
HTTP_Requests[("Web & API Clients")]
end
HTTP_Requests --> HTTP_Handlers
subgraph "Application Delivery (The 'Adapter')"
HTTP_Handlers[HTTP Handler Layer]
end
HTTP_Handlers --> RPC_Facade
subgraph "Application Core"
RPC_Facade[RPC Interface Layer (The 'Port')]
Controller_Layer[Controller Layer]
Store_Layer[Data Access Layer]
end
RPC_Facade --> Controller_Layer
Controller_Layer --> Store_Layer
subgraph "Data Persistence"
Store_Layer --> Database[(Database)]
end - HTTP Handler Layer: Translates HTTP requests and responses. Knows about web-specific concerns like status codes and headers.
- RPC Interface Layer: Defines the application's public contract in a transport-agnostic way (e.g.,
create_account(...)). This is the single entry point to the core logic. - Controller Layer: Orchestrates business logic and use cases, often coordinating multiple stores.
- Store Layer: Manages data persistence and queries for a single entity.
The Data Access Layer (DAL): A Trait-Based Design¶
The DAL is built on a "Foundation + Capability" pattern using traits.
Foundational Trait: Store¶
A single base trait defines the core identity of a data store. The compiler enforces this contract for every store.
pub trait Store {
// The Iden enum for the store's table and columns.
type TableIden: 'static + Iden;
// Guaranteed constants for the table and primary key.
const TABLE_NAME: Self::TableIden;
const TABLE_PK: Self::TableIden;
// The type of the Primary Key (e.g., Uuid, i64).
type IdKind: ToString + Into<sea_query::Value> + Send;
// The Rust struct that represents a database row.
type Row: for<'r> FromRow<'r, PgRow> + Unpin + Send + Sync;
}
Capability Traits¶
Additional traits grant specific abilities (CRUD, Joins, etc.) to a store.
// Example: grants the `.create()` method
pub trait Create where Self: Store {
type CreateStoreParams: HasSeaFields + Send;
async fn create(&self, ctx: &StoreCtx, data: Self::CreateStoreParams) -> Result<Self::Row>;
}
// Example: grants the `.get_joined()` method for one-to-many relationships
pub trait JoinOneToManyStore where Self: Store {
// ... metadata for the related table and join condition
async fn get_joined(&self, ctx: &StoreCtx, id: Self::IdKind) -> Result<Self::JoinedRow>;
}