Skip to content

Commit 296465d

Browse files
committed
Add documentation about components life cycle
1 parent 0f50f88 commit 296465d

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

docs/lifecycle.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
## When should you create each component?
2+
3+
### Bind manager
4+
5+
The `SQLAlchemyBindManager` object holds all the SQLAlchemy Engines, which
6+
are supposed to be global objects. Therefore it should be created on application
7+
startup and be globally accessible.
8+
9+
From SQLAlchemy documentation:
10+
11+
> The Engine is intended to normally be a permanent fixture established up-front
12+
> and maintained throughout the lifespan of an application.
13+
14+
### Repositories
15+
16+
[SQLAlchemy documentation](https://docs.sqlalchemy.org/en/20/orm/session_basics.html#when-do-i-construct-a-session-when-do-i-commit-it-and-when-do-i-close-it)
17+
recommends we create `Session` object at the beginning of a logical operation where
18+
database access is potentially anticipated.
19+
20+
The repository starts a `Session` for each _operation_, in order to maintain isolation.
21+
This means you can create a repository object almost whenever you want.
22+
23+
/// details | The only exception is creating repositories in global variables!
24+
type: warning
25+
The repository creates a session object, which is not thread safe, to avoid
26+
emitting unnecessary queries to refresh the model state on new session.
27+
28+
Being the session object not thread safe, the repository is not thread safe as well.
29+
30+
Check the [Notes on multithreaded applications](/manager/session/#note-on-multithreaded-applications)
31+
///
32+
33+
Even using multiple repository instances will work fine, however as they will have completely
34+
different sessions, it's likely that the second repository will fire additional SELECT queries
35+
to get the state of the object prior to saving it.
36+
37+
/// details | Example
38+
```python
39+
from sqlalchemy import String
40+
from sqlalchemy.orm import Mapped, mapped_column
41+
from sqlalchemy_bind_manager import SQLAlchemyBindManager ,SQLAlchemyConfig
42+
from sqlalchemy_bind_manager.repository import SQLAlchemyRepository
43+
44+
config = SQLAlchemyConfig(
45+
engine_url="sqlite:///./sqlite.db",
46+
engine_options=dict(connect_args={"check_same_thread": False}, echo=True),
47+
session_options=dict(expire_on_commit=False),
48+
)
49+
50+
sa_manager = SQLAlchemyBindManager(config={})
51+
52+
class MyModel(sa_manager.get_bind().model_declarative_base):
53+
id: Mapped[int] = mapped_column(primary_key=True)
54+
name: Mapped[str] = mapped_column(String(30))
55+
56+
def update_my_model():
57+
# Create 2 instances of the same repository
58+
repo = SQLAlchemyRepository(sa_manager.get_bind(), model_class=MyModel)
59+
repo2 = SQLAlchemyRepository(sa_manager.get_bind(), model_class=MyModel)
60+
61+
o = repo.get(1)
62+
o.name = "John"
63+
64+
repo2.save(o)
65+
66+
update_my_model()
67+
```
68+
///
69+
70+
The recommendation is of course to use the same repository instance where possible,
71+
and structure your code in a way to match the single repository instance approach.
72+
73+
For example a strategy similar to this would be optimal, if possible:
74+
75+
* Create repositories
76+
* Retrieve all the models you need
77+
* Do the changes you need, as per business logic
78+
* Save all the changed models as needed
79+
80+
### Unit of work
81+
82+
The Unit of Work session management follows the **same exact rules as the repository**,
83+
therefore you should approach the creation af a `UnitOfWork` object in the same way.

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ nav:
4747
- Repository:
4848
- Repository usage: repository/repository.md
4949
- Unit of work: repository/uow.md
50+
- Components life cycle: lifecycle.md
5051

5152
markdown_extensions:
5253
- pymdownx.details

0 commit comments

Comments
 (0)