Classic Domain
Основывается на книге Эрика Эванса "Domain Driven Design: tackling complexity in the heart of software".
Предоставляет базовый примитив для описания языка предметной области - Criteria (Критерий).
Критерий нужен для описания выборки объектов. Есть два пути использования - через определение класса, и через декоратор.
Установка
pip install classic-domain
Критерии-классы
Представим себе, что у нас класс, олицетворяющий понятие из предметной области - задачу:
Также, представим себе, что в предметной области задача называется открытой, если нет времени завершения и просроченной, если больше работа не завершена больше какого-то количества дней.
Также представим, что нам необходимо получать из некоторого хранилища задачи, соответствующие этим критериям.
Задачу можно описать обычным датаклассом, а состояния задач - критериями. У критериев можно определить метод is_satisfied_by, которые будут определять, подходит ли объект под этот критерий или нет:
from datetime import datetime
from dataclasses import dataclass
from typing import Optional
from classic.domain import Criteria
@dataclass
class Task:
created_at: datetime
finished_at: Optional[datetime] = None
@dataclass
class Open(Criteria[Task]):
"""Критерий для открытых задач"""
def is_satisfied_by(self, candidate: Task) -> bool:
return candidate.finished_at is None
@dataclass
class Obsolete(Criteria[Task]):
"""Критерий для просроченных задач"""
days_to_work: int
def is_satisfied_by(self, candidate: Task) -> bool:
return (
candidate.finished_at is not None and
(candidate.created_at - candidate.finished_at).days > self.days_to_work
)
Затем описанные критерии можно использовать в разных сценариях.
Прямое использование
from datetime import datetime
some_task = Task(created_at=datetime(2025, 1, 1))
is_open = Open()
is_open.is_satisfied_by(some_task)
# True
Булева алгебра
Критерии можно связать логическими условиями:
from datetime import datetime
some_task = Task(created_at=datetime(2025, 1, 1))
some_crit = Open() | Obsolete(3)
some_crit.is_satisfied_by(some_task)
# True
Фильтрация
У критерия метод __call__ определен как алиас для is_satisfied_by.
Это придает лаконичности при использовании критерия для фильтрации списка:
from datetime import datetime
tasks = [
Task(created_at=datetime(2025, 1, 1)),
Task(created_at=datetime(2025, 1, 1),
finished_at=datetime(2025, 1, 5)),
Task(created_at=datetime(2025, 1, 1),
finished_at=datetime(2025, 1, 2)),
]
some_crit = Open() | Obsolete(3)
filtered_tasks = filter(some_crit, tasks)
# True
Критерии-методы
Критерии можно определить как методы у класса (например, у самой сущности),
используя декоратор criteria:
from datetime import datetime
from dataclasses import dataclass
from typing import Optional
from classic.domain import criteria
@dataclass
class Task:
created_at: datetime
finished_at: Optional[datetime] = None
@criteria
def open(self) -> bool:
return self.finished_at is None
@criteria
def obsolete(self, days_to_work: int) -> bool:
return (
self.finished_at is not None and
(self.created_at - self.finished_at).days > days_to_work
)
У таких критериев есть две группы сценариев использования, использование на инстансах, и использование на классах.
Далее будут примеры с инстансами, аналогичные примерам выше.
Прямое использование
from datetime import datetime
some_task = Task(created_at=datetime(2025, 1, 1))
some_task.open()
# True
Булева алгебра:
from datetime import datetime
some_task = Task(created_at=datetime(2025, 1, 1))
some_task.open() or some_task.obsolete()
# True
Обратите внимание, что булева алгебра используется с and, 'or' и not.
Критерии-методы с классами
Критериев-методы можно использовать без инстансов, обращаясь к классам. При таком применении не произойдет проверки критерия. Полученный объект-критерий предоставляет собой отложенное условие:
from datetime import datetime
is_open = Task.open() # Отложенная проверка
some_task = Task(created_at=datetime(2025, 1, 1))
is_open.is_satisfied_by(some_task)
# True
Фильтрация
Отложенные критерии также можно использовать для фильтрации списка:
from datetime import datetime
some_crit = Task.open() | Task.obsolete()
tasks = [
Task(created_at=datetime(2025, 1, 1)),
Task(created_at=datetime(2025, 1, 1),
finished_at=datetime(2025, 1, 5)),
Task(created_at=datetime(2025, 1, 1),
finished_at=datetime(2025, 1, 2)),
]
filtered_tasks = filter(some_crit, tasks)
# True
Критерии-функции
Также можно использовать декоратор criteria на обычных функциях.
Все свойства таких критериев абсолютно аналогичны варианту с методами:
from datetime import datetime
from dataclasses import dataclass
from typing import Optional
from classic.domain import criteria
@dataclass
class Task:
created_at: datetime
finished_at: Optional[datetime] = None
@criteria
def task_open(task: Task) -> bool:
return task.finished_at is None
@criteria
def task_is_obsolete(task: Task, days_to_work: int) -> bool:
return (
task.finished_at is not None and
(task.created_at - task.finished_at).days > days_to_work
)
Выборки в БД
Также, критерии можно использовать для построения запросов к БД.
Этот раздел будет заполнен позднее, по мере готовности classic-db-tools
Metadata
Release files for classic-domain 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| classic_domain-0.2.0.tar.gz | 9.5 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| classic_domain-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 17.7 kB
Release files / classic_domain-0.2.0.tar.gz
| Download URL | classic_domain-0.2.0.tar.gz |
|---|---|
| Size | 9.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8c490abc2f5c6605f3a334108f87d57ab79203408cb8ccf33d340a156fded984
|
|
BLAKE2b-256 checksum How to use checksums |
29896ad54b2a4d4cb2c76452e99d7782c88eb69cf56ae8bfd063bbe20be0432c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 9, 2026.
Transparency logRelease files / classic_domain-0.2.0-py3-none-any.whl
| Download URL | classic_domain-0.2.0-py3-none-any.whl |
|---|---|
| Size | 8.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
408785e9c8d4ce573417767e09797b75eaa6683b34554d33f67dcccf884942ea
|
|
BLAKE2b-256 checksum How to use checksums |
126329f0d523a6a286ab992fa99d20c4f7365dd63b499282f5635ce12e3fe40a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 9, 2026.
Transparency logSecurity for classic-domain 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for classic_domain-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 9, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.