In general, four very important high-level decisions must be made when setting out to create an architecture for either a new system or a redesign of an existing system. These high-level decisions can be thought of as dimensions.
In the book Head First Software Architecture, the authors do a very good job of laying out these four fundamental dimensions.
Architecture is much more than just lines and boxes that make up a diagram to represent an architecture; many other things must be decided before breaking out your favorite diagramming tool or even deciding what architecture style to use. For example, you are hired as an architect to build a volunteer management system. This is great news, and they want to see something completed in the next six months; however, this is all they tell you. Well, we all know how that story will end—and it is not good.
Before starting to examine any of these dimensions it is a good idea to review the two software architecture laws coined by Neal Ford and Mark Richards.
The first law is: Everything in Software Architecture is a trade-off, and the second law is: Why is more important than the how.
— Neal Ford and Mark Richards Fundamentals of Software Architecture
Architectural Characteristics
Architectural characteristics describe the facets of the system you are creating an architecture for. Architectural characteristics such as scalability, recoverability, and extensibility describe what the system is expected to support outside of its business function. For example, the system should support daily backups, elastic scaling, and be easy to maintain.
Architectural characteristics are also known as non-functional requirements, quality attributes, technical requirements, and many others. In the book The Fundamentals of Software Architecture, these architectural characteristics are known as “-ilities.” This is mostly because many of the architectural characteristics end with the suffix “ility.”
Here are just a few of the most common “ilities”:
Scalability - The ability of the system to manage increases in the amount or complexity of data or users.
Performance - How well the software performs under various conditions, including speed, response times, and resource usage.
Reliability - The system's ability to perform its required functions under stated conditions for a specified period.
Availability - Ensuring that the system is operational and accessible when needed by users.
Security - Protecting the system against unauthorized access or attack, including data integrity and confidentiality.
Maintainability - Ease with which the software can be modified to correct faults, improve performance, or adapt to a changed environment.
Modularity - The degree to which a system is composed of discrete components (modules) that can be independently created, modified, replaced, or exchanged.
Reusability - The extent to which parts of the software can be used in other systems with little or no modification.
Testability - How easily the software can be tested, including the ability to verify that changes do not introduce new issues.
Interoperability - The capability of the system to interact and exchange data with other systems or components, adhering to standards or protocols.
Architectural Decisions
Architectural decisions are choices about the system's structure that have long-term effects on your architecture. These decisions create constraints and serve as guides for building the system. They include things like the type of user interface the system will have, whether it will support both a rich client and a web-based interface, what type of database is needed, whether only certain vendors are allowed, and if there are any open-source restrictions. There are many other considerations as well. For example, I worked for a company where only Microsoft Azure was allowed as a cloud provider due to a decision made by upper management.
It is very common to have many architectural decisions for a given architecture. The larger the project, the decisions can be expected.
Architectural Logical Components
Architectural logical components make up the different components of the system. Logical components perform the different functions of the system. Logical components can be namespaces, libraries, folder structures, or services as examples. System functionally is the “how” of the problem you are attempting to solve within the system.
The following is an example of how you might organize the components of a volunteer management system by namespaces.
Architectural Style
Lastly, let's talk about everyone's favorite part: what style of architecture should be used? This aspect defines the form of the architecture that will be implemented. Each architecture style has a distinct form. The authors of the Head First Software Architecture book used a great analogy: they said that an architecture style can be recognized by its form, just like the architectural style of a house can be identified by its shape.
For example, here are serval types of home architectures.
A Tudor home architecture style
A Modern home architecture style
Next are two common architecture styles.
A Layered software architecture style
An Event Driven architecture style
Of course, there are many architectural styles to select from, and it is not uncommon to see a hybrid of different styles. Many times, Microservices and Service-Based architectures include Event-Driven components.
Selecting an architecture style should be done with great care and thought as there are many things to consider. For example, if you are a startup, it would not be a great idea to start with Microservice or even a Service-Based architecture as these two types of architectures is very expensive to implement in terms of time and cost.
Here is a list of just a few architectural styles you can choose from.
Layered Architecture
Separation of Concerns: Divides the application into layers like presentation, business logic, and data access.
Dependency Flow: Typically, layers interact in a top-down manner, with higher layers depending on lower ones.
Maintainability: Easier to maintain as changes in one layer do not directly affect others.
Examples: Traditional three-tier architecture (UI, Application Logic, Data Storage).
Microkernel Architecture
Core System: Has a minimal core system (kernel) handling basic functionality.
Plugins/Extensions: Allows for plug-in components that can be added or removed without changing the core system.
Flexibility: Highly adaptable for systems needing frequent updates or customizations.
Use Case: Often used in operating systems or applications like Eclipse, where extensibility is key.
Service-Based Architecture
Services as Components: Applications are built from services which are independently deployable and maintainable.
Inter-service Communication: Services communicate over a network, typically using HTTP or messaging protocols.
Loose Coupling: Promotes independence between services, reducing impact of changes in one service on others.
Scalability: Easier to scale individual services based on demand.
Microservices Architecture
Fine-Grained Services: Breaks down applications into even smaller services, each focused on a specific business capability.
Autonomy: Each microservice can be developed, deployed, and scaled independently.
Complex Communication: Requires robust strategies for service discovery, load balancing, and data consistency.
Challenges: Increased complexity in managing many services and their interactions.
Modular Monolith
Monolithic Structure: Still a single deployable unit but organized into modules that could theoretically be services.
Code Organization: Modules are separate but within the same codebase, promoting better organization and modularity.
Easier Transition: Allows for easier refactoring into microservices if needed, without the immediate overhead of distributed systems.
Development and Deployment: Simpler initial development and deployment compared to microservices but with benefits of modularity.
Event-Driven Architecture
Event-Centric Design: System components communicate through events, which are changes in state or significant occurrences.
Asynchronous Messaging: Uses message queues or event streams for communication, promoting loose coupling.
Reactivity: Systems respond to events as they happen, allowing for real-time processing and reactions.
Scalability and Resilience: Good for handling high loads and maintaining system operation even if parts fail.
Complexity in Event Management: Requires careful management of event flows, consistency, and potential data duplication.
Each of these styles has its own advantages and is suited for different scenarios depending on project requirements, team size, and system complexity.
Summary
We explored the key considerations in software architecture, focusing on four main areas crucial for designing new systems or redesigning existing ones. We highlighted the importance of architectural characteristics, known as "ilities," like scalability and security, which describe the system's non-functional requirements. We also looked at architectural decisions that shape the system's structure and set constraints, along with logical components that define the functional organization. Finally, we discussed architecture styles, recommending careful selection based on project needs, as each style offers unique forms and trade-offs.
The four dimensions in software architecture are:
Architectural Characteristics: These describe the non-functional requirements of a system, such as scalability, performance, reliability, and security. They define what the system is expected to support beyond its business functions.
Architectural Decisions: These are choices about the system's structure that have long-term effects, such as the type of user interface, database, and technology stack. They create constraints and guide the system's development.
Architectural Logical Components: These are the different components that make up the system, performing various functions. They can be namespaces, libraries, or services, representing the "how" of solving the problem within the system.
Architecture Style: This defines the form of the architecture to be implemented, such as Layered, Microservices, or Event-Driven. Each style has distinct characteristics and trade-offs, influencing the system's design and implementation.
References
Neal Ford, and Mark Richards. Fundamentals of Software Architecture: An Engineering Approach. O'Reilly Media, 2020.
Neal Ford, Mark Richards and Raju Gandhi. Head First Software Architecture. O'Reilly Media, 2024.