Architecture¶
The configuration layer¶
The configuration layer (defined in app.config
) is used to
retrieve (and validate) configuration from environment variables, and
make it available to the rest of the code using a ContextVar
.
Public interface:
self
The resources layer¶
The resources layer (defined in app.resources
) is responsible for
instantiating clients to all the external services being used.
This includes:
databases
message queues
smtp providers
3rd party web APIs
Note that these clients will generally not be accessed directly by the business logic, but instead they’ll be used through the Repo and / or IO layers described below.
Depends on:
configuration
Public interface:
self
The repo layer¶
The repo layer (app.repo.*
) provides abstraction over permanent storage,
eg. databases and file storage, considered “internal” to the
application.
Sub-modules and the root level of sub-packages are considered public interface, unless their name starts with an underscore (_).
Depends on:
resources
,models
,config
Used by:
core
Public interface:
submodules
The I/O layer¶
The I/O layer (app.io.*
) provides abstraction over access to
3rd-party services, eg. SMTP servers or Web APIs.
Sub-modules and the root level of sub-packages are considered public
interface, unless their name starts with an underscore (_
).
Depends on:
resources
,models
,config
Used by:
core
Public interface:
submodules
The Core layer¶
The Core layer (app.core.*
) contains the actual business logic of the application.
It can be structured in any way that fits the domain, and provides the main library interface into the application, which upper layers can use to expose functionality through a number of interfaces.
Depends on:
repo
,io
,models
,config
Used by: interface layers (
webapp
,cli
, …)Public interface:
tree
(or anything that makes sense for the particular application)
The Web API layer¶
Provides a Web API interface (GraphQL, but RESTful can also be used, or any other type of web API format, even at the same time) into the application business logic.
The command-line interface¶
Typically used for administrative commands (eg. creating the first admin user on a production server).
Core functions are typically invoked as a special “superuser”, which is not tied to an actual user in the database, and provide extra privileges not normally accessible to the web application users.
Async workers¶
Async workers are typically separate processes that listen for messages or events on some type of queue, and execute tasks accordingly.
Worker processes can be create wherever it makes sense (eg. in an
app.workers
package), and extra services can be added to the
docker-compose.yml
file to start the processes.
Models¶
The models package app.models
contains all the domain objects
(usually data structures defined using the dataclasses
module) that
are used to represent data inside the application.
Used by:
core
,repo
,io
Public interface:
tree
Library code¶
The app.lib
package can contain any generic code that is not
strictly part of the business logic.
A good rule of thumb is that code inside this package could be eventually split into a completely separate library.
Can be used (sensibly) by any part of the codebase
Depends on: no other part of the codebase a part from within
app.lib
itselfPublic interface: any
Definition: public interface¶
self
: any public object defined in the module itself, or in the top-level__init__.py
for packages.submodules
: public objects defined in first-level modules or packages with a name not starting with_
are considered public.tree
: any public objects contained in any sub-module or sub-package not starting with_
are considered public.