The main worker (process and thread) is the user app. The user defines the in-memory architecture.
Workers are started and stopped via manager instances. The managers are helpers to setup and start the business logic in workers.
We have:
- RepositoryManager
- EngineManager
- LoggerManager
- etc...
Due to a python limitation, all the channels used by a worker MUST be defined before it gets started (so it's passed as argument).
The user can choose which worker backend to use for each one (multiprocessing, threading, etc).
Each worker run the business logic:
- Repository (ImapRepository, MaildirRepository, StateRepository, etc)
- Engine
- Logger
- etc...
All the workers loop. All the workers are passed channels. All the workers send messages to the other workers via the channels. All the workers have one ErrorLink channel (read in main) so it can stop the workers on errors.
The repository manager handles:
- the repository: designed to receive all the requests from the engine.
- the endpoints: each endpoint is attached to the repository via a channel. It's the abstract on top of a socket. However, it's high-level. It is a chain of controllers on top of one reader and one writer.
The user can redefine any class. There are the following namespaces:
- imapfw.api: the classes intented to be redefined by the user.
- imapfw.contrib: the classes written by the users for the users.
- imapfw: the core (should not be redefined by the users).
- imapfw.apps: the apps provided by the project, written as any other user app.
In the app, the user can use the classes in imapfw.api as-is or redefine them in the app package. So, we should have imports like:
from imapfw.api import Something # as-is
from .repositories.imap import MyImapRepository # redefined
and in app/repositories/imap
we have something like:
from imapfw.api.repositories import ImapRepository
class MyImapRepository(ImapRepository):
"""Redefinitions."""