Adoption and migration¶
Talea can be introduced at one boundary without converting an entire codebase. Start with a request, event, or third-party payload whose strictness and failure semantics are explicit.
From dataclasses¶
Keep dataclasses when their standard mutable or immutable record semantics are
the intended domain representation. Add Contract(DomainType) when the same
object needs strict current-state validation, Mapping/JSON construction,
structured errors, detached output, resource policy, introspection, or schema
projection.
from dataclasses import dataclass
from talea import Contract
@dataclass(slots=True)
class Customer:
customer_id: int
name: str
customers = Contract(Customer)
customer = customers.from_json('{"customer_id":1,"name":"Ada"}')
The dataclass stays unchanged and its constructor/default/post-init lifecycle
remains authoritative. Choose a Spec instead when Talea should own immutable
construction, transforms, checks, serializers, or derived PATCH declarations.
See Standard-library dataclasses for the complete boundary.
From TypedDict¶
Use Contract[Payload](Payload) when dictionary identity is useful. Use a Spec
when nominal type identity, attributes, immutability, methods, inheritance, or
field hooks belong in the domain.
From Pydantic¶
| Pydantic concept | Nearest Talea surface |
|---|---|
BaseModel |
Spec |
TypeAdapter |
Contract |
model_dump() |
to_dict() or Contract.to_python() |
model_validate_json() |
from_json() |
| field validator | transform for conversion; check for assertion |
create_model() |
create_spec() |
The mapping is conceptual, not drop-in compatibility. Talea is strict by default, immutable, Python 3.14-only, and does not provide settings, ORM attribute extraction, callable validation, or Pydantic's plugin ecosystem. Pydantic's default coercion and Talea's external conversion must be reviewed field by field.
From manually written validators¶
Begin with Contract(annotation) around the value currently checked by custom
code. Preserve application-specific business rules as check callbacks only
when they belong to the data contract. Keep authorization, persistence, and
I/O outside Talea.
Before adoption, review comparison, known limitations, and security architecture.
Introduce one boundary safely¶
Choose one request or event whose representation is already documented. Write down the accepted Python form, JSON form, aliases, failure response, maximum payload shape, and output contract before replacing code. Then:
- declare the Spec or Contract without changing application business rules;
- add accepted and rejected fixtures from production-shaped payloads;
- compare old and new error/serialization semantics explicitly;
- select a
ResourcePolicyfrom the actual shape and upstream limits; - project input/output schemas and review them with the framework owner;
- measure the exact operation under expected valid and invalid workloads;
- cut over at the adapter seam, not throughout the domain at once.
This keeps rollback local. It also reveals whether the system truly wants strictness or was relying on implicit conversion such as UUID, boolean, or numeric strings.
Preserve intentional coercion¶
Do not translate a coercive model mechanically and assume strict input is
compatible. Inventory every accepted representation. If a Python Mapping
boundary intentionally accepts one alternate form, declare a focused
@transform and test it. If broad parsing is a product requirement across many
types, Pydantic may remain the more coherent choice.
Transforms are trusted callback code and can make the input JSON Schema domain unknowable. That tradeoff should be visible in review rather than hidden behind a migration helper.
Keep boundary and domain models distinct when needed¶
An API request can contain aliases, write-only credentials, and optional PATCH presence that do not belong in a stored domain object. A response may need an allow-listed shape that deliberately omits request secrets. Reusing one type everywhere saves declarations but can weaken ownership.
The production service example separates request, stored, and response contracts. The PATCH guide derives partial input from a complete source while preserving whole-object checks on application.
Adoption stop conditions¶
Stop or narrow the migration if the project requires Python below 3.14, depends on unavailable framework integrations, needs a settings/ORM ecosystem Talea does not provide, or produces mostly msgspec-native codec workloads already served well. Also stop if the new declaration is less clear than a tiny manual validator and none of Contract's additional boundaries are useful.
Adoption during the evolving 0.x series should pin versions, review release notes, keep boundary tests in the application repository, and assign an owner for compatibility decisions. The absence of required runtime dependencies reduces deployment surface; it does not remove the normal operational cost of adopting a young library.