Skip to content

Commit fb4f8cd

Browse files
authored
Merge pull request #194 from allmonday/feature/refactor-doc
Feature/refactor doc
2 parents bbb87f3 + 1b0a15f commit fb4f8cd

File tree

5 files changed

+454
-392
lines changed

5 files changed

+454
-392
lines changed

README.md

Lines changed: 25 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,15 @@ Starting from pydantic-resolve v1.11.0, both pydantic v1 and v2 are supported.
106106
- **Demo**: https://github.com/allmonday/pydantic-resolve-demo
107107
- **Composition-Oriented Pattern**: https://github.com/allmonday/composition-oriented-development-pattern
108108

109-
## Quick Start
109+
## 3 Steps to construct complex data
110110

111-
Building complex data structures requires only 3 systematic steps, let's take Agile's Story for example.
111+
Let's take Agile's Story for example.
112112

113113
### 1. Define Domain Models
114114

115-
Establish entity relationships as foundational data models (stable, serves as architectural blueprint)
115+
Establish entity relationships as foundational data models
116+
117+
(which is stable, serves as architectural blueprint)
116118

117119
<img width="630px" alt="image" src="https://github.com/user-attachments/assets/2656f72e-1af5-467a-96f9-cab95760b720" />
118120

@@ -158,7 +160,11 @@ DataLoader implementations support flexible data sources, from database queries
158160

159161
### 2. Compose Business Models
160162

161-
Based on a specific business logic, create domain-specific data structures through selective schemas and relationship dataloader (stable, reusable across use cases)
163+
Based on a our business logic, create domain-specific data structures through selective schemas and relationship dataloader
164+
165+
We need to extend `tasks`, `assignee` and `reporter` for `Story`, extend `user` for `Task`
166+
167+
Extending new fields is dynamic, all based on business requirement, but the relationships / loader are restricted by the definition from step 1.
162168

163169
<img width="630px" alt="image" src="https://github.com/user-attachments/assets/ffc74e60-0670-475c-85ab-cb0d03460813" />
164170

@@ -199,18 +205,20 @@ class Story(BaseModel):
199205

200206
```
201207

202-
> Once business models are validated, consider optimizing with specialized queries to replace DataLoader for enhanced performance.
208+
> Once this combination is stable, you can consider optimizing with specialized queries to replace DataLoader for enhanced performance, eg ORM's join relationship
203209
204210
### 3. Implement View-Layer Transformations
205211

206-
Apply presentation-specific modifications and data aggregations (flexible, context-dependent)
212+
Dataset from data-persistent layer can not meet all requirements, we always need some extra computed fields or adjust the data structure.
207213

208-
Leverage post_field methods for ancestor data access, node transfers, and in-place transformations.
214+
post method could read fields from ancestor, collect fields from descendants or modify the data fetched by resolve method.
209215

210216
#### Case 1: Aggregate or collect items
211217

212218
<img width="630px" alt="image" src="https://github.com/user-attachments/assets/2e3b1345-9e5e-489b-a81d-dc220b9d6334" />
213219

220+
`__pydantic_resolve_collect__` can collect fields from current node and then send them to ancestor node who declared `related_users`.
221+
214222
```python
215223
from pydantic_resolve import Loader, Collector
216224

@@ -244,6 +252,8 @@ class Story(BaseStory):
244252

245253
<img width="630px" alt="image" src="https://github.com/user-attachments/assets/fd5897d6-1c6a-49ec-aab0-495070054b83" />
246254

255+
post methods are executed after all resolve_methods are resolved, so we can use it to calculate extra fields.
256+
247257
```python
248258
class Story(BaseStory):
249259
tasks: list[Task] = []
@@ -266,6 +276,12 @@ class Story(BaseStory):
266276

267277
### Case 3: Propagate ancestor data through ancestor_context
268278

279+
`__pydantic_resolve_expose__` could expose specific fields from current node to it's descendant.
280+
281+
alias_names should be global unique inside root node.
282+
283+
descendant nodes could read the value with `ancestor_context[alias_name]`.
284+
269285
```python
270286
from pydantic_resolve import Loader
271287

@@ -294,7 +310,7 @@ class Story(BaseStory):
294310
return loader.load(self.report_to)
295311
```
296312

297-
### 4. Execute Resolution
313+
### 4. Execute Resolver().resolve()
298314

299315
```python
300316
from pydantic_resolve import Resolver
@@ -303,38 +319,8 @@ stories = [Story(**s) for s in await query_stories()]
303319
data = await Resolver().resolve(stories)
304320
```
305321

306-
Complete!
307-
308-
## Technical Architecture
309-
310-
The framework significantly reduces complexity in data composition by maintaining alignment with entity-relationship models, resulting in enhanced maintainability.
311-
312-
> Utilizing an ER-oriented modeling approach delivers 3-5x development efficiency gains and 50%+ code reduction.
313-
314-
Leveraging pydantic's capabilities, it enables GraphQL-like hierarchical data structures while providing flexible business logic integration during data resolution.
315-
316-
Seamlessly integrates with FastAPI to construct frontend-optimized data structures and generate TypeScript SDKs for type-safe client integration.
317-
318-
The core architecture provides `resolve` and `post` method hooks for pydantic and dataclass objects:
319-
320-
- `resolve`: Handles data fetching operations
321-
- `post`: Executes post-processing transformations
322-
323-
This implements a recursive resolution pipeline that completes when all descendant nodes are processed.
324-
325-
![](docs/images/life-cycle.png)
326-
327-
Consider the Sprint, Story, and Task relationship hierarchy:
328-
329-
<img src="docs/images/real-sample.png" style="width: 600px"/>
330-
331-
Upon object instantiation with defined methods, pydantic-resolve traverses the data graph, executes resolution methods, and produces the complete data structure.
332-
333-
DataLoader integration eliminates N+1 query problems inherent in multi-level data fetching, optimizing performance characteristics.
334-
335-
DataLoader architecture enables modular class composition and reusability across different contexts.
322+
`query_stories()` returns `BaseStory` list, after we transformed it into `Story`, resolve and post fields are initialized as default value, after `Resolver().resolve()` finished, all these fields will be resolved and post-processed to what we expected.
336323

337-
Additionally, the framework provides expose and collector mechanisms for sophisticated cross-layer data processing patterns.
338324

339325
## Testing and Coverage
340326

0 commit comments

Comments
 (0)