What is an Architectural Model and why is it important?
Basically, an architectural model is the abstract structure on which your application will be implemented.
“The software architecture of a program or computer system is the structure or structures of the system that encompasses the software components, the externally visible properties of those components, and the relationships between them.” (Bass, Clements, & Kasman, Software Architecture in Practice)
To define the model that will best suit your project, we need to know well the company's short, medium and long-term strategies, the software's non-functional and architectural requirements, as well as the user growth curve over time and the volume of requests.
As well as the points mentioned throughout this article, there are still others to take into account when deciding which architectural model to apply. As an example, we can list:
Total volume of users;
Volume of simultaneous users;
TPS (transactions per second);
Availability on one or more types of platforms;
The survey of architecture, RAs (architectural requirements), VAs (architectural variables), RFs (functional requirements), RNFs (non-functional requirements) and the criteria that define each of these items directly influence the choice of the correct model.
The choice of architectural model can impact the entire life cycle of the application. Therefore, this is a subject that we must treat with great attention. The use of MVPs (especially those that do not go into production) can greatly help with this task. They give a unique opportunity to make mistakes, adjust, make mistakes again, prove concepts, adjust and make mistakes as many times as necessary so that in the end the software has the architecture in the most correct version, thus bringing the true gains of this choice.
How the architectural models are divided
It is ideal to make it clear that like many definitions in the software world, what architectural models are and what they are can vary. Therefore, in this article I tried to divide them into four large groups: monolithic, semi-monolithic (or modular monolith), distributed monolith (or microlith) and microcomponentized.
Model in which all components form a single application or executable integrated into a single source code. In this case, it is all developed, deployed and scaled as a single unit.
Figure 1 – Example of a Monolithic Model.
Simplicity: As the application is treated as a single, cohesive unit, it becomes simpler as all parts are contained in a single source code.
Greater adherence to Design Patterns: taking into account that we have a single source code, another factor that makes it easier is that the design patterns themselves (Design Patterns, 01/2000) were written in times of monolith dominance, making the application of even more adherent.
Greater performance: due to low latency in communication, monoliths tend to have good performance, even using more outdated technologies.
Lower resource consumption: low complexity, simplicity and lower communication overhead between layers favor lower resource consumption.
Easier troubleshooting: Creation of development and debug environments is made easier in monoliths, as the components share the same processes in them. Another factor that we can take into account is that monoliths have fewer external failure points, simplifying the search for errors.
Limited team size: breakdowns related to Continuous Integration and merge conflicts happen more regularly in monoliths, creating difficulties in parallel work for large teams.
Scalability: Scalability may be limited in certain aspects. Even with ease in vertical scalability, horizontal scalability can often become a problem that could affect the growth of the application.
Availability of windows: normally, for a monolith, executables are exchanged, which requires a window of availability without users accessing the application, which does not happen with other architectural models that can use other deployment techniques such as Blue-Green or even work with images or pods.
Single technology: low technological diversity can often become an impediment to the growth of the application by only serving one type of operating system, for example, or not fully meeting new features requested by customers due to not having updates that have the capacity to solve complex problems.
Greater expenditure on compilation and execution: large monoliths generally take a long time to compile and execute locally, generating a greater commitment in development time.
When to Use
Low Scalability and Availability: if the application has a limited scale where, for example, the number of users is low or high availability is not mandatory, the monolithic model is a good solution.
Desktop Applications: the monolithic model is highly recommended for desktop applications.
Low seniority teams: monolithic models, due to their simplicity and location of components, enable low seniority teams to work with better performance.
Limited resources: for a limited infrastructure with scarce resources.
Semimonolithic (or Modular Monolith)
Model in which applications are composed of parts of monolithic structures. In this case, the combination tries to balance the simplicity of the monolithic model and the flexibility of the microcomponentized model. Currently, this architectural model is often confused with microservices.
Figure 2 – Example of a Semimonolithic Model.
It brings benefits of the monolithic and microcomponentized models: with this, it is possible to maintain parts as monolithic structures and only microcomponentize components that have a real need.
Technological diversity: possibility of using different technological approaches.
Diversified infrastructure: this model can be developed to use both On-Premise and Cloud infrastructure, favoring migration between both.
Supports larger teams: the segmentation of components allows several teams to work in parallel, each within its own scope.
Technical Specialties: due to segmentation, the team's hard skills are made better use of, such as frontend, UX, backend, QA, architects, etc.
Standardization: due to the large number of components that can appear in a semi-monolithic model, standardization (or lack thereof) can become a major problem.
Complexity: the complexity inherent to this type of model also tends to increase with new features. Therefore, new features such as messaging, caching, integrations, transaction control, testing, among others, can add even more complexity to the model.
Budget: in models that support the use of different technologies with large teams, more specialist professionals with a higher level of seniority are needed, often resulting in greater expenditure on personnel expenses.
Complex troubleshooting: the complexity of the model and the diversity of technologies make troubleshooting the application increasingly difficult. This is due to the large number of failure points (including external to the application) that come to exist and the communication between them.
When to Use
Accepted in Various Scenarios: it is a flexible model that can meet various scenarios, but not always in the best way.
Little Definition: in projects that have uncertainties or even that do not have the full definition of their requirements, this model is the most suitable.
In medium and large teams: as mentioned, the division of components into several groups facilitates parallel work in medium and large teams. Typically, groups have their own code repositories, which makes parallel work more agile.
Diverse Seniority: this model benefits from teams with this format, as semi-monolithic software presents varied challenges, both in the frontend and backend layers and in infrastructure issues (IaC – Infrastructure as a Code).
Infrastructure: for a Cloud-based or hybrid infrastructure, this model is more applicable. It is a model that allows, for example, gradual adoption between On-Premise and Cloud, facilitating adaptation and minimizing operational impacts.
This modeling is a "modern" modeling that has also been implemented and confused with the microcomponentized/microservices model.
"You shouldn't start a new project with microservices, even if you're sure your application will be big enough to make it worthwhile." (Fowler, Martin. 2015)
In summary, in this architectural model the software is built on the basis of the monolithic model, but implemented according to the microcomponentized model. Currently, many consider it an antipattern.
Figure 3 – Example of Distributed Monolith Model.
It wouldn't be worth listing the pro features (I don't know if there are any), but it's still worth mentioning features that go against it: this architectural model brings together the negative points of the other two styles with which it is confused.
In it, services are highly coupled and also have various types of complexity, such as: operational, testability, deployment, communication and infrastructure.
The high coupling, especially between backend services, brings serious difficulties in deployment, not to mention the significant increase in points of failure in the software.
Software model in which all components are segmented into small, completely decoupled parts. Within microcomponents, we can mention:
Figure 4 – Example of a Microcomponentized Model.
"A microservice is a service-oriented application component that is tightly scoped, strongly encapsulated, loosely coupled, independently deployable, and independently scalable" (Gartner, n.d.).
Opinions converge to say that every microservice that worked was first a monolith that became too big to be maintained and reached a common point of having to be separated.
Scalability: Scalability in this model becomes quite flexible. Depending on the need, the components are scaled in a specific way. Agile Development: Teams can work independently on each component, facilitating continuous deployment and accelerating the development cycle.
Resilience: if a component fails, it does not necessarily affect the entire application. This improves the overall resilience of the system. It is important to note that there are single point of failure approaches to avoid this type of problem.
Diversified Technology: each component can be developed using different technologies, allowing the choice of the best tool for each specific task. Furthermore, it also favors the existing skills of each team.
Ease of Maintenance: changes to one component do not automatically affect others, facilitating maintenance and continuous updating.
Decoupling: components are independent of each other, which means that changes to one service do not automatically affect others, facilitating maintenance.
Cost: high cost of all components of this model (input, output, requests, storage, tools, security, availability, among others).
Size: microcomponentized software tends to be larger in essence. Not only the size of the application, but the entire ecosystem that permeates it from commit to the production environment.
Operational Complexity: there is an exponential increase in complexity in this model. Designing good architectural components so that this complexity is managed is of great importance. It is important to choose and manage logging tools, APM and Continuous Monitoring, for example, well. Managing many microservices can be complex. Additional effort is required to monitor, orchestrate, and keep services running.
Latency: Communication between microservices can become complex, especially in distributed systems, requiring appropriate communication and API management strategies.
Network Overhead: Network traffic between microservices can increase, especially compared to monolithic architectures, which can affect performance.
Consistency between Transactions: Ensuring consistency in operations involving multiple microservices can be challenging, especially when it comes to distributed transactions.
Testability: Testing interactions between microservices can be more complex than testing a monolithic application, requiring efficient testing strategies.
Infrastructure: You may need to invest in robust infrastructure to support the execution of multiple microservices, including container orchestration tools and monitoring systems.
Technical Dispersion: at this point, we can say that there is an action of "Reverse" Conway's Law, as teams, as well as technologies and tools, tend to follow dispersion and segregation. In teams, each person becomes aware of a small part of a larger whole. This way, for technologies and tools, each developer uses the framework or tools that suit them best.
Domain-Driven Design: to increase the chances of success of this model, teams must have knowledge of DDD.
When to Use
Volumetrics: the microservices/microcomponents architecture has proven to be effective in high-volume systems, that is, those that need to deal with large amounts of transactions, data and users.
Availability: one of the main reasons for adopting this type of architecture is availability. When well constructed, software that adopts microcomponentization does not tend to fail as a whole when small parts present problems. Therefore, other components continue to operate while the problematic component recovers.
Scalability: If different parts of your application have different scalability requirements, microservices can be useful. You can scale only those services that need the most resources, rather than scaling the entire application.
Team Size: Small teams can be problems. Configurations, boilerplates, environments, tests, integrations, input and output processes.
Resilience > Performance": in cases of uncertainty, for example, the volume of requests and how far it can reach, such as large e-commerces in periods of high access (Black Friday) where it is necessary for the software to be more resilient and perform better median.
Figure 5 – Checklist Comparison between models.
In summary, the choice of the architectural model is crucial to the success of the project, requiring a careful analysis of needs and goals. Each architectural model has its advantages and disadvantages and we must guide the decision by aligning it with the specific requirements of the project. By considering company strategies, requirements and architectural surveys, it is possible to make a decision that will positively impact the application life cycle.
The work (and support) of the architecture team is extremely important. It is also of great importance that management and related areas support by providing time to collect this entire range of information.
Still in doubt? At first, start with the modular semi-monolith/monolith. Likewise, pay close attention to database modeling.
Gartner. (n.d.). Microservice. Retrieved from https://www.gartner.com/en/information-technology/glossary/microservice
Gamma, E., Helm, R., Johnson, R., & Vlissides, J. (1994) Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley.
Bass, L., Clements, P., Kazman, R. (2013) Software Architecture in Practice (3rd ed.). Addison-Wesley.
Microservices Architecture (12/2023). Retrieved from https://microservices.io/
Fowler, S. J. (2017) Production Ready Microservices. Novatec.
ArchExpert Training. (n.d.). Premium Content. Retrieved from https://one.archoffice.tech/
Monolith First (06/2015). Retrieved from https://martinfowler.com/bliki/MonolithFirst.html
Microservices. Accessed on 01/2024.