-
-
Notifications
You must be signed in to change notification settings - Fork 0
Примеры
Практические примеры для распространённых случаев использования.
Классическая сущность пользователя с полями аутентификации:
use entity_derive::Entity; use uuid::Uuid; use chrono::{DateTime, Utc}; #[derive(Entity)] #[entity(table = "users", schema = "auth")] pub struct User { #[id] pub id: Uuid, #[field(create, update, response)] pub username: String, #[field(create, update, response)] pub email: String, #[field(create, update)] // Принимаем, но никогда не возвращаем pub password_hash: String, #[field(response)] pub email_verified: bool, #[field(update, response)] pub avatar_url: Option<String>, #[field(response)] pub role: String, #[field(skip)] // Внутреннее поле аудита pub last_login_ip: Option<String>, #[auto] #[field(response)] pub created_at: DateTime<Utc>, #[auto] #[field(response)] pub updated_at: DateTime<Utc>, }
Использование:
// Создание пользователя let request = CreateUserRequest { username: "john_doe".into(), email: "john@example.com".into(), password_hash: hash_password("secret123"), }; let user = pool.create(request).await?; // Обновление пользователя let update = UpdateUserRequest { avatar_url: Some(Some("https://cdn.example.com/avatar.jpg".into())), ..Default::default() }; let user = pool.update(user.id, update).await?; // Response безопасен - без password_hash, без last_login_ip let response = UserResponse::from(&user);
Посты со связью с автором:
#[derive(Entity)] #[entity(table = "posts", schema = "blog")] pub struct Post { #[id] pub id: Uuid, #[field(create, update, response)] pub title: String, #[field(create, update, response)] pub slug: String, #[field(create, update, response)] pub content: String, #[field(create, update, response)] pub excerpt: Option<String>, #[field(create, response)] // Задаётся один раз, нельзя сменить автора pub author_id: Uuid, #[field(update, response)] pub published: bool, #[field(update, response)] pub published_at: Option<DateTime<Utc>>, #[field(response)] // Только чтение, управляется триггерами pub view_count: i64, #[field(skip)] // Внутренняя модерация pub moderation_status: String, #[auto] #[field(response)] pub created_at: DateTime<Utc>, #[auto] #[field(response)] pub updated_at: DateTime<Utc>, }
Категории со связью многие-ко-многим:
#[derive(Entity)] #[entity(table = "categories", schema = "blog")] pub struct Category { #[id] pub id: Uuid, #[field(create, update, response)] pub name: String, #[field(create, update, response)] pub slug: String, #[field(create, update, response)] pub description: Option<String>, #[field(response)] pub post_count: i64, // Вычисляемое поле #[auto] #[field(response)] pub created_at: DateTime<Utc>, }
Каталог продуктов:
#[derive(Entity)] #[entity(table = "products", schema = "catalog")] pub struct Product { #[id] pub id: Uuid, #[field(create, update, response)] pub name: String, #[field(create, update, response)] pub sku: String, #[field(create, update, response)] pub description: Option<String>, #[field(create, update, response)] pub price_cents: i64, #[field(create, update, response)] pub currency: String, #[field(update, response)] pub stock_quantity: i32, #[field(update, response)] pub is_active: bool, #[field(create, response)] pub category_id: Uuid, #[field(skip)] // Внутренний учёт себестоимости pub cost_cents: i64, #[field(skip)] // Информация о поставщике pub supplier_id: Option<Uuid>, #[auto] #[field(response)] pub created_at: DateTime<Utc>, #[auto] #[field(response)] pub updated_at: DateTime<Utc>, }
Заказы с отслеживанием статуса:
#[derive(Entity)] #[entity(table = "orders", schema = "sales")] pub struct Order { #[id] pub id: Uuid, #[field(create, response)] pub customer_id: Uuid, #[field(response)] pub order_number: String, // Генерируется sequence БД #[field(update, response)] pub status: String, #[field(create, response)] pub total_cents: i64, #[field(create, response)] pub currency: String, #[field(create, update, response)] pub shipping_address: String, #[field(update, response)] pub tracking_number: Option<String>, #[field(skip)] // Данные платёжного процессора pub payment_intent_id: Option<String>, #[field(skip)] // Внутренние заметки pub admin_notes: Option<String>, #[auto] #[field(response)] pub created_at: DateTime<Utc>, #[auto] #[field(response)] pub updated_at: DateTime<Utc>, }
Сущности с привязкой к организации:
#[derive(Entity)] #[entity(table = "organizations", schema = "tenants")] pub struct Organization { #[id] pub id: Uuid, #[field(create, update, response)] pub name: String, #[field(create, response)] pub slug: String, // Неизменяемый после создания #[field(update, response)] pub plan: String, #[field(response)] pub member_count: i32, #[field(skip)] // Платёжная информация pub stripe_customer_id: Option<String>, #[auto] #[field(response)] pub created_at: DateTime<Utc>, } #[derive(Entity)] #[entity(table = "projects", schema = "tenants")] pub struct Project { #[id] pub id: Uuid, #[field(create, response)] // Задаётся один раз pub organization_id: Uuid, #[field(create, update, response)] pub name: String, #[field(create, update, response)] pub description: Option<String>, #[field(update, response)] pub archived: bool, #[auto] #[field(response)] pub created_at: DateTime<Utc>, #[auto] #[field(response)] pub updated_at: DateTime<Utc>, }
Для API-контрактов без персистенции:
#[derive(Entity)] #[entity(table = "webhooks", sql = "none")] pub struct WebhookPayload { #[id] pub id: Uuid, #[field(create, response)] pub event_type: String, #[field(create, response)] pub payload: String, #[field(create, response)] pub timestamp: DateTime<Utc>, #[field(response)] pub signature: String, }
Генерируется только:
CreateWebhookPayloadRequestWebhookPayloadResponse- Реализации
From
Без репозитория, без SQL, без Row/Insertable структур.
Когда стандартного CRUD недостаточно:
#[derive(Entity)] #[entity(table = "analytics_events", schema = "analytics", sql = "trait")] pub struct AnalyticsEvent { #[id] pub id: Uuid, #[field(create, response)] pub event_name: String, #[field(create, response)] pub user_id: Option<Uuid>, #[field(create, response)] pub properties: serde_json::Value, #[auto] #[field(response)] pub created_at: DateTime<Utc>, } // Реализуйте пользовательские запросы самостоятельно: impl AnalyticsEventRepository for PgPool { type Error = sqlx::Error; async fn create(&self, dto: CreateAnalyticsEventRequest) -> Result<AnalyticsEvent, Self::Error> { // Пакетная вставка, партиционированные таблицы и т.д. } async fn find_by_id(&self, id: Uuid) -> Result<Option<AnalyticsEvent>, Self::Error> { // Запрос с учётом временного партиционирования } // Пользовательские методы за пределами CRUD: async fn aggregate_by_event(&self, start: DateTime<Utc>, end: DateTime<Utc>) -> Result<Vec<EventAggregate>, Self::Error> { // Сложный агрегационный запрос } }
Каждый репозиторий получает батч-примитивы: find_by_ids (WHERE id = ANY(1ドル), один запрос), атомарный create_many (одна транзакция, сбой любой строки откатывает весь батч) и delete_many с учётом soft delete, возвращающий число реально затронутых строк. При включённой доставке событий (streams или outbox) события эмитятся на каждую строку в той же транзакции.
let posts: Vec<Post> = pool.find_by_ids(ids).await?; let created: Vec<Post> = pool.create_many(dtos).await?; let removed: u64 = pool.delete_many(stale_ids).await?;
🇬🇧 English | 🇷🇺 Русский | 🇰🇷 한국어 | 🇪🇸 Español | 🇨🇳 中文
Getting Started
Features
Advanced
Начало работы
Возможности
Продвинутое
시작하기
기능
고급
Comenzando
Características
Avanzado
入门
功能
高级