I’ve been struggling with the problem of modules for almost my entire working life. And I am convinced that this area of architecture is one of the most underrated challenges. By definition, a module is a set of parts that can be used to construct a more complex structure. Let’s work backwards. Given a complete design or architecture for a problem domain, we should be able to break into parts and then reassemble these parts to reconstitute the original architecture. We, most probably, can justify any form of decomposition strategy, be it functional, sub-system boundaries, sources and sinks of data, etc. Working forwards is a lot more difficult. Creating parts from nothing that can then be assembled into a complex structure is not easy because we often don’t know what the final structure will look like. This exercise is complicated further when working in an agile manner, where a big design up front is against the grain of short, sharp iterations of analysis, design, test, code, release.
Now think back to the start of a new project, those days where you first tackled a new problem domain. Those days are spent analyzing the domain. And if it was complex domain, you most likely started fragmenting it so that it was conceptually possible to understand little parts at a time. Eventually, you reach a single point where everything clicks into place. That’s your light-bulb moment. It’s an immensely satisfying moment that is filled with a great sense of achievement. And with this fulfilling sense of accomplishment, you naturally carry that conceptual decomposition into the design exercises.
In this very act lurks the conflict. Our modular decompositions created for conceptual understanding is often carried, unconsciously, into architectural designs. In my experience, it is very seldom that the conceptual parts are left alone for conceptual understanding of the problem domain and that a separate effort is expended to focus on modular decomposition for architectural soundness. This resonates back to the days when a huge database design was created early in the project and it was the most static design artifact of the solution. Anything that came afterwards, was morphed to fit in with uber-database design.
I think that the reason for trying to maintain that static set of modules (or that database-design-cast-in-stone) is that refactoring of modules is very immature. At best, it is downright painful because it often results in pruning your source tree. Domain Driven Design is very explicit about modules. There are modules of varying granularity. Tiny modules made up of aggregates or an aggregate and its repository. Big, fat modules based on bounded contexts. Medium sized modules that cohesively fit together because they tell a story in the domain. This is great, but refactoring modules as you discover more of the domain is still frightening. But I digress and will elaborate on module refactoring in another post.
I do view modules as parts that can be assembled to construct the complex whole. But I also view modules as part of a graph, kind of. Every module has a meaningful domain relationship to another module. Each module needs another module to fulfill some feature that is required of the complex structure. It is rare for a module to be completely disassociated from every other module. If a module is disassociated, then is it part of the whole? Hence, the module graph perspective. The graph of modules is likely to be significantly different to the modules that are the result of conceptual decomposition during analysis. This graph of modules is a design exercise, not an analysis exercise. By focusing on domain correctness and meaningful relationships that are true within the problem domain, we can still achieve sound architectural principles. Each module has a public interface and shields internal complexity from its neighbors. This gives us low coupling and good separation of concerns that reflect the reality within the problem domain.
I also believe that using a domain driven design approach that focuses on reflecting reality, will allow for less painful experiences in refactoring modules because the domain must change significantly to force changes to the natural interconnection of its parts. Sure enough, when a business changes strategy, its domain changes which will result in significant changes to modules. But that’s an exception.
So, ask yourself these questions:
- Are my modules artifacts of conceptual decomposition during analysis?
- Can I combine my modules to reconstruct the complete complex structure?
- Do my modules reflect the realities and deep truths of the problem domain?
- Are my modules, modules?