-
Notifications
You must be signed in to change notification settings - Fork 245
Dev: Comments on design
This page describes the underlying reasoning for some of design choices made during maddy development.
This page is WIP, the initial design discussions are scattered across issue tracker, IRC channel logs and etc. I'm slowly collecting the main facts here to help new contributors understand why things are done this way.
mod•u•lar (adj.)
- a self-contained unit or item that can be combined or interchanged with others like it to create different shapes or designs.
-- https://www.thefreedictionary.com/modular
"Module", in terms of maddy, is a implementation of replaceable functionality that may be used by other code.
Maddy exposes access to the technique called "dependency injection" to the user, this allows supporting a great amount of possible configurations with a reasonably simple framework. Essentially, maddy becomes a set of buildings blocks that can be connected together in arbitrary ways to achieve wanted functionality.
Some concerns were risen about that exposure of internal logic to users. However, provided that most modules follow common rules and interact with each other in a predictable way, additional complexity added to the configuration by its modular nature is not that high.
Was added to remediate the maintenance burden when amount of modules that implement similar functionality will become big. Each module doesn't need to keep track of all modules that can interact with it, it simply looks up requested module by name and checks whether it can be used (e.g. whether it implements a certain interface).
Concept of "module instances" (aka configuration blocks) was initially introduced to address efficiency concerns. Initially, components of a single module were isolated and had no way to interact with each other. For example, there was no way for the part of storage implementation responsive for message delivery to notify the part of storage implementation responsive for providing mail access to clients about the new message. The solution was to put them back together. Now, with a single object managing certain resource that resource can be used more efficiently by sharing information between contexts where that resource is used.
However, the modules system is more generic that it is needed sometimes. "State sharing" explained above is important only for modules that wrap some sort of resource (e.g. database with messages). There are also modules that don't care about where and how they are used (e.g. dumb message filters based on a single static rule). Configuration syntax defining them at the top-level is a little bit too verbose. There is no need to share state so there is no need to have only one instance of require_matching_rdns
module, for example. However, there are different configurations for require_matching_rdns
users would want to use, so we can't just have only one object for require_matching_rdns
and ditch entire modules thing altogether.
Currently used solution is to allow creating new module instances right where they are used. Such module instances are not registered globally and have no name associated so they are used only where they are defined in configuration.
- https://github.com/foxcpp/maddy/issues/15 (initial proposal of modular design)
- https://github.com/foxcpp/maddy/issues/42 (inline module definitions)
Initially, the idea was to have the minimal possible amount of lines in a configuration that users want in most cases. So, basically, if you don't tell maddy what to do, it will do something you probably want it do (get SMTP/IMAP listeners, SQL DB for messages, DKIM/DMRAC/etc verification, etc... all that mess for a typical mail server setup). This idea is based on how Caddy configuration works, where it is enough to specify the domain name to have it obtain a TLScertificate for you and configure HTTPS.
There was an argument on IRC about how this contradicts the principle of least surprise: You can't know what maddy is going to do by just looking at its configuration. E-Mail is a much more complex technology than Web (HTTP). For example, strict DNS checks enabled by default may cause unexpected delivery failures.
So the conclusion we came to: It is important for the configuration file to provide complete description of server behavior in all situations to make sure there are no surprises.
- IRC logs of ##emersion channel (TODO, get them)