8. Domain Logic happens in native Python
Date: 2024-04-24
Domain, Storage, Protocols.
From Subclassing, Composition, Python, and You, by Hynek Schlawack, and Learning the ropes: understanding Python generics, by David Seddon.
Something common to every successful project I've worked in was a rich domain model that informed the current design of the codebase and its future changes.
A program is not its source code
When it comes to the implementation, I've decided to avoid any third party library to express anything related to the
payments domain. No money
library, for example. To make that more visible, imports from third party libraries are
module imports.
When it comes to the architecture, I've separated the domain
folder from a storage
folder where everything related
to database I/O is defined, and from a protocols
folder where the specifics of the type system are isolated from how
everything gets implemented. Dataclasses are hence first class citizens,
and types express intent without mixing in with how that intent is implemented.
My first realization when I began with this approach was that
acquiring
stopped being a django-specific library. It can support
SQLAlchemy and any other ORM out there. To the best of my knowledge, acquiring
is the first Python library able to
support Flask, Django and FastAPI out of the box. Any Web development framework, really.
The second one was that separating the domain logic (/domain
) from the intent expressed by types (/protocols
) is key
to building a Protocol library, rather than an API. In other words, each component of the implementation can be replaced
by a different one, as long as it respects the boundaries imposed by the interfaces.
This is the point where acquiring
's design and Cosmic Python's diverge. Persistence
ignorance separates domain from storage. But duck typing allows me to build a Protocol.