Files
temp/docs/modules/domain-base-model.md
T

149 lines
4.2 KiB
Markdown

# Domain Base Model
**Package:** `cleveragents.domain.models.base`
**Introduced:** v3.8.0 (issue #1941)
The `DomainBaseModel` is a shared Pydantic base class that centralises the
common `model_config` previously duplicated across 14 domain model classes.
All standard domain-layer Pydantic models should inherit from
`DomainBaseModel` instead of `pydantic.BaseModel` directly.
---
## Purpose
Before `DomainBaseModel` was introduced, every domain model file contained
an identical `model_config = ConfigDict(...)` block. This created a
maintenance burden: any change to the shared configuration had to be
propagated manually to all 14 files, and it was easy for individual models
to drift out of sync.
`DomainBaseModel` solves this by defining the configuration in exactly one
place. Future changes to the shared config automatically propagate to every
consumer.
---
## Usage
```python
from cleveragents.domain.models.base import DomainBaseModel
class MyDomainModel(DomainBaseModel):
name: str
value: int
```
That's all that's required. The model inherits all five configuration
options described below.
---
## Configuration Options
`DomainBaseModel` sets the following `model_config` options:
| Option | Value | Effect |
|--------|-------|--------|
| `str_strip_whitespace` | `True` | Leading/trailing whitespace is stripped from all `str` fields on assignment and validation |
| `validate_assignment` | `True` | Field assignments after construction are validated just like constructor arguments |
| `arbitrary_types_allowed` | `False` | All field types must be Pydantic-compatible; arbitrary Python objects are not permitted |
| `populate_by_name` | `True` | Models can be constructed using either the Python field name or the JSON alias |
| `use_enum_values` | `True` | Enum fields are stored and serialised as their underlying primitive values rather than as enum instances |
---
## Example: Whitespace Stripping
```python
from cleveragents.domain.models.base import DomainBaseModel
class ProjectName(DomainBaseModel):
name: str
p = ProjectName(name=" my-project ")
assert p.name == "my-project" # whitespace stripped automatically
```
---
## Example: Assignment Validation
```python
from cleveragents.domain.models.base import DomainBaseModel
from pydantic import field_validator
class BoundedValue(DomainBaseModel):
count: int
@field_validator("count")
@classmethod
def must_be_positive(cls, v: int) -> int:
if v < 0:
raise ValueError("count must be non-negative")
return v
bv = BoundedValue(count=5)
bv.count = -1 # raises ValidationError — validate_assignment=True
```
---
## Migration Guide
If you are adding a new domain model, inherit from `DomainBaseModel`:
```python
# Before
from pydantic import BaseModel, ConfigDict
class MyModel(BaseModel):
model_config = ConfigDict(
str_strip_whitespace=True,
validate_assignment=True,
arbitrary_types_allowed=False,
populate_by_name=True,
use_enum_values=True,
)
name: str
# After
from cleveragents.domain.models.base import DomainBaseModel
class MyModel(DomainBaseModel):
name: str
```
If you need to **override** one of the shared options for a specific model,
you can still do so by declaring `model_config` on the subclass — Pydantic
merges configs with the subclass taking precedence:
```python
from pydantic import ConfigDict
from cleveragents.domain.models.base import DomainBaseModel
class SpecialModel(DomainBaseModel):
# Allow arbitrary types for this specific model only
model_config = ConfigDict(arbitrary_types_allowed=True)
data: object
```
---
## Scope
`DomainBaseModel` is intended for **domain-layer** models only. Do not use
it for:
- Infrastructure ORM models (use SQLAlchemy `Base` instead)
- CLI output models (use plain `pydantic.BaseModel` or dataclasses)
- Configuration models (use `pydantic_settings.BaseSettings`)
---
## Related Documentation
- [Architecture Overview](../architecture.md) — domain layer description
- [ADR-004 Data Validation](../adr/ADR-004-data-validation.md) — validation strategy
- [ADR-001 Layered Architecture](../adr/ADR-001-layered-architecture.md) — layer boundaries