The monolithic architectural style is a traditional architecture type and has been widely used in the industry. The term monolithic is not new and is borrowed from the UNIX world. In UNIX, most of the commands exist as a standalone program whose functionality is not dependent on any other program. As seen in the following image, we can have different components in the application, such as:
- User interface: This handles all of the user interaction while responding with HTML or JSON or any other preferred data interchange format (in the case of web services).
- Business logic: All the business rules applied to the input being received in the form of user input, events, and database exist here.
- Database access: This houses the complete functionality for accessing the database for the purpose of querying and persisting objects. A widely accepted rule is that it is utilized through business modules and never directly through user-facing components.
Software built using this architecture is self-contained. We can imagine a single .NET assembly that contains various components, as described in the following diagram:
As the software is self-contained here, its components are interconnected and interdependent. Even a simple code change in one of the modules may break a major functionality in other modules. This would result in a scenario where we'd need to test the whole application. With the business depending critically on its enterprise application frameworks, this amount of time could prove to be very critical.
Having all the components tightly coupled poses another challenge—whenever we execute or compile such software, all the components should be available or the build will fail; refer to the preceding diagram that represents a monolithic architecture and is a self-contained or a single .NET assembly project. However, monolithic architectures might also have multiple assemblies. This means that even though a business layer (assembly, data access layer assembly, and so on) is separated, at runtime, all of them will come together and run as one process.
A user interface depends on other components' direct sales and inventory in a manner similar to all other components that depend upon each other. In this scenario, we will not be able to execute this project in the absence of any one of these components. The process of upgrading any one of these components will be more complex as we may have to consider other components that require code changes too. This results in more development time than required for the actual change.
Deploying such an application will become another challenge. During deployment, we will have to make sure that each and every component is deployed properly; otherwise, we may end up facing a lot of issues in our production environments.
If we develop an application using the monolithic architecture style, as discussed previously, we might face the following challenges:
- Large code base: This is a scenario where the code lines outnumber the comments by a great margin. As components are interconnected, we will have to bear with a repetitive code base.
- Too many business modules: This is in regard to modules within the same system.
- Codebase complexity: This results in a higher chance of code-breaking due to the fix required in other modules or services.
- Complex code deployment: You may come across minor changes that would require whole system deployment.
- One module failure affecting the whole system: This is in regard to modules that depend on each other.
- Scalability: This is required for the entire system and not just the modules in it.
- Intermodule dependency: This is due to tight coupling.
- Spiraling development time: This is due to code complexity and interdependency.
- Inability to easily adapt to a new technology: In this case, the entire system would need to be upgraded.
As discussed earlier, if we want to reduce development time, ease of deployment, and improve maintainability of software for enterprise applications, we should avoid the traditional or monolithic architecture.