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.

References