

Discover more from Tech World With Milan Newsletter
How do you test your software architecture?
This week’s issue brings to you the following:
Testing your software architecture by using fitness functions
Most Common Software Architecture Styles
Bonus: Cloud Monitoring Cheat Sheet
So, let’s dive in.
Postman's VS Code Extension (Sponsored)
Postbot is now available across Postman with enhanced capabilities! The latest refresh of Postbot now offers a consistent, conversational interface available to you across your workspace. Learn how you can leverage Postbot throughout Postman to get contextual assistance.
A standard problem development organizations face is that code implementations often need to be more consistent with the original design and architecture. The problem is common enough, especially on large projects.
Software architecture is essential for codebases' comprehensibility, changeability, and adherence to software quality goals. Regarding the codebase, three significant software architecture goals are maintainability, replaceability, and extensibility.
To keep a software system in good shape, you must ensure that it is modular and that interdependencies are as small and correct as possible, leading to high cohesion and loose coupling. These goals can be met by introducing specific patterns and code conventions that are documented and communicated by and to the entire development team that has agreed to them.
We can use Fitness functions to ensure that our architectural goals are met. They are promoted by Neal Ford, Rebecca Parsons, and Patrick Kua in the book "Building Evolutionary Architectures," With fitness functions, we can write tests that measure a system's alignment with architectural goals (attributes such as security, resilience, or stability) in a similar fashion to TDD. Such functions should be drafted in a testing framework and included in appropriate CI/CD pipelines.
In Building Evolutionary Architecture, architectural fitness functions are defined as:
Any mechanism that performs an objective integrity assessment of some architectural characteristic or combination of architectural characteristics.
Example of Architectural Fitness Functions
One example of such a function would be in the context of a web application, where one of our architectural goals is that our app should respond to user requests within 200ms (latency and performance). Here, we measure the response time for an API endpoint. If we want to automate it, we can put this function into your CI/CD pipeline to run automatically whenever new code is pushed.
Also, some tools can test that a code implementation is consistent with the defined architecture. ArchUnit is a small, simple, extensible, open-source Java testing library for verifying predefined application architecture characteristics and architectural constraints. At the same time, NetArchTest is a fluent API for .NET Standard that can enforce architectural rules in unit tests.
Here is an example in NetArchTest of how we can enforce layered architecture and some specific rules. You can test these rules separately and assert the result (with Assert.True
).
Software Architecture Styles
Software architecture styles are the foundational blueprints for constructing various software systems, ensuring they meet specific requirements and quality attributes. By adhering to a suitable architecture style, organizations can ensure that their software systems are built to align with their strategic goals, accommodate future changes, and are resilient in the face of evolving technological landscapes and user demands.
An architectural pattern, on the other hand, communicates a fundamental organizational structure for software systems. By selecting the appropriate patterns for your issue, you can avoid creating anything from scratch and potentially dangerous traps that might arise if you try to devise a novel solution.
Here are the most common architectural styles:
Monolithic: Builds the entire application as a single unit, where all functionalities and components are managed and served from one place. Examples of monolithic architectures are Onion and Layered.
Pros: simple to implement, enhanced maintainability, as issues or updates can be addressed in specific modules or layers without affecting the entire system, and improved reusability and scalability, as modules can be reused in different projects and layers can be independently scaled or modified.
Cons: performance issues due to data transmission overhead between layers and complexity in managing and ensuring interaction and consistency among various modules and layers.
Service-oriented (SOA): Divides a system into individual services, each providing specific functionalities and allowing them to communicate and interact, promoting reusability and easier management of each service independently. Examples of SOA architectures are Microservices, Broker, and Serverless.
Pros: allows software to adapt to changes quickly, can work well as things scale up, and lets different software systems talk to each other smoothly. Services can also be reused in different areas, saving time and effort.
Cons: it can be tricky because managing all the different services and how they interact can get complex. It might slow down system performance due to the extra steps in communication between services, but it can also be hard to debug if an error happens.
Component-Based: The software is built using different modular components, each providing a specific functionality, and these components can be easily replaced, updated, or modified without affecting the entire system. Examples of Component-Based architectures are Microkernel, Object-Oriented, and Plug-in architectures.
Pros: allows for flexibility in system evolution, as components can be added or updated independently, and promotes reusability, as components can be employed across various projects. It also enables parallel development and potentially reduces development costs and time.
Cons: introduces challenges such as ensuring coherent and reliable interactions among components, which can be complex and error-prone.
Distributed Systems: Divides and manages the software components across multiple machines or networks to provide a unified service, enhancing scalability and reliability. Examples of Distributed Systems are Peer-to-Peer and Space-based architectures.
Pros: the system can efficiently manage loads by distributing them across various nodes, reducing the risk of single points of failure and often providing robustness against faults or disruptions.
Cons: introduce complexities in ensuring data consistency, synchronization, and integrity across all nodes, especially in scenarios of network partitions or failures.
Event-Driven: Designed to respond to events or messages, where components perform actions in response to receiving specific notifications, making the system reactive and capable of handling asynchronous operations. Examples of Event-Driven Architectures are Publish-Subscribe and Event-Driven Architectures.
Pros: allows for the decoupling of components, as producers and consumers of events do not need to interact directly, enhancing scalability and maintainability.
Cons: managing and debugging systems in event-driven architectures can be complex due to the asynchronous and often non-deterministic nature of event processing.
Interpreter: Involves translating high-level code into machine code line by line, executing it directly rather than compiling it first, providing flexibility but often at the cost of performance. These architectures include Python Interpreter, JavaScript Engine (such as V8), JVM, etc.
Pros: it allows developers to run code on various hardware platforms without requiring modification, and it often provides more straightforward error handling and debugging since the code is executed line-by-line, making it easier to identify and resolve issues.
Cons: exhibit slower performance than compiled languages due to the overhead of interpreting code on the fly.
Data-centric: Prioritizes the management and utilization of data, ensuring that data integrity, storage, and retrieval are optimized and the system’s functionalities are built around efficient data processing. Examples of Data-centric architectures are CQRS, Event-Sourcing, Kappa, and Lambda architectures.
Pros: ensure data consistency across systems, and they can facilitate detailed auditing and historical data analysis, particularly in the case of Event-Sourcing. They also allow for the segregation of read and write workloads, enhancing performance and scalability in certain use cases.
Cons: they can be complex and may introduce additional overhead regarding system design, development, and maintenance. Ensuring data consistency and managing eventual consistency in distributed environments can be challenging.
Check the complete list here:
Each architectural style offers unique advantages and may be chosen based on the specific needs, challenges, and context of the software being developed. We should also consider that all of these styles are not mutually exclusive, as the architecture of your solution can be multidimensional (e.g., component-based, layered, and event-driven) or include different architectural patterns.
Bonus: Cloud Monitoring Cheat Sheet
Check out the Cloud Monitoring Cheat Sheet for all significant Cloud providers (AWS, GCP, Azure, and OCI).
When we talk about monitoring, we cover the following aspects:
Data Collection: Gathering information from various sources to monitor the performance and health of cloud resources.
Data Storage: Storing the collected monitoring data in a repository or database for future reference and analysis.
Data analysis: Examining the stored monitoring data to identify patterns, anomalies, or insights about the cloud environment.
Alerting: Receiving notifications when specific conditions or thresholds are met or exceeded.
Visualization: Representing monitoring data graphically, such as through charts or dashboards, to make it easier to understand.
Reporting: Generating summaries or detailed monitoring data reports to ensure adherence to policies or regulations.
Automation: Using software to automatically perform tasks or actions based on monitoring data without manual intervention.
Integration: Combining monitoring tools or data with other systems or applications to enhance functionality.
Feedback Loops: Processes where the results or outcomes from monitoring are used to make improvements or adjustments to the cloud environment.
Check the complete Cloud Product mapping in the following GitHub repository.
More ways I can help you
1:1 Coaching: Book a working session with me. 1:1 coaching is available for personal and organizational/team growth topics. I help you become a high-performing leader 🚀.
Promote yourself to 16,000+ subscribers by sponsoring this newsletter.