forked from HAL9000/cleveragents-core
149 lines
4.2 KiB
Markdown
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
|