Functional Software Architecturerefers to methods of construction and structure of large and long-lived software projects that are implemented in functional languages and released to real users, typically in industry.
We strive for ...
Problem domains are complex enough. The solutions we build should be as simple as possible.
Software development is about running software just as much as it is about gaining insight into a domain.
Maintainability and malleability fundamentally enable the software creation process.
We value software being correct both in the small and in the large.
Software has to be both correct and fast. If software were not fast, computers would be obsolete.
We strive for the values described above. We do so by following this set of principles.
Stop thinking in terms of state and resource management and start thinking in terms of your domain.
A pure function transforms immutable values without performing any side effects.
Reifying concepts as values allows these concepts to be passed around, analyzed and composed. Functions as values, property accessors as values, UI components as values ...
We like to combine small software structures to form larger structures – without cognitive overhead.
Functional software architects try to find models that build on algebraic structures that stood the test of time, such as Monoids, Functors, and Monads.
Abstraction is the sharpest weapon of reason. Functional software architects welcome abstraction as a tool for coping with complexity.
Functional Software Architecture allows many architectural decisions to be expressed in code. We may still use diagrams and descriptions as supporting documentation, but the source of truth is always to be found in the code.
Make the communication channels between building blocks as wide as neccessary and as narrow as possible. Build tools with affordances toward low coupling and high cohesion.
Software design is usually performed under uncertainty. Instead of trying to make the right decisions up front, we want to design our systems in such a way that it is easy to change our minds later in the process. This shifts our focus from making decisions to making decisions possible.
Modules hide difficult decisions behind simple interfaces. While modularization is not an exclusive feature of functional architectures, functional abstractions allow for simpler interfaces and therefore allow to hide more decisions, leading to more malleable designs overall.
«Make illegal states unrepresentable» is a functional design technique that leverages product and sum types to decrease the bug surface of your software.
→ MoreWe follow the principles described above by employing some of the following techniques.
Structure software into functions in the core that are pure and functions in the shell that are impure.
→ MoreTODO
TODO
Functional software architecture is best done in proper functional programming languages.
Type systems allow you to enrich your code with descriptions of properties and requirements, which can be statically checked and enforced.
TODO
Different components of a system may need the same information but may have different demands on its structure. We employ bidirectional data transformations with functional optics to simplify conversions from one representation to the next.
→ MoreTODO
Effect systems allow us to deal with effects by making them explicit. Effect systems also allow effectful code to be run in a pure environment, which makes our code better testable.
Handle errors in a way that they can be composed, combined, and passed through different parts of your program predictably.
Facebook’s React popularized the component model of user interface programming. Functional programming languages allow to improve on that model by treating components as composable first-class user interfaces. Functional UI libraries provide a set of primitive components and a set of UI combinators, which let you build sophisticated graphical user interfaces without cognitive overhead.
TODO
TODO
Denotational Design is a software design methodology which tries to extract the essence of a domain’s problem and describe it formally in machine-checkable code. Denotational design affords software designers to be absolutely precise in what they want to achieve before they talk about how they plan to achieve it. Denotational Design informs both the use and the implementation of a unit of software without coupling them. Denotational Design is therefore a methodology to build airtight abstraction barriers.
«Parse, don’t validate» is a simple mnemonic for type-driven design.
«Trees that grow» is a method to make models built with algebraic data types more extensible.
«Data types à la carte» is a technique to deal with the dreaded Expression Problem in functional languages.
A smart constructor semantically behaves like any ordinary constructor, but it performs some useful computations such as preprocessing, normalization, parsing, or validation.
TODO
No (TODO)
TODO