Skip to content

Commit 056a1d6

Browse files
author
Вадим Козыревский
committed
Добавлена документация типов
1 parent 30e118c commit 056a1d6

13 files changed

Lines changed: 1528 additions & 0 deletions

File tree

docs/javascripts/navigation.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,17 @@
6868
{ title: 'Dependency Injection', url: 'di/', path: 'di' },
6969
// Request Handler
7070
{ title: 'Commands / Requests Handling', url: 'request_handler/', path: 'request_handler' },
71+
// Request / Response Types
72+
{ title: 'Request / Response Types', url: 'request_response_types/', path: 'request_response_types/index' },
73+
{ title: 'Pydantic', url: 'request_response_types/pydantic/', path: 'request_response_types/pydantic' },
74+
{ title: 'Dataclasses', url: 'request_response_types/dataclasses/', path: 'request_response_types/dataclasses' },
75+
{ title: 'Standard Classes', url: 'request_response_types/standard_classes/', path: 'request_response_types/standard_classes' },
76+
{ title: 'NamedTuple', url: 'request_response_types/namedtuple/', path: 'request_response_types/namedtuple' },
77+
{ title: 'Attrs', url: 'request_response_types/attrs/', path: 'request_response_types/attrs' },
78+
{ title: 'Msgspec', url: 'request_response_types/msgspec/', path: 'request_response_types/msgspec' },
79+
{ title: 'TypedDict', url: 'request_response_types/typeddict/', path: 'request_response_types/typeddict' },
80+
{ title: 'Mixed Usage', url: 'request_response_types/mixed_usage/', path: 'request_response_types/mixed_usage' },
81+
{ title: 'Best Practices', url: 'request_response_types/best_practices/', path: 'request_response_types/best_practices' },
7182
// Stream Handling
7283
{ title: 'Stream Handling', url: 'stream_handling/', path: 'stream_handling/index' },
7384
{ title: 'Stream Configuration', url: 'stream_handling/configuration/', path: 'stream_handling/configuration' },

docs/request_handler.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Request handlers process commands (write operations) and queries (read operation
1414
Before creating handlers, ensure you've configured [Bootstrap](bootstrap/index.md) and understand [Dependency Injection](di.md).
1515

1616
!!! tip "Related Topics"
17+
- [Request / Response Types](request_response_types/index.md) — Different types for requests and responses (Pydantic, Dataclasses, attrs, etc.)
1718
- [Stream Handling](stream_handling/index.md) — For incremental processing
1819
- [Chain of Responsibility](chain_of_responsibility/index.md) — For sequential handler chains
1920
- [Saga Pattern](saga/index.md) — For distributed transactions with compensation
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# Attrs Request/Response
2+
3+
**Advanced features** - Uses the `attrs` library for more features than standard dataclasses.
4+
5+
## Best For
6+
7+
- More features than standard dataclasses
8+
- Functional-style codebases
9+
- When you need more control than dataclasses
10+
- Complex validation scenarios
11+
12+
!!! info "Installation"
13+
```bash
14+
pip install attrs
15+
```
16+
17+
## Usage
18+
19+
```python
20+
import cqrs
21+
import attrs
22+
from typing import Self
23+
24+
@attrs.define
25+
class AttrsRequest(cqrs.IRequest):
26+
"""Request using attrs."""
27+
user_id: str = attrs.field(validator=attrs.validators.instance_of(str))
28+
action: str = attrs.field(validator=attrs.validators.instance_of(str))
29+
30+
def to_dict(self) -> dict:
31+
"""Convert to dictionary."""
32+
return attrs.asdict(self)
33+
34+
@classmethod
35+
def from_dict(cls, **kwargs) -> Self:
36+
"""Create from dictionary."""
37+
return cls(**kwargs)
38+
39+
@attrs.define(frozen=True)
40+
class AttrsResponse(cqrs.IResponse):
41+
"""Immutable response using attrs."""
42+
result: str = attrs.field()
43+
status: str = attrs.field()
44+
45+
def to_dict(self) -> dict:
46+
"""Convert to dictionary."""
47+
return attrs.asdict(self)
48+
49+
@classmethod
50+
def from_dict(cls, **kwargs) -> Self:
51+
"""Create from dictionary."""
52+
return cls(**kwargs)
53+
54+
class AttrsHandler(cqrs.RequestHandler[AttrsRequest, AttrsResponse]):
55+
@property
56+
def events(self) -> list[cqrs.IEvent]:
57+
return []
58+
59+
async def handle(self, request: AttrsRequest) -> AttrsResponse:
60+
return AttrsResponse(
61+
result=f"Processed {request.action}",
62+
status="success"
63+
)
64+
```
65+
66+
## Validation
67+
68+
Attrs provides powerful validation capabilities:
69+
70+
```python
71+
import attrs
72+
from typing import Self
73+
74+
@attrs.define
75+
class ValidatedRequest(cqrs.IRequest):
76+
email: str = attrs.field(
77+
validator=attrs.validators.matches_re(r'^[\w\.-]+@[\w\.-]+\.\w+$')
78+
)
79+
age: int = attrs.field(
80+
validator=attrs.validators.and_(
81+
attrs.validators.ge(0),
82+
attrs.validators.le(120)
83+
)
84+
)
85+
86+
def to_dict(self) -> dict:
87+
return attrs.asdict(self)
88+
89+
@classmethod
90+
def from_dict(cls, **kwargs) -> Self:
91+
return cls(**kwargs)
92+
```
93+
94+
## Custom Converters
95+
96+
You can use converters for automatic type conversion:
97+
98+
```python
99+
@attrs.define
100+
class ConvertedRequest(cqrs.IRequest):
101+
count: int = attrs.field(converter=int)
102+
tags: list[str] = attrs.field(
103+
converter=lambda x: x.split(",") if isinstance(x, str) else x
104+
)
105+
106+
def to_dict(self) -> dict:
107+
return attrs.asdict(self)
108+
109+
@classmethod
110+
def from_dict(cls, **kwargs) -> Self:
111+
return cls(**kwargs)
112+
```
113+
114+
## Factory Functions
115+
116+
Attrs supports factory functions for default values:
117+
118+
```python
119+
@attrs.define
120+
class RequestWithDefaults(cqrs.IRequest):
121+
user_id: str
122+
metadata: dict = attrs.field(factory=dict)
123+
tags: list[str] = attrs.field(factory=list)
124+
125+
def to_dict(self) -> dict:
126+
return attrs.asdict(self)
127+
128+
@classmethod
129+
def from_dict(cls, **kwargs) -> Self:
130+
return cls(**kwargs)
131+
```
132+
133+
## Immutable Classes
134+
135+
Use `frozen=True` for immutability:
136+
137+
```python
138+
@attrs.define(frozen=True)
139+
class ImmutableRequest(cqrs.IRequest):
140+
user_id: str
141+
action: str
142+
143+
def to_dict(self) -> dict:
144+
return attrs.asdict(self)
145+
146+
@classmethod
147+
def from_dict(cls, **kwargs) -> Self:
148+
return cls(**kwargs)
149+
```
150+
151+
## See Also
152+
153+
- [Dataclasses](dataclasses.md) - Standard library alternative
154+
- [Request Handlers](../request_handler.md) - Learn about handler implementation

0 commit comments

Comments
 (0)