Monolithics and Microservices: Software Architecture in Real-Life Applications
Look over these microservices reference architectures to ensure that you know how to properly structure and update your monolith with DevOps practices.
Join the DZone community and get the full member experience.Join For Free
The development of software systems without a good architecture in place may cost an organization its existence. For example, if an online e-commerce company developed their platform in a coupled non-modular approach where the user interface and business-logic functionalities are mixed in same source files, they may need a lot of investment (time and resources) to be able to support new smart-phone native apps or scale to achieve a higher demand. This style of design affects software maintainability quality attribute and will increase the business time-to-market.
This article aims to provide the reader with a list of reference architectures that can be used as the base for designing modern software projects.
The presented architectures are designed based on three factors: distributed or non-distributed, front-end (server-side or client-side), and finally, monolithic or microservices.
In this article, Section I starts with explaining the concepts of monolithic systems, microservices architecture, and DevOps culture. Section II discusses software architecture in general and its importance. Section IV presents the list of reference architectures (architecture templates), that can be used in modern software applications development for both: Monolithic and Microservices based applications.
The following figure summaries the discussed reference architectures presented in this article.
I. Deployment Monolithics, Microservices, and DevOps
Modern software systems may scale from tens of users to tens of millions of users if designed correctly, without changing the source code, for example, NetFlix demand has increased 100 times in 2013 since 2009 because of utilizing Amazon Web Services public cloud (Long and Bastani, 2017). Thanks to cloud computing elasticity that enabled unprecedented efficient scalability. In addition, continuous integration and continuous delivery approaches have enabled a tremendous improvement in software quality and deployment automation. For example, in 2011 Amazon deployed a change to production every 11.6 seconds without ever impacting end users (Richardson, 2018).
However, cloud computing can’t do much if systems are still designed in deployment monolithic approach (Wolff, 2016), where software applications are deployed as a single unit. Even though the monolithic approach is easy to understand by developers and has been adopted for decades, it has become a bottleneck with the new technology trends.
In the monolithic approach, software systems are developed most likely in the same technology stack, uses a centralized database repository, and uses heavyweight, horizontal, cluster-based replication as a scalability strategy.
Due to many factors, Microservices architecture has become the de-facto standard of designing and developing cloud-based internet applications. These factors and trends include cloud computing, global work-forces, the need to faster time-to-market, various technology options, load increase enabled by smart-phones and internet of things.
Microservices architecture is based on developing software projects into modular services that are: small, self-contained, self-deployable, designed around business capabilities, manage its own data, and communicate with other services over a lightweight protocol (most likely HTTP).
In microservices, every service is designed, developed and operated by a dedicated team, which has almost a full decision on the design and technology of their service. This approach of team structure and management is called DevOps.
II. What Is and Why Software Architecture?
The main purpose of software development processes is to deliver primary functionality expected by the end users and external stakeholders. In the software engineering discipline, this category of functionality is known as functional requirements (Sommerville, 2016).
Most of the time, functional requirements can be achieved without following best practices or having a good architecture. For example, implementing a full-featured wire-transfer functionality (including security, validation, integration, and auditing), can be implemented in a single file (maybe tens of hundreds of lines of code).
However, in the last example, the application will not survive for a long time. Problems will start to be visible in many stages, starting with the internal testing, user acceptance testing, to the going-live. Main concerns will be reliability (achieving the required functionality as expected all the time) and maintainability (the ability to maintain and apply changes to this functionality). Also, other potential issues include scalability and security. This leads to a different type of requirements: non-functional requirement (i.e. quality attributes).
Quality attributes are mainly classified as functionality, reliability, usability, efficiency, maintainability, and portability attributes. The full list of quality attributes can be found here.
A wider term proposed in the Attribute-Driven Design method (ADD 2.0) by the Software Engineering Institute (SEI), called Architecturally Significant Requirements (ASR). ASR includes
For example, design purpose may be a proposal, a proof-of-concept, or a final product, which will significantly affect the architectural design scope and process (Cervantes and Kazman, 2016).
The importance of software architecture lies in achieving the quality attributes to the accepted levels by the internal and external stakeholders. For example, with very dynamic markets such as the startup's ecosystem, time to market is very important, so maintainability shall have a high-priority. For online payment gateways, security, and auditability shall be significant to achieve.
III. Service Oriented Architecture (SOA) vs Microservices
Service Oriented Architecture (SOA) is an architectural style adopted by most enterprises in the last decade. Its main objective was to achieve a higher level of reusability and modularity by providing standard interfaces of individual enterprise applications as services and being able to build business processes from orchestrating these services using an orchestration and integration platform.
Even though SOA concepts look like microservices from reusability and flexibility point of view, the huge investments by organizations didn’t gain its expected results.
“We have seen so many botched implementations of service orientation - from the tendency to hide complexity away in ESBs, to failed multi-year initiatives that cost millions and deliver no value, to centralized governance models that actively inhibit change, that it is sometimes difficult to see past these problems.” (Fowler, 2014)
The tools complexity, the over-engineered platforms, the communication overhead, and the need to have all the enterprise divisions automated to be able to develop a full process, are all constraints that make SOA not the right option in many cases.
The main differences between SOA and microservices are summarized as follows:
- SOA services communicate over heavyweight smart-pipes (ESB), microservices communicate over dump pipes with smart-end points (Fowler,2014).
- SOA aims to integrate enterprise large complex monolithic applications into enterprise-wide processes, while microservices aims to integrate small microservices into a single project (Richardson, 2018).
- SOA is enterprise-wide based, microservices is project based (Wolff, 2016).
- In SOA, a business process is created as orchestration and managed by the platform, in microservices, a business process is written as another microservice on the application level, which can be replaced by another service if the process changed (Wolff, 2016).
IV. Modern Reference Architectures
This section includes a list of my perspective of modern software architecture. It is classified based on its deployment strategy (monolithic vs microservices-based), distribution nature (distributed vs nondistributed backend), and front-end coupling with the backend (server-side vs client-side front-end).
1. Non-Distributed Monolithic With Server-Side Front-End
In this architecture, as shown in Figure 1, the application is developed using a 3-layer architecture which runs in a single process. In this approach, the front-end can be developed using Java EE( JSP or JSF), Spring MVC (FreeMarker, Thymeleaf, or JSP), and dynamic HTML rendering using server-side scripting languages such as PHP or Python.
Figure 1: Monolithic version 1 (nondistributed with server-side front-end architecture).
The main advantage of this approach is development simplicity and convenience. However, scalability, maintainability, and security are the main concerns. In addition, support for modern front-end technologies (such as smartphones apps) may be challenging as well.
2. Non-Distributed Monolithic With Client-Side Front-End
This design is similar to the previous one, except that the frontend and the backend are separated into two subsystems. In this approach, the front-end will be fully running on the client side while the backend will run as a server-side process. The components diagram of this architecture is shown in Figure 2.
Figure 2: Monolithic version 2 (nondistributed with a client-side front-end).
Potential technologies to be used in the front-end, in this case, could be
Native client applications such as Android, iOS, or desktop apps.
Hybrid applications, such as Cordova, PhoneGap, or Xamarin.
This approach enables technology separation between the front-end and backend, where a new front-end can be supported easily without any modifications to the backend.
3. Distributed Monolithic With Client-Side Front-End
In this version, the application is divided into 4-tiers (separate processes), where each tier communicates with the tier next to it over the network as shown in Figure 3.
Figure 3: Monolithic version 3 (distributed with clientside frontend).
In this architecture, even though extra complexity is added to development, deployment, and operations, it enables more levels of modularity and reusability of every tier, where any tier can be easily replaced with another one. In addition, it is considered more secure than the one-tier architecture presented int he previous two approaches. Moreover, it allows more efficient scalability, especially if asynchronous communication is implemented (maybe by using event-driven reactive techniques or traditional messaging such as Java messaging service).
4. Non-Cloud Native Microservices
This design utilizes the microservices architectural style. In particular, the application is modulized into self-contained services that can be deployed independently. In microservices architecture, every service shall be designed around business capabilities and manage its own database. This design is shown in Figure 4.
Figure 4: Microservices version 1 (non-cloud-native).
Microservices enables evolutionary approach of software development, where services and features can be developed and deployed incrementally without affecting other services. In addition, it enables more efficient scalability with its lightweight horizontal scalability on the microservice level (not the application level).
The main challenge of microservices including the difficulty of an early architectural decision of the application's microservices, their specifications, and their communication contract. In fact, a recommendation by Martin Fowler is to start monolithic and refactor to microservices later. I think that Martin's recommendation is based on having a team who follows best practices, design patterns, and code refactoring processes. However, I don't fully agree with that, since refactoring an application from monolithic to microservices may be very costly, especially if it wasn't designed with future refactoring in mind.
Potential technology for microservices implementation includes:
Java Micro profile.
Theoretically, any technology talks HTTP can be used to develop microservices.
5. Cloud-Native Microservices
The architecture discussed in this section based on cloud-native microservices architecture. In this approach, a microservices-based application will exploit the full benefits of cloud computing based on the 12-Factors concepts. Figure 5 shows a basic diagram of this architecture.
Figure 5: Microservices version 2 (basic cloud-native based).
This design might be sufficient for small-medium scale cloud-based applications. However, for more complex and larger scale applications, more infrastructure microservices should be considered, such as the components provided out of the box by Spring Cloud project.
In addition, other opensource projects may be useful to explore, such as Spring Boot Admin for real-time web-based application monitoring and JHipster project which provides a nice generator for cloud-based applications.
In this article, I presented my perspective about the modern software architectures for real-life applications. Definitions and importance of software architecture, microservices architecture, DevOps, and SOA were presented.
As with any other new trends and technologies, I think the concepts of microservices and cloud-native applications are still in the early stages and still evolving, so the debates and arguments about them will stay around for a while.
Opinions expressed by DZone contributors are their own.