Layered software architecture and dependencies

April 14, 2024  [software-design]  [programming] 

Having dependencies is an ever-present trait of software development, as it stems from the idea of modularity: one component naturally depends on funtionality “outsourced” to other components, creating dependencies. At the same time, for maintainability and extensibility of a software project, one is interested in minimizing dependencies and creating loosely coupled systems. In this blog post, I will briefly describe two slightly different views on how to minimize dependencies in a layered architecture.

Designing a software in terms of layers is a natural way to decouple different parts of the code, roughly in a progression from the closeness to the user to the closeness to the hardware/infrastructure. As such, the layers (high to low) can often be the following:

In this book “Domain Driven Design”, Eric Evans provides a simple guideline on how to minimize dependencies in a multi-layered system: depend only on components in the same layer or the layers beneath. As such, it is OK to refer to a component in a lower layer and call its methods, but one should not do that to realize the bottom-up communication. For that, the tool of trade is to use callbacks and listeners (see the Observer pattern). For example, if an event happens in the domain layer, and the application layers needs to know about it, its handler should be registered as a listener.


A bit stricter view is presented by the athors of “Dependency Injection Principles, Practices, and Patterns”. With a simple example of a web application with such layers and UI, Domain, and Data access, the preferred way to reduce dependencies is to consolidate definition of interfaces in the Domain layer, with both the lower and the higher level containing the implementations of those interfaces, all depending on the Domain layer.


If this technique is used, the dependency graph becomes much less coupled:


comments powered by Disqus