|  | @@ -1,77 +1,117 @@
 | 
	
		
			
				|  |  |  # clean-python
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -Introduction
 | 
	
		
			
				|  |  | +``clean-python`` contains abstractions for *clean architecture* in Python
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -Usage, etc.
 | 
	
		
			
				|  |  | +using asyncio. It is independent of frameworks.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -## Installation
 | 
	
		
			
				|  |  | +The terminology used is consistently derived from the "Big Blue Book" (Domain Driven Design by E. Evans, 2004). Software consists of one or more modules, each having four layers: presentation, application, domain, and infrastructure. Each layer has its own responsibilities, in short:
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -We can be installed with:
 | 
	
		
			
				|  |  | +- presentation: show information to the user and interpret the user's commands.
 | 
	
		
			
				|  |  | +- application: implement use cases that direct the domain objects.
 | 
	
		
			
				|  |  | +- domain: all domain concepts and rules; this layer is the heart of the software.
 | 
	
		
			
				|  |  | +- infrastructure: generic capabilities that support the higher layers
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    $ pip install clean-python
 | 
	
		
			
				|  |  | +A big inspiration for this was the ``easy`` typescript framework by S. Hoogendoorn and others
 | 
	
		
			
				|  |  | +(https://github.com/thisisagile/easy).
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +## Motivation
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +The main goals of using layered architecture is isolating the domain-specific concepts from
 | 
	
		
			
				|  |  | +other functions related only to software technology. In this way:
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +- The knowledge embedded in the domain is distilled and can more easily be 
 | 
	
		
			
				|  |  | +  understood and changed.
 | 
	
		
			
				|  |  | +- Developers are able to quickly grasp the code base because it uses
 | 
	
		
			
				|  |  | +  a consistent structure and naming system.
 | 
	
		
			
				|  |  | +- Depenencies are reduced resulting in a higher maintainability.
 | 
	
		
			
				|  |  | +- Unittests can be made more easily (increasing reliability).
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +## Dependencies
 | 
	
		
			
				|  |  | +Layers are loosly coupled with dependencies in only one direction: presentation > application > infrastructure > domain. In other words: the number of dependencies of the software's core business are as limited as possible.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +A module may only depend on another module though its infrastructure layer. See ``InternalGateway``.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -(TODO: after the first release has been made)
 | 
	
		
			
				|  |  | +This library was initially developed as a web backend using FastAPI. Its core dependencies are ``pydantic``, ``inject`` and ``asgiref``. Optional dependencies may be added in case for instance an ``SQLGateway`` is needed.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +## Core concepts
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -## Development installation of this project itself
 | 
	
		
			
				|  |  | +### Domain Layer
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -We use python's build-in "virtualenv" to get a nice isolated
 | 
	
		
			
				|  |  | -directory. You only need to run this once:
 | 
	
		
			
				|  |  | +The domain layer is where the model lives. The domain model is a set of concepts; the domain layer
 | 
	
		
			
				|  |  | +is the manifestation of that model. Concepts in the domain model must have a 1:1 representation in the
 | 
	
		
			
				|  |  | +code and vice versa. 
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    $ python3 -m venv .
 | 
	
		
			
				|  |  | +THe layer does not depend on all other layers. Interaction with the infrastructure layer may be done
 | 
	
		
			
				|  |  | +using dependency injection from the application layer. It is allowable to have runtime dependencies on the
 | 
	
		
			
				|  |  | +infrastructure layer to set for instance default ``Gateway`` implementations.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -A virtualenv puts its commands in the `bin` directory. So `bin/pip`,
 | 
	
		
			
				|  |  | -`bin/pytest`, etc. Set up the dependencies like this:
 | 
	
		
			
				|  |  | +There are 5 kinds of objects in this layer:
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    $ bin/pip install -e .[test]
 | 
	
		
			
				|  |  | +- *Entity*: Types that have an identity (all attributes of an instance may change- but the instance is still the same)
 | 
	
		
			
				|  |  | +  Entities have an ``id`` and default fields associated with state changes ()``created_at``, ``updated_at``). 
 | 
	
		
			
				|  |  | +- *ValueObject*: Types that have no identity (these are just complex values like a datetime).
 | 
	
		
			
				|  |  | +- *DomainService*: Important domain operations that aren't natural to model as objects. A service is stateless.
 | 
	
		
			
				|  |  | +- *Repository*: A repository is responsible for persistence (``add`` / ``get`` / ``filter``). This needs
 | 
	
		
			
				|  |  | +  a *Gateway* to interface with e.g. a database; an instance of a *Gateway* is typically injected into a
 | 
	
		
			
				|  |  | +  Repository from the application layer.
 | 
	
		
			
				|  |  | +- *DomainEvent*: A domain event may be emitted to signal a state change.
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +Associations between objects are hard. Especially many-to-many relations. We approach this by grouping objects
 | 
	
		
			
				|  |  | +into *aggregates*. An aggregate is a set of objects that change together / have the same lifecycle (e.g. delete together). One entity is the aggregate root; we call this the ``RootEntity``. A ``ChildEntity`` occurs only very
 | 
	
		
			
				|  |  | +rarely; mostly a nested object derive its identity from a ``RootEntity``.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -There will be a script you can run like this:
 | 
	
		
			
				|  |  | +All change and access goes through the repository of a ``RootEntity``. The ``RootEntity`` can be a complicated
 | 
	
		
			
				|  |  | +nested object; how to map this to an SQL database is the issue of the infrastructure layer.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    $ bin/run-clean-python
 | 
	
		
			
				|  |  | +### Infrastructure Layer
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -It runs the `main()` function in `[clean-python/scripts.py`,
 | 
	
		
			
				|  |  | -adjust that if necessary. The script is configured in
 | 
	
		
			
				|  |  | -`TODO, MISSING NOW` (see `entry_points`).
 | 
	
		
			
				|  |  | +An infrastructure layer primarily contains ``Gateway`` objects that interface with a single external resource.
 | 
	
		
			
				|  |  | +The ``Gateway`` implements persistence methods to support the domain and application layers. Much of the implementation will be in frameworks or other dependencies.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -In order to get nicely formatted python files without having to spend
 | 
	
		
			
				|  |  | -manual work on it, get [pre-commit](https://pre-commit.com/) and install
 | 
	
		
			
				|  |  | -it on this project:
 | 
	
		
			
				|  |  | +The methods of a ``Gateway`` may directly return a domain object, or return a dictionary with built-in types (``Json``).
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    $ pre-commit install
 | 
	
		
			
				|  |  | +Other gateway examples are: email sending and logstash logging.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -Run the tests regularly with coverage:
 | 
	
		
			
				|  |  | +### Application layer
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    $ bin/pytest --cov
 | 
	
		
			
				|  |  | +The application layer defines the use cases of the application. Example use cases are `create_user` or `list_user_roles`. These methods have nothing to do with a REST API or command-line interface; this is
 | 
	
		
			
				|  |  | +the business of the presentation layer.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -The tests are also run automatically [on "github
 | 
	
		
			
				|  |  | -actions"](https://github.com/nens/clean-python/actions) for
 | 
	
		
			
				|  |  | -"main" and for pull requests. So don't just make a branch, but turn it into a
 | 
	
		
			
				|  |  | -pull request right away. On your pull request page, you also automatically get
 | 
	
		
			
				|  |  | -the feedback from the automated tests.
 | 
	
		
			
				|  |  | +In addition to directing the domain objects, an application layer method could trigger other behavior
 | 
	
		
			
				|  |  | +like logging or triggering other applications. At first, it may as well be just a single function call.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -If you need a new dependency (like `requests`), add it in
 | 
	
		
			
				|  |  | -`pyproject.toml` in `dependencies`. And update your local install with:
 | 
	
		
			
				|  |  | +This layer is kept thin. It directs domain objects, and possibly interacts with other systems 
 | 
	
		
			
				|  |  | +(for instance by sending a message through the infrastructure layer). The application layer should
 | 
	
		
			
				|  |  | +not contain fundamental domain rules.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    $ bin/pip install -e .[test]
 | 
	
		
			
				|  |  | +### Presentation Layer
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +The presentation layer shows information to the user and interprets the user's commands.
 | 
	
		
			
				|  |  | +Its main job is to get the application-layer use cases to be usable for an actual user.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -## Steps to do after generating with cookiecutter
 | 
	
		
			
				|  |  | +The currently only option in ``clean-python`` is a REST API using FastAPI.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -- Add a new project on <https://github.com/nens/> with the same name.  Think
 | 
	
		
			
				|  |  | -  about the visibility to ("public" / "private") and do not generate a
 | 
	
		
			
				|  |  | -  license or readme.
 | 
	
		
			
				|  |  | +## Modules
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  Note: "public" means "don't put customer data or sample data with real
 | 
	
		
			
				|  |  | -  persons' addresses on github"!
 | 
	
		
			
				|  |  | +The primary objective of compartimentalizing code into modules is to prevent cognitive overload.
 | 
	
		
			
				|  |  | +The modules divide the domain layer, everything else follows. There should be low coupling
 | 
	
		
			
				|  |  | +between modules and high cohesion whithin a module. Modules are first and foremost a conceptual
 | 
	
		
			
				|  |  | +structure.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -- Follow the steps you then see (from "git init" to "git push origin main")
 | 
	
		
			
				|  |  | -  and your code will be online.
 | 
	
		
			
				|  |  | +In Python, a module should be implemented with a single .py file or a folder of .py files (respectively
 | 
	
		
			
				|  |  | +called modules and packages).
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -- Go to
 | 
	
		
			
				|  |  | -  https://github.com/nens/>clean-python/settings/collaboration
 | 
	
		
			
				|  |  | -  and add the teams with write access (you might have to ask someone with
 | 
	
		
			
				|  |  | -  admin rights (like Reinout) to do it).
 | 
	
		
			
				|  |  | +Modules have a public API (presentation layer) and encapsulate their database. Only in this way
 | 
	
		
			
				|  |  | +the internal consistency can be guaranteed by the module's domain layer.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +Our current approach is to have 1 *aggregate* (whose root is implemented as a ``RootEntity``) per module.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +## Installation
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +``clean-python`` can be installed with:
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    $ pip install clean-python
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -- Update this readme.
 | 
	
		
			
				|  |  | +Optional dependencies can be added with:
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -- Remove this section as you've done it all :-)
 | 
	
		
			
				|  |  | +    $ pip install clean-python[sql,fastapi]
 |