Clean Architecture - adaptive-learning/flocs GitHub Wiki
This page collects best practices for creating clean architecture.
Non-functional requirements
The main non-functional requirements for our project are:
- easy to understand (and pleasure to read the code)
- easy to extend (and pleasure to write the code)
- easy to test (and really tested)
- easy to refactor (without fear of breaking things)
- robust (not brittle) and with interpretable behavior
Component design
Project components (= top-level packages, Django "apps") should follow this architecture:
diagrams/clean-architecture.png
Sub-package | Description | Interface | Size |
---|---|---|---|
views |
web controllers | http request, http response | as thin as possible |
management |
console controllers | command line arguments, console output | as thin as possible |
services |
use cases (procedural glue) | arguments, DB, file system | as thin as possible |
core |
algorithms (data transformations) | pure functions | the more the better |
models |
entities and their managers | method arguments, DB | as thin as possible |
Views
TBA ...
- single responsibility: parse the request, call a correct service, create a response from the result
- no other logic
- no side-effects (e.g. if it's possible, they should not write to DB, this should happen only in services)
- from its code (and docstrings) it needs to be immediately clear how to use it for communication from the client (what it expects, how the response looks like)
Management
TBA
Services
Services represent use-cases (TBA: good examples). They include input and output (usually reading from and writing to DB, but they may also e.g. generate CSV files etc.). They should delegate all other logic besides handling input and output to pure functions in the core.
Core
You should strive to put as much functionality as possible into pure functions (in the core sub-package). Pure functions depend only on their input parameters and have no side-effects. The advantage of pure functions is that they are easy to understand, easy to test and it's also easy to create new functionality by combining pure functions.
Models
For simplicity, we have one sub-package for all data models, both persistent and non-persistent (and also possibly some managers for persistent entities). Entities are represented by a simple class which should only represent and possibly persist the information. Nothing else. Domain logic is expressed in services and core pure functions, which both work with entities. For core functions to be pure, of course, they must not use any persistent capability that the entities might have.