Merge branch 'master' into ss/rocket-example

This commit is contained in:
Sam Samai 2021-09-02 22:30:59 +10:00
commit 3f6b2e3516
4 changed files with 87 additions and 5 deletions

39
ARCHITECTURE.md Normal file
View File

@ -0,0 +1,39 @@
# Architecture
To understand the architecture of SeaORM, let's discuss what is an ORM. ORM exists to provide abstractions over common operations you would do against a database and hide the implementation details like the actual SQL queries.
With a good ORM, you shouldn't bother to look under the API surface. Until you do. I hear you say *'abstraction leaks'*, and yes, it does.
The approach SeaORM takes is **'layered abstraction'**, where you'd dig one layer beneath if you want to. That's why we made SeaQuery into a standalone repository. It's useful on its own, and with a public API surface and a separate namespace, it's far more difficult to create confusing internal APIs than a monolithic approach.
The central idea in SeaORM is nearly everything is runtime configurable. At compile time, it does not know what the underlying database is.
What benefits does database-agnostic bring? For example, you can:
1. Make your app work on any database, depending on runtime configuration
1. Use the same models and transfer them across different databases
1. Share entities across different projects by creating a 'data structure crate', where the database is chosen by downstream 'behaviour crates'
The API of SeaORM is not a thin shell, but consist of layers, with each layer underneath being less abstract.
There are different stages when the API is being utilized.
So there are two dimensions to navigate the SeaORM code base, **'stage'** and **'abstractness'**.
First is the declaration stage. Entities and relations among them are being defined with the `EntityTrait`, `ColumnTrait` & `RelationTrait` etc.
Second is the query building stage.
The top most layer is `Entity`'s `find*`, `insert`, `update` & `delete` methods, where you can intuitively perform basic CRUD operations.
One layer down, is the `Select`, `Insert`, `Update` & `Delete` structs, where they each have their own API for more advanced operations.
One layer down, is the SeaQuery `SelectStatement`, `InsertStatement`, `UpdateStatement` & `DeleteStatement`, where they have a rich API for you to fiddle with the SQL syntax tree.
Third is the execution stage. A separate set of structs, `Selector`, `Inserter`, `Updater` & `Deleter`, are responsible for executing the statements against a database connection.
Finally is the resolution stage, when query results are converted into Rust structs for consumption.
Because only the execution and resolution stages are database specific, we have the possibility to use a different driver by replacing those.
I imagine some day, we will support a number of databases, with a matrix of different syntaxes, protocols and form-factors.

View File

@ -1,4 +1,4 @@
# Design Goals
# Design
We are heavily inspired by ActiveRecord, Eloquent and TypeORM.
@ -20,7 +20,7 @@ After some bitterness we realized it is not possible to capture everything compi
want to encounter problems at run time either. The solution is to perform checking at 'test time' to
uncover problems. These checks will be removed at production so there will be no run time penalty.
## Readability
## API style
### Turbofish and inference
@ -66,3 +66,34 @@ we'd prefer having a builder and stating the params explicitly:
```rust
has_many(cake::Entity).from(cake::Column::Id).to(fruit::Column::CakeId)
```
### Method overloading
Consider the following two methods, which accept the same parameter but in different forms:
```rust
fn method_with_model(m: Model) { ... }
fn method_with_active_model(a: ActiveModel) { ... }
```
We would define a trait
```rust
pub trait IntoActiveModel {
fn into_active_model(self) -> ActiveModel;
}
```
Such that `Model` and `ActiveModel` both impl this trait.
In this way, we can overload the two methods:
```rust
pub fn method<A>(a: A)
where
A: IntoActiveModel,
{
let a: ActiveModel = a.into_active_model();
...
}
```

View File

@ -25,7 +25,7 @@ This is an early release of SeaORM, the API is not stable yet.
```
[![Getting Started](https://img.shields.io/badge/Getting%20Started-blue)](https://www.sea-ql.org/SeaORM/docs/index)
[![Examples](https://img.shields.io/badge/Examples-orange)](https://github.com/SeaQL/sea-orm/tree/master/examples/sqlx)
[![Examples](https://img.shields.io/badge/Examples-orange)](https://github.com/SeaQL/sea-orm/tree/master/examples)
[![Starter Kit](https://img.shields.io/badge/Starter%20Kit-green)](https://github.com/SeaQL/sea-orm/issues/37)
[![Discord](https://img.shields.io/discord/873880840487206962?label=Discord)](https://discord.com/invite/uCPdDXzbdv)
@ -108,7 +108,7 @@ let pear: fruit::ActiveModel = Fruit::update(pear).exec(db).await?;
// update many: UPDATE "fruit" SET "cake_id" = NULL WHERE "fruit"."name" LIKE '%Apple%'
Fruit::update_many()
.col_expr(fruit::Column::CakeId, Expr::value(Value::Null))
.col_expr(fruit::Column::CakeId, Expr::value(Value::Int(None)))
.filter(fruit::Column::Name.contains("Apple"))
.exec(db)
.await?;
@ -148,6 +148,12 @@ fruit::Entity::delete_many()
.await?;
```
## Learn More
1. [Design](https://github.com/SeaQL/sea-orm/tree/master/DESIGN.md)
1. [Architecture](https://github.com/SeaQL/sea-orm/tree/master/ARCHITECTURE.md)
## License
Licensed under either of

View File

@ -25,7 +25,7 @@
//! ```
//!
//! [![Getting Started](https://img.shields.io/badge/Getting%20Started-blue)](https://www.sea-ql.org/SeaORM/docs/index)
//! [![Examples](https://img.shields.io/badge/Examples-orange)](https://github.com/SeaQL/sea-orm/tree/master/examples/sqlx)
//! [![Examples](https://img.shields.io/badge/Examples-orange)](https://github.com/SeaQL/sea-orm/tree/master/examples)
//! [![Starter Kit](https://img.shields.io/badge/Starter%20Kit-green)](https://github.com/SeaQL/sea-orm/issues/37)
//! [![Discord](https://img.shields.io/discord/873880840487206962?label=Discord)](https://discord.com/invite/uCPdDXzbdv)
//!
@ -180,6 +180,12 @@
//! # Ok(())
//! # }
//! ```
//!
//! ## Learn More
//!
//! 1. [Design](https://github.com/SeaQL/sea-orm/tree/master/DESIGN.md)
//! 1. [Architecture](https://github.com/SeaQL/sea-orm/tree/master/ARCHITECTURE.md)
//!
//! ## License
//!
//! Licensed under either of