Models
When modeling the application logic through Domain Driven Design, you usually need the following object types:
-
Value object: Any domain object that is uniquely identified by the data it holds, so it has no conceptual identity. They should be treated as immutable. We can still have complex behaviour in value objects. In fact, it's common to support operations, for example, mathematical operators.
-
Entity: An object that is not defined by it's attributes, but rather by a thread of continuity and it's identity. Unlike values, they have identity equality. We can change their values, and they are still recognizably the same thing.
Entities¶
The Entity class is based on the
pydantic's BaseModel
to enforce that they have the id_
attribute of type
int
, str
or AnyHttpUrl
, used for comparison and hashing of entities.
They also have a private model_name
property with the name of the model.
If you use integer IDs (which is the default), you don't need to define the
id_
at object creation. When you add the entity to the repository, it will
populate it.
from repository_orm import Entity, load_repository
class Author(Entity):
first_name: str
repo = load_repository()
author = Author(first_name="Brandon")
# Add entities
repo.add(author)
repo.commit()
# Retrieve entities by their ID
brandon = repo.get(0, Author)
assert brandon == author # noqa
!!! warning "This will only work with int
ids! For the rest of the cases you
need to give the id_
yourself."
Merging entities¶
Entities have a merge
method that let's you update it's attributes with
the ones of another entity.
from repository_orm import Entity
class Author(Entity):
name: str
is_alive: bool = True
author = Author(name="Brandon")
# Imagine a complex process here that creates an updated version of the author object
new_author = Author(name="New name", is_alive=False)
author.merge(new_author)
assert author.name == "New name"
assert not author.is_alive
# Nevertheless the default values are not merged!
author.merge(Author(name="Brandon"))
assert not author.is_alive
# Unless specified by the user
author.merge(Author(name="Brandon", is_alive=True))
assert author.is_alive # noqa
For two entities to be mergeable, they need to belong from the same model and
have the same id_
. The previous example worked because by default the id_
is
-1
until the entity is added to the repository. If you want to check other
attribute to see if the objects are mergeable, probably that attribute should be
the id_
instead.
If you don't want to propagate some attributes when merging, add them to the
_skip_on_merge
configuration option of the model:
from datetime import datetime
from repository_orm import Entity
class Author(Entity):
name: str
is_alive: bool = True
birthday: datetime
_skip_on_merge = ["birthday"]
author = Author(name="Brandon", birthday=datetime(2020, 1, 1))
new_author = Author(name="Brandon", birthday=datetime(1900, 1, 1), is_alive=False)
author.merge(new_author)
assert author.birthday == datetime(2020, 1, 1)
assert not author.is_alive # noqa
Files¶
The File class is a special Entity model used to work with computer files.
It has useful attributes like:
path
.created_at
.updated_at
.owner
.group
.permissions
.
And methods:
basename
.dirname
.extension
.
Until Pydantic 1.9
is released, you need to store the content in the file
using the _content
attribute, to access the content, you can use content
directly.