ORM async on Tokio. HTTP: axum (DatabaseConnection in State). SQL compile-time / explicit queries: sqlx. Diesel: diesel. Motor: postgres. Do not mix SeaORM and sqlx in the same aggregate.
Preserve the backend (sqlx-postgres etc.) that the crate already uses. Do not introduce SeaORM if the repo is already sqlx/Diesel.
Stack
| Crate | Role |
|---|---|
sea-orm |
Entities, queries, tx |
sea-orm-migration |
Async Migrator |
sea-orm-cli |
generate entity if the team already generates |
Organization
migration/ # crate migrator (ou pasta do repo)
src/
entities/ # Model, ActiveModel, Column
db.rs # connect, pool
- Generated entities or hand-written — the project's flow. Do not edit generated and hand-written in the same file without a boundary.
- Relationships in the aggregate module, not a
entities.rsof 3k lines.
Non-negotiable rules
- Pool at boot; clone in the
State. NoDatabase::connectper request. - Transaction on composite writes (
db.transaction(|txn| async move { … })). - Tenancy/
user_idin the filter. Consistent soft delete if the schema has it. ActiveModelfor mutation; not blindUPDATEof all fields if the domain is partial.- Errors: map
DbErr→AppError(404/409). No SQL in the JSON.
Models
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]+enum Relation.Column/Entity::find_by_id.find().filter().one/all.- Cursor/keyset in hot lists — not deep
offset(postgres). - JSON:
Json/JsonBinaryonly for unstable shape; invariants in columns.
Axum
async fn get_order(
State(db): State<DatabaseConnection>,
Path(id): Path<Uuid>,
) -> Result<Json<Order>, AppError> {
let row = Order::find_by_id(id).one(&db).await?;
// …
}
- Handler does not build an SQL string. Services if the aggregate is fat.
- Migrations in the deploy before the serve (
rust-axum-production).auto-migrateonly in dev if the repo already does it.
Anti-patterns
find().all()without a limit- God Entity with 40 eager relations
unwrapinDbErr- SeaORM +
sqlx::query!in the same use case “because the generate failed” - Sync/diesel Connection in the SeaORM handler
Integration with other skills
- Runtime:
tokio - HTTP:
axum - Raw SQL:
sqlx - Classic ORM:
diesel - Observability:
tracing(instrumentin the repository) - Tests:
rust-unit-testing
Completion criteria
- Pool in the
State; tx in composite writes - Ownership/tenant filter
- Versioned migration if the schema changed
DbErrmapped; no SQL in the response- One persistence style per aggregate