Typed values at the boundaries¶
A function that crosses a boundary returns a typed structure and not a bare dict.
What to read¶
Pointis a frozen@dataclass, the shape of a boundary that needs no validation.originreturns it.NewUseris a pydanticBaseModelof the domain, the shape of a boundary whose fields are validated.normalizetakes one and returns one.
The rules report the two shapes of a bare dict at a boundary:
def origin() -> dict: # a `dict` return annotation
return {"x": 0, "y": 0} # a returned `{...}` literal
What to adapt¶
PointandNewUserare examples. Name the structure for each boundary of your code.- Use a dataclass when no validation is needed and a
BaseModelwhere it is. - The input and the output of an API are request and response schemas, with their own names and
settings: see the topic
api-schemas.
References¶
code/app/core/models.py:10-20¶
a boundary that needs no validation: a dataclass that a function returns
@dataclass(frozen=True)
class Point:
"""A point of the plane: a dataclass, since nothing in it needs validation."""
x: int
y: int
def origin() -> Point:
"""Return the origin of the plane."""
return Point(x=0, y=0)
code/app/core/models.py:23-32¶
a boundary whose fields are validated: a pydantic model of the domain
class NewUser(BaseModel):
"""A user before it is stored: a model of the domain whose fields are validated."""
email: str
display_name: str
def normalize(user: NewUser) -> NewUser:
"""Return the user with the email in lower case and the name without surrounding spaces."""
return NewUser(email=user.email.lower(), display_name=user.display_name.strip())
Cited by¶
no-dict-alias(BTDM001)no-dict-call-return(BTDM002)no-dict-literal-return(BTDM003)no-dict-return-annotation(BTDM004)- CES-79