Modularity is designing a system that is divided into a set of functional units (named modules) that can be composed into a larger application. A module represents a set of related concerns. It can include components, such as views or business logic, and pieces of infrastructure, such as services for logging or authenticating users. Modules are independent of one another but can communicate with each other in a loosely coupled fashion.
A composite application exhibits modularity. Imagine an online banking program. The user can access a variety of functions, such as transferring money between accounts, paying bills, and updating personal information from a single user interface (UI). However, behind the scenes, each of these functions is a discrete module. These modules communicate with each other and with back-end systems such as database servers. Application services integrate components within the different modules and handle the communication with the user. The user sees an integrated view that looks like a single application.
Figure 1 illustrates a design of a composite application with multiple modules.
Designing a Modular System
When you develop in a modularized fashion, you structure the application into separate modules that can be individually developed, tested, and deployed by different teams. Modules can enforce separation of concerns by vertically partitioning the system and keeping a clean separation between the UI and business functionality. Not having modularity makes it difficult for the team to introduce new features and makes the system difficult to test and to deploy.
The following are specific guidelines for developing a modular system:
- Modules should be opaque to the rest of the system and initialized through a well-known interface.
- Modules should not directly reference one another or the application that loaded them.
- Modules should use services to communicate with the application or with other modules.
- Modules should not be responsible for managing their dependencies. These dependencies should be provided externally, for example, through dependency injection.
- Modules should not rely on static methods that can inhibit testability.
- Modules should support being added and removed from the system in a pluggable fashion.