-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Persisting Aggregates - Documentation
- Loading branch information
Showing
13 changed files
with
265 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Repositories | ||
|
||
- Collection-oriented design | ||
- Mimics a `set` collection | ||
- Has in-built Unit of Work context |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# Persisting State | ||
|
||
- About Repositories and Repository Pattern | ||
|
||
- Different available repositories | ||
- Repository Configuration | ||
- Automatic generation of repositories | ||
|
||
## Basic Structure | ||
|
||
A repository provides three primary methods to interact with the persistence | ||
store: | ||
|
||
### **`add`** - Adds a new entity to the persistence store. | ||
|
||
### **`get`** - Retrieves an entity from the persistence store. | ||
|
||
- Persisting aggregates | ||
- Retreiving aggregates | ||
- Queries | ||
- Data Access Objects | ||
- Removing aggregates | ||
|
||
- Custom Repositories | ||
- Registering a custom Repository | ||
- Database-specific Repositories | ||
|
||
- Working with the Application Layer | ||
- Unit of Work |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
# Persist Aggregates | ||
|
||
Aggregates are saved into the configured database using `add` method of the | ||
repository. | ||
|
||
```python hl_lines="23" | ||
{! docs_src/guides/persist-state/001.py !} | ||
``` | ||
|
||
1. Identity, by default, is a string. | ||
|
||
```shell | ||
In [1]: domain.repository_for(Person).get("1") | ||
Out[1]: <Person: Person object (id: 1)> | ||
|
||
In [2]: domain.repository_for(Person).get("1").to_dict() | ||
Out[2]: {'name': 'John Doe', 'email': 'john.doe@localhost', 'id': '1'} | ||
``` | ||
|
||
## Transaction | ||
|
||
The `add` method is enclosed in a [Unit of Work](unit-of-work.md) context by | ||
default. Changes are committed to the persistence store when the Unit Of Work | ||
context exits. | ||
|
||
The following calls are equivalent in behavior: | ||
|
||
```python | ||
... | ||
# Version 1 | ||
domain.repository_for(Person).add(person) | ||
... | ||
|
||
... | ||
# Version 2 | ||
from protean import UnitOfWork | ||
|
||
with UnitOfWork(): | ||
domain.repository_for(Person).add(person) | ||
... | ||
``` | ||
|
||
This means changes across the aggregate cluster are committed as a single | ||
transaction (assuming the underlying database supports transactions, of course). | ||
|
||
```python hl_lines="22-30 33" | ||
{! docs_src/guides/persist-state/002.py !} | ||
``` | ||
|
||
!!!note | ||
This is especially handy in ***Relational databases*** because each entity is a | ||
separate table. | ||
|
||
## Events | ||
|
||
The `add` method also publishes events to configured brokers upon successfully | ||
persisting to the database. | ||
|
||
```python hl_lines="15" | ||
{! docs_src/guides/persist-state/003.py !} | ||
``` | ||
|
||
```shell hl_lines="12-16 21-22" | ||
In [1]: post = Post(title="Events in Aggregates", body="Lorem ipsum dolor sit amet, consectetur adipiscing...") | ||
|
||
In [2]: post.to_dict() | ||
Out[2]: | ||
{'title': 'Events in Aggregates', | ||
'body': 'Lorem ipsum dolor sit amet, consectetur adipiscing...', | ||
'published': False, | ||
'id': 'a9ea7763-c5b2-4c8c-9c97-43ba890517d0'} | ||
|
||
In [3]: post.publish() | ||
|
||
In [4]: post._events | ||
Out[4]: [<PostPublished: PostPublished object ({ | ||
'post_id': 'a9ea7763-c5b2-4c8c-9c97-43ba890517d0', | ||
'body': 'Lorem ipsum dolor sit amet, consectetur adipiscing...' | ||
})>] | ||
|
||
In [5]: domain.repository_for(Post).add(post) | ||
Out[5]: <Post: Post object (id: a9ea7763-c5b2-4c8c-9c97-43ba890517d0)> | ||
|
||
In [6]: post._events | ||
Out[6]: [] | ||
``` | ||
|
||
## Updates | ||
|
||
Recall that Protean repositories behave like a `set` collection. Updating is | ||
as simple as mutating an aggregate and persisting it with `add` again. | ||
|
||
```shell hl_lines="15 20 22 25 27" | ||
In [1]: post = Post( | ||
...: id="1", | ||
...: title="Events in Aggregates", | ||
...: body="Lorem ipsum dolor sit amet, consectetur adipiscing..." | ||
...: ) | ||
|
||
In [2]: domain.repository_for(Post).add(post) | ||
Out[2]: <Post: Post object (id: 1)> | ||
|
||
In [3]: domain.repository_for(Post).get("1") | ||
Out[3]: <Post: Post object (id: 1)> | ||
|
||
In [4]: domain.repository_for(Post).get("1").to_dict() | ||
Out[4]: | ||
{'title': 'Events in Aggregates', | ||
'body': 'Lorem ipsum dolor sit amet, consectetur adipiscing...', | ||
'published': False, | ||
'id': '1'} | ||
|
||
In [5]: post.title = "(Updated Title) Events in Entities" | ||
|
||
In [6]: domain.repository_for(Post).add(post) | ||
Out[6]: <Post: Post object (id: 1)> | ||
|
||
In [7]: domain.repository_for(Post).get("1").to_dict() | ||
Out[7]: | ||
{'title': '(Updated Title) Events in Entities', | ||
'body': 'Lorem ipsum dolor sit amet, consectetur adipiscing...', | ||
'published': False, | ||
'id': '1'} | ||
``` |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Unit of Work | ||
|
||
|
||
Never enclose updates to multiple aggregates in a single unit of work. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from protean import Domain | ||
from protean.fields import String | ||
|
||
domain = Domain(__file__, load_toml=False) | ||
|
||
|
||
@domain.aggregate | ||
class Person: | ||
name = String(required=True, max_length=50) | ||
email = String(required=True, max_length=254) | ||
|
||
|
||
domain.init(traverse=False) | ||
with domain.domain_context(): | ||
person = Person( | ||
id="1", # (1) | ||
name="John Doe", | ||
email="john.doe@localhost", | ||
) | ||
domain.repository_for(Person).add(person) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
from protean import Domain | ||
from protean.fields import Float, HasMany, String, Text | ||
|
||
domain = Domain(__file__, load_toml=False) | ||
|
||
|
||
@domain.aggregate | ||
class Post: | ||
title = String(required=True, max_length=100) | ||
body = Text() | ||
comments = HasMany("Comment") | ||
|
||
|
||
@domain.entity(part_of=Post) | ||
class Comment: | ||
content = String(required=True, max_length=50) | ||
rating = Float(max_value=5) | ||
|
||
|
||
domain.init(traverse=False) | ||
with domain.domain_context(): | ||
post = Post( | ||
id="1", | ||
title="A Great Post", | ||
body="This is the body of a great post", | ||
comments=[ | ||
Comment(id="1", content="Amazing!", rating=5.0), | ||
Comment(id="2", content="Great!", rating=4.5), | ||
], | ||
) | ||
|
||
# This persists one `Post` record and two `Comment` records | ||
domain.repository_for(Post).add(post) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from protean import Domain | ||
from protean.fields import Boolean, Identifier, String, Text | ||
|
||
domain = Domain(__file__, load_toml=False) | ||
|
||
|
||
@domain.aggregate | ||
class Post: | ||
title = String(required=True, max_length=100) | ||
body = Text() | ||
published = Boolean(default=False) | ||
|
||
def publish(self): | ||
self.published = True | ||
self.raise_(PostPublished(post_id=self.id, body=self.body)) | ||
|
||
|
||
@domain.event(part_of=Post) | ||
class PostPublished: | ||
post_id = Identifier(required=True) | ||
body = Text() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters