How to Choose the Best Software Architecture with Architectural Drivers?
In software development, Craftsmanship is the art of creating high-quality, reliable, and easy-to-maintain solutions. Choosing the right architecture for your project is an important part of this process.
The success of your development depends on selecting the right software architecture. However, there are so many options that it can be hard to know where to start. This is where Architectural Drivers come in. They give us a method for making this decision.
In this post, we’ll discuss two popular architectures that have sparked much interest and debate among developers: microservices and modular monolithic models using Architectural Drivers.
We’ll look at critical factors like performance, scalability, security, and maintainability and explain how they can help you make informed decisions.
Understanding Architectural Drivers
Architectural Drivers are important elements that help shape a system’s architecture. They represent project requirements and limitations that significantly impact the system’s overall structure.
There are four types of Architectural Drivers:
- Functional requirements: these are the system’s expected functionality and capabilities. They specify what the software needs to do to meet the business requirements. For instance, business processes, user interfaces, and the way the system interacts with other systems are all examples of functional requirements.
- Constraints and preferences: these are the limits the system must work within, such as budget, time, resources, and regulatory requirements.
- Technical constraints and concerns: these relate to the technical environment in which the system will be deployed and maintained. Technical requirements can include things like hardware and software platforms, coding standards, communication protocols, and how to integrate with other third-party systems.
- Quality attributes: non-functional requirements that specify how the system performs its functions. This may include factors such as reliability, scalability, maintainability, security, accessibility, and performance. Identifying and ranking the desired quality attributes helps make sure the system architecture can meet these requirements.
Architectural Drivers play a key role in choosing the best software architecture for your needs. They do this by giving you a framework to help you make decisions during the system design and implementation phases. They help make sure that the system architecture stays in line with its intended purpose:
- Alignment with business objectives: an architecture must meet the project’s functional and non-functional needs. It ensures that the developed system will meet the needs of the stakeholders and help the company reach its goals.
- Adaptability to future changes: an architecture that follows the Architectural Drivers makes it easier to predict how a system might change in the future. This makes it possible to choose an architecture that adapts to changes in the functional and technical requirements.
- Code consistency and maintainability: taking the Architectural Drivers into account when choosing the architecture ensures that the code is structured consistently and easy to maintain, making it easier to make changes and updates in the future.
Because of this, it’s important to think carefully about your software architecture choices, weighing the pros and cons of each option and balancing functional and non-functional needs with the project’s constraints and the preferences of its stakeholders. Trade-offs are inevitable, and finding the best solution(s) for each situation is vital.
Understanding the Microservices Architecture: Definition, Benefits, and Challenges
What Is the Microservices Architecture?
The microservices architecture is a software architecture model that organizes an application into a set of independent services. Each service is responsible for a specific function and communicates with the others through clearly defined interfaces called Application Programming Interfaces (APIs).
It’s based on a few fundamental principles that allow us to build scalable, flexible, and resilient systems:
- Decoupling of services: microservices can be built and deployed separately. This means that work on one part of the system won’t disrupt other services.
- Better granularity: microservices are independent units that can be developed, deployed, and scaled individually.
- Single responsibility: each microservice performs one task. This is the “S” in the SOLID method. A coherent set of functions is required to achieve this.
- Interoperability: the microservices communicate with each other using standard protocols that do not depend on the technology being used. This makes it easier for services to connect to and communicate with each other.
This architecture was created to meet businesses’ growing needs for increasingly complex, scalable, resilient, and flexible software systems:
- Scalability and upgradability: the microservices can be resized independently. This means that the capacity of each service can be changed as the workload changes, and the resources allocated to development and maintenance can be adjusted separately as business needs change.
- Technological flexibility: each microservice can be developed, tested, and deployed using different programming languages and technologies. This lets you choose the best tools and processes for each service (programming language and framework, database model, DevOps techniques and approaches, continuous integration/continuous deployment – CI/CD, etc.).
- Resilience: microservices should be designed so that errors or performance issues in one service won’t spread to the other services. This makes the system more resilient overall.
Microservices are not a silver bullet to solve all your problems. They solve certain problems while introducing others.
Disadvantages of the Microservices Architecture
Although the microservices architecture has numerous advantages, it can also present some design challenges (a more accurate way of describing the disadvantages of a software architecture), particularly in terms of coordinating and managing the various services:
- The “Complexity” box may need revisiting: managing many microservices can increase the complexity of both development and infrastructure management.
- Latency: communication between microservices can add latency, mainly due to data serialization and deserialization.
“Networks are not reliable. Packets get lost. Connections are severed. Yet they work.” – Dave Piscitello, IT security expert.
- Deployment and management: deploying and managing microservices requires specific tools and skills, such as containers, orchestrators, and API gateways.
- Security: securing communications across microservices and managing access can be more complex and requires special care.
In the next section, we’ll discuss the pros and cons of the modular monolithic architecture compared to the microservices architecture.
Understanding the Modular Monolithic Architecture: Definition, Benefits, and Challenges
Definition of the Modular Monolithic Architecture
The modular monolithic architecture is an architectural approach that organizes an application into a single system divided into different modules. Within a monolith, each module performs a specific function and communicates with the other modules.
The modular monolith offers the advantages of modularity without the distributed-systems complexities.
The key principles of a modular monolith architecture are:
- Modularity, the route to encapsulation: modular monoliths are made up of independent modules that encapsulate specific functions and interact with each other through clear interfaces.
- Cohesion: modules in a modular monolith should be highly cohesive. That means they should contain elements related to a single functional area or responsibility.
- Loose but not non-existent coupling: modular monolith modules should be loosely coupled. This means they should have as few dependencies as possible and promote independence.
- Integration: the modules in a modular monolith are integrated into a single code base and share the same development, deployment, and execution cycle.
Pros of a Modular Monolith
The following are some of the benefits of a modular monolith:
- Simplicity: modular monoliths are generally easier to build and deploy than microservices because they need fewer tools and less infrastructure.
- Performance: communication between modules in a monolith is usually faster than between microservices because it doesn’t require network communication.
- Management: modular monoliths can be easier to manage because you don’t have to manage multiple instances of services and ensure they interact well.
- Testability: unit and integration testing can be easier in a modular monolith due to the absence of inter-service communication.
Cons of a Modular Monolith
However, like any architecture, a modular monolith is not a reliable solution to certain problems, such as:
- Scalability: scaling modular monoliths is less flexible than scaling microservices because it involves the whole system rather than a specific service or module.
- Technological flexibility: because modular monoliths are built into a single codebase, they can limit the number of different technologies that can be used for each module.
- Resilience: because modular monolith modules share a single codebase and the same deployment and execution cycle, errors or performance issues in one module can affect the whole system. This makes the system less resilient overall.
In the following sections of this post, we’ll compare the two styles of architecture based on Architectural Drivers and explain how to choose the best architecture for your project.
Microservices and Modular Monolithic Architecture: Comparison Based on Architectural Drivers
Now that we’ve learned about microservices and modular monolithic architectures, we’ll look at Architectural Drivers to help us decide.
To do this, let’s start by figuring out the most important factors that might affect our decision about how to design the new system and which might significantly impact its qualities and performance.
The table below summarizes the main differences between microservice and modular monolithic architectures. It takes into account the various criteria derived from the Architectural Drivers, such as scalability, the technical skills required, and the potential financial costs associated with the infrastructure. It also shows several disadvantages of these architectures, such as the complexity of integration testing, dependency management, infrastructure management, deployment, and adaptability to growth.
|Architectural Drivers||Microservices||Modular Monoliths|
|Functional requirements||(+) Flexibility, (+) Independence of functions||(+) Data coherence and consistency, (+) Internal function performance|
|Quality attributes||(+) Scalability, (+) Resilience||(+) Overall performance, (+) Ease of maintenance, (-) Scalability of the whole application|
|Adaptability and upgradability||(+) Ease of adding and changing services, (+) No impact on the other services||(+) Ease of adding and changing modules, (-) Need to revise the entire code sometimes for some changes|
|Size and organization||(+) Independent teams, (+) Teams divided into squads||(+) Centralized teams|
|Technical skills||(+) Continuous integration, (+) Independent service management, (+) Versioning of services, (+) Communication protocols||(+) Modularity, (+) Loose coupling|
|Infrastructure and costs||(-) Potentially more expensive, (-) Complexity of infrastructure management, (-) More complex deployment||(+) Generally lower infrastructure and maintenance costs, (-) Less adaptable to growth|
|Testability||(-) Complexity of integration tests, (-) Need for specific mocks and tools||(+) Simplified unit and integration testing|
|Dependency management||(-) Risk of managing multiple service versions and dependencies between services||(+) Simplified dependency management, (-) Risk of rigid dependencies|
Note: This table can be used as a quick reference to compare the pros and cons of each architecture based on various factors.
The second table below shows a detailed comparison between microservices architectures and modular monolith architectures in terms of how they meet specific selection criteria, such as scalability, complexity, reliability, resilience, flexibility, and upgradability:
|Selection criteria||Microservices||Modular Monoliths|
|Scalability||(*) Easy horizontal scalability, (+) Granular load balancing||(-) Limited scalability, (+) Vertical scalability|
|Complexity||(-) Service and infrastructure management complexity, (-) More complex deployment and integration||(+) Easier code management, (+) Easier integration and deployment|
|Reliability and resilience||(+) Isolation of failures, (+) Rapid recovery from service failure||(-) One failure can affect the whole system, (+) Overall system stability|
|Flexibility and upgradability||(+) Ease of adding and changing services, (+) No impact on the other services||(+) Ease of adding and changing modules, (-) Need to revise the entire code sometimes for some changes|
Note: This explanation doesn’t go into detail about some elements of microservice and modular monolithic architectures. This is because the aim is to explain the general approach rather than provide an exhaustive list of all factors. You can also consider other things in your analysis, such as development and deployment time and cross-functional integration (module vs. service).
How to Choose an Architecture: The Decision-Making Method
A general approach can be followed when choosing the right architecture for a project. This makes decision-making easier. This approach can be refined later based on the required analysis level and how needs change.
Here is a suggested six-step approach:
- Identify the project’s Architectural Drivers. These define the functional and non-functional needs and requirements.
- Identify and evaluate different architecture types by looking at their pros and cons.
- Identify and rank the selection factors based on the project’s Architectural Drivers and how they affect the project.
- Assess the available resources, such as the development team size, the team members’ skills and expertise, and the tools and technologies available.
- Identify and assess the risks associated with each architecture to determine their potential impact on the project.
- Use all the information you have gathered to make an informed decision about the right architecture for the project.
|Example/context||Key factors||Chosen architecture||Reason for decision|
|Order management for an e-commerce site||· High complexity
· High availability and performance requirements
· Large budget
· Critical reliability and resilience
· Adaptability and upgradability
· Microservices development team skills
|Microservices||Microservices are a good fit for the project’s need for high availability and performance and its required complexity and adaptability. The team also has the required microservices development skills, and the high budget means they can invest in an infrastructure that can handle the critical reliability and resilience requirements.|
|Small business project management application||Low complexity
Modest performance and availability requirements
Modular monolith development team skills
Considerable reliability and resilience
Ease of maintenance and responsiveness to changes
Flexibility and upgradability
|Modular monolith||The reason for choosing a modular monolith is the project’s simplicity and low complexity, as well as the modest performance and availability requirements. In addition, the team has expertise in developing modular monoliths, which will make maintenance and responding to changes easier. Finally, choosing a modular monolith ensures high reliability and resilience on a tight budget.|
Essential Additions to a Sound Software Architecture
It’s important to adopt good habits and take the right approach when choosing the perfect software architecture for your project, but this alone doesn’t guarantee your project’s success. Consideration of other factors alongside this approach is recommended if you want your project to have the best chance of succeeding and to ensure the quality and durability of your software architecture:
- Regular architecture reviews and assessments: it’s vital to review and evaluate the architecture regularly to ensure it still meets your current and future needs. These reviews help identify areas for improvement, possible problems, and streamlining opportunities.
- Using metrics and monitoring tools: these can help you understand how the architecture is performing, identify any bottlenecks, and make changes to improve it. Tools such as Azure Monitor, Application Insights, and Azure Log Analytics can provide valuable insights into your system’s health and performance.
- Documentation of architectural decisions and reasons: this involves recording the choices and reasons that led to the design and development of a software architecture. This documentation is crucial to make sure that the decisions made are understood and followed up on and to ensure the quality and durability of your architecture. It needs to be kept up to date and accessible to all team members to reduce mistakes and misunderstandings when the system undergoes maintenance or upgrades.
- Adopt good development practices: it’s important to adopt good development practices, such as Clean Code and the SOLID principles, to ensure the quality, maintainability, and upgradability of your code. Design templates can be used to support and facilitate implementation of the software architecture.
- Testing: you need a rigorous testing policy in place to ensure the quality and durability of your software architecture and the reliability of your application. Unit, integration, and load tests ensure that each software component is working correctly, and that the system as a whole is coherent and working well. Security testing can be performed to find and fix security vulnerabilities in the application. This helps protect your application from malicious attacks.
- CI/CD automation: when these processes are automated, code problems can be found and fixed early before they become serious issues. Automation can also reduce deployment risks and improve update times by enabling faster and more regular deployments.
By thinking about these key factors, you can increase the chances of your project succeeding and ensure the quality and durability of your software architecture.
Software Architecture: Key Takeaways
Software architecture is fundamental in developing quality, high-performance, scalable, and easy-to-maintain software. The decisions made during the design phase have a considerable impact on the project’s success, and some can be irreversible or have serious consequences.
For this reason, it’s essential to take the time to analyze the project’s requirements and constraints, compare the various available architecture options, and select the one that works best in the specific situation. Using an Architectural Drivers-based approach lets you make more informed and structured decisions, with a focus on the most important aspects of the project.
It’s also important to note that the software architecture alone cannot guarantee software quality. Other factors, such as code, testing, and documentation quality, are also important to ensure software quality.
In short, there is no one-size-fits-all solution for projects, and each project will have its own requirements and challenges. As a result, the architecture must be continually reviewed to ensure it still meets the project’s changing needs.
Want to learn more about Craftsmanship? Read our Craft Month series of posts:
- Is the Craft Still Relevant?
- How to Build an Infrastructure with Terraform?
- Craft and PowerShell: Why Software Engineering Practices Need to Be Applied to Infrastructure
- PySpark Unit Test Best Practices
- Telemetry: Ensuring Code That Works
- How to Boost Your Apps’ Performance with Asyncio: A Practical Guide for Python Developers