Project demoing the Polylith architecture using the Django app system and the Python Polylith tool.
This project uses Poetry as the package manager. To install the tool on your local machine, run the command
curl -sSL https://install.python-poetry.org | python3 -After installing Poetry and downloading the repo, install the dependencies for the project and run the migrations. Then you're set to run the development server:
poetry install
poetry shell
python manage.py migrate
python manage.py runserverYou can run any command in this doc as an ad-hoc command in your root shell by prefixing it with poetry run. The rest of this doc assumes you're in an active poetry shell for brevity.
Since this is a Django variant of the Polylith architecture it uses most of the key terms from the Polylith overview docs.
To summarize the table below: libraries are used by components, which represent app entities through an interface module. Bases expose functionality of components publicly through endpoints. Projects assemble each of these into an artifact like a service.
Polylith concepts
Below are the Django-specific details of each term.
Component
Each component lives in a separate directory in the components folder and namespace.
The interface.py module is a set of functions using native data structures (e.g. lists and maps) for inputs and outputs. Each function in the interface module "passes-through" to an equivalent function in core.py, which enables encapsulation and allows for any private implementation (like using the ORM).
- The constraint of native data structures for input/output is necessary both as a “protocol” between components and to ensure encapsulation. For example: if the
questionsmodule returnedQuestionORM objects, that would expose the implementation details of using the ORM, and allow callers to use methods on the object for functionality instead of those fromquestions.interface.
apps.py is the module for Django app configuration, types.py is for shared types within the app, and models.py is the standard module for data models.
▾ workspace
▾ components
▾ cpackard
▾ questions
▸ migrations
__init__.py
apps.py
core.py
interface.py
models.py
types.py
Base
Like components, each base lives in a separate directory in the bases folder and namespace.
Bases expose component interfaces through endpoint functions defined in the conventional Django views.py module, which have their routes defined in urls.py. They also hold the common top-level Django configuration modules like settings.py and wsgi.py.
▾ workspace
▾ bases
▾ cpackard
▾ api
__init__.py
asgi.py
manage.py
settings.py
urls.py
views.py
wsgi.py
Project
A project is the result of combining one base (or in rare cases several bases) with multiple components and libraries.
Unlike bases and components, projects have no src or tests directories because they contain no logic of their own. Instead, they have the pyproject.toml includes the components, bases, and required libs as dependencies for the final artifact.
▾ workspace
▾ projects
▾ monolith-api
__init__.py
pyproject.toml
poetry.lock
Development Project
The development folder is where you can put code that you write to experiment or try out features. Here, you add all dependencies and bricks. This will make it possible to have the entire code-base available in one and the same virtual environment.
In this folder, it is quite common that developers keep their scratch-style Python modules. It is perfectly fine to version control them.
▾ workspace
▾ development
__init__.py
cpackard.py
pyproject.toml
Tests
Tests are kept in a separate test folder in the top-level of the repo. Tests are organized by brick type and namespace:
▾ workspace
▾ test
▾ bases
▾ cpackard
▾ api
__init__.py
test_views.py
▾ components
▾ cpackard
▸ choices
▾ questions
__init__.py
test_core.py
You can create a new component using the project's command:
poetry poly create component --name questionsFor more info on other commands, see the Python Polylith docs.
Migrations can be created for any components by running the makemigrations command for it from the root of the repo:
python manage.py makemigrations questionsThen apply the migrations as normal:
python manage.py migrateFrom the Django docs:
To refer to models defined in another application, you can explicitly specify a model with the full application label. For example, if the
Manufacturermodel above is defined in another application calledproduction, you’d need to use:
class Car(models.Model):
manufacturer = models.ForeignKey(
'production.Manufacturer',
on_delete=models.CASCADE,
)To run tests for the entire project, run this command from the root of the repo:
pytest -vBy default, this command will use the DJANGO_SETTINGS_MODULE = "cpackard.api.settings" attribute in the root-level pyproject.toml. You can override this by setting a DJANGO_SETTINGS_MODULE environment variable or by passing the --ds CLI flag like pytest --ds=cpackard.api.settings. See the pytest-django docs for more info.





