Lately, I’ve been thinking about two ways to design a system. I’m having trouble with figuring out which one I actually prefer. I’ll try to describe them below.
If you would like to read through the description and leave a comment or two I would be very grateful! Feel free to just add a comment or two in this document.
I’m using a fictional weather service as example. It does something along the lines of fetching the current weather data from a web service, parse it and extract relevant data, fetch some information about the current geographical location, and finally analyze them to provide the user with some interesting information about the weather.
The system is designed with the majority of the code as a set of completely independent modules which are joined together with a small amount of glue code.
Classes and modules tend to send primitive data or value objects to each other rather than intelligent classes. Modules tend to have none or very few dependencies. Re-use of modules is simplified because they can be used independently of each other.
Modules tend to be stateless with any state kept in the caller/glue code. External dependencies are kept as shallow as possible in the call/dependency tree.
The sequence of calls needed to perform something is encoded in the glue code rather than spread out in the system. It is up to the caller needs to know in which order to call modules. It delegates the execution of each step to the modules, but keeps control. The call stack will always remain rather short.
Each module can be unit tested in isolation and internal classes inside the modules do not have separate tests. The glue code is tested through integration tests that run end-to-end to verify that the system works. Unit tests often become easier to write because there is no need for mocking dependencies.
This approach is more “functional” in nature as modules tend to be stateless, get all required data as input and return the result as output.
The system is designed as a set of interacting, often stateful, modules which are joined together in a graph of dependencies.
One module often calls another module without providing all information necessary to complete a task, secure in the knowledge that the other module can obtain it. This is typically performed through injection of dependencies. Modules therefore often have multiple dependencies.
Modules are often stateful or depends on objects which are. External dependencies are often pushed as deep down the dependency tree as possible.
The sequence of calls needed to perform something is often spread out over a set of modules and partially encoded in the dependency setup. The caller can often call one or a few modules knowing they will in turn call others. The caller delegates not only the execution of tasks but also control to a large degree. The call stack will in the middle of execution be rather deep.
Each module is unit tested, typically with any dependencies replaced with stub/mock/fake instances. Integration tests ensure that the wiring of the modules is correct and that the system works together. There is little fundamental difference between tests on different levels.
This approach is perhaps more “object oriented” in nature where the system is divided into a set of stateful, intelligent objects that cooperate in generating the output.
I would appreciate any comments or input, such as your thoughts about:
- Which alternative you prefer? Which one do you most often use? Perhaps a hybrid?
- Under which circumstances is one better than the other?
- Are they good at different levels? E.g. method, class, component, or system level.
- Is there a “OO vs functional” difference, as I suggest?
- Is either of them easier to maintain than the other?
- Can they be described more clearly? What are they called?
- Have I gotten anything or everything completely wrong?
Feel free to enter any feedback or comments below.