The discipline borrowed its name from the architecture of buildings, and the analogy is genuinely instructive on three points:
And it is instructive on a fourth: architectural styles arose in building architecture as the accumulated experience of previous generations of architects and building-dwellers — a set of constraints put upon development in order to elicit particular desirable qualities. Hold on to that definition; Chapter 3 uses exactly the same words for software.
But the module also insists on where the analogy breaks:
The nature of the medium is fundamentally different. Software is far more malleable than physical building materials, offering types of change that are unthinkable in a physical domain. As a follow-up, the building analogy is a poor source of ideas for dealing with change, since buildings accommodate change with difficulty. Every time you hear “you cannot change the foundations once they are poured”, remember that in software you very often can — and the whole of Part III of this course is about making that possible.
There is no single definition or viewpoint in the literature. The course works with two, and they are complementary rather than competing.
| Source | Definition |
|---|---|
| [SAP] Bass et al., Software Architecture in Practice | “The software architecture of a system is the set of structures needed to reason about the system. These structures comprise software elements, relations among them, and properties of both.” |
| [FSA] Richards & Ford, Fundamentals of Software Architecture | “A software architecture consists of the structure of the system, combined with architecture characteristics or qualities the system must support, architecture decisions, and finally design principles.” |
The second definition is the more operational one, because it names four distinct things that you must be able to tell apart.
The book’s own example: if an architect is asked to describe an architecture and answers “it is a microservices architecture”, that architect is only talking about the structure of the system. Knowledge of the architecture characteristics, the architecture decisions and the design principles is also needed to fully understand the architecture. The difference between a decision and a principle is likewise examinable: a decision is a hard-and-fast rule that constrains what is and is not allowed; a design principle is a guideline.
[SAP] uses an analogy with the human body: the same organism admits several different physiological structures — skeletal, circulatory, nervous — and no single one of them is the body. Software systems are the same. There are three kinds of software structures.
Runtime view of the system. It focuses on the way the elements interact with each other at runtime to carry out the system’s functions, describing how the system is structured as a set of elements that have runtime behaviour (components) and interactions (connectors).
C&C structures answer questions such as: what are the major executing components and how do they interact at runtime? What are the major shared data stores? Which parts of the system are replicated? How does data progress through the system? Which parts can run in parallel? Can the system’s structure change as it executes, and if so how?
By extension, these structures are crucially important for asking questions about the system’s runtime properties — performance, security, availability.
Static view of the system. It describes how a system is structured as a set of modules, where a module is an implementation unit — a code or data unit, including packages, classes and layers. Typically modules are assigned specific computational responsibilities and are the basis of work assignments for programming teams.
Module relations include uses, generalization (“is-a”) and is-part-of; UML notation is the usual vehicle. Module structures answer: what is the primary functional responsibility assigned to each module? What other software elements is a module allowed to use? What does it actually use and depend on? Which modules are related by generalization or specialization?
Examples of module structures: decomposition structure, uses structure, layer structure, generalization structure, data model. In a layer structure a layer is an abstract “virtual machine” providing a cohesive set of services through a managed interface; in a strictly layered system a layer may only use a single adjacent layer.
Allocation structures establish the mapping from software structures to the system’s non-software structures — such as its organisation, or its development, test and execution environments.
Note that the third structure is the one that becomes decisive in Part III: whether two pieces of code are in the same deployment unit or on opposite sides of a network is an allocation fact, and it changes everything about the qualities of the system.
Functionality is the ability of the system to do the work for which it was intended — the system’s capabilities, services and behaviour, defined by functional properties or requirements.
Software architectures can help satisfy functional requirements through effective compositions of well-defined components and connectors. But the module states the crucial negative claim twice, and it is worth reading slowly:
Functionality per se does not determine architecture. Given a set of required functionality, there is no end to the architectures you could create to satisfy it. Functionality is independent of any particular structure — but it is achieved by assigning responsibilities to architectural elements. In terms of architectural design, therefore, it is the allocation of functionality to elements, rather than the functionality per se, that matters.
Module 1.3 sharpens this into a design criterion. We can have all the functionality of a system coded in a single enormous module, or neatly distributed across many smaller, highly cohesive modules; externally the system will look and work the same way if you consider only functionality. What matters is what happens when we want to change it: in the first case changes will be difficult and costly, in the second much easier and cheaper. Hence:
A good architecture is one in which the most common changes are localised in a single or a few elements, and hence easy to make.
When designing an architecture we need to consider at least the primary functionality: functionality that is critical to achieve the business goals that motivate the development of the system. Other criteria for primary functionality: it implies a high level of technical difficulty, or it requires the interaction of many architectural elements. As a rule of thumb, approximately 10 per cent of your use cases or user stories are likely to be primary.
Three names circulate for closely related notions, and the course uses them near-interchangeably while noting their nuances.
| Term | Definition used in the course |
|---|---|
| Architecture characteristic [FSA] | Defines the success criteria of a system, generally orthogonal to the functionality. The characteristics do not require knowledge of the functionality of the system, yet they are required for the system to function properly. Colloquially: the “-ilities”. |
| Non-functional property (NFP) | A constraint on the manner in which the system implements and delivers its functionality. Important NFPs include security, reliability, availability, efficiency, scalability and fault tolerance. |
| Quality attribute (QA) [SAP] | A measurable or testable property of a system used to indicate how well the system satisfies the needs of its stakeholders beyond the basic function of the system. |
An architecture characteristic meets three criteria [FSA]:
Methodologically, the point of naming them early is traceability: software engineering methodology stresses doing the correct things from the very beginning of the process in order to curb development and maintenance costs — codifying NFPs such as availability, security and fault tolerance early on in the architectural models, and maintaining their traceability across the system’s artefacts and throughout its lifespan.
There are multiple ways to characterise or categorise qualities:
The last split is the one the course uses operationally:
| Runtime | Development time |
|---|---|
| Availability · Performance · Safety · Energy efficiency · Security · Usability · Efficiency · Adaptability · Scalability and heterogeneity · Dependability · Complexity | Deployability · Integrability · Modifiability · Testability |
The broader catalogue of software qualities from [FSA] adds: correctness, reliability, robustness, verifiability, maintainability, reparability, evolvability, reusability, portability, understandability, interoperability, productivity, timeliness, visibility.
Identifying the driving quality attributes is one of the first steps in creating an architecture, or in determining the validity of an existing one. It requires the architect not only to understand the domain problem but to collaborate with problem-domain stakeholders to determine what is truly important from a domain perspective: an architect uncovers architecture characteristics by extracting them from domain concerns, requirements and implicit domain knowledge.
Quality attributes have been studied since the 1970s, with a variety of taxonomies and definitions published. Two problems persisted:
Quality Attribute Scenarios (QAS) are the principled approach introduced to tackle both. A QAS introduces a common form to specify all QA requirements as scenarios that are testable and unambiguous, not sensitive to whims of categorisation, and that provide regularity in how we treat all quality attributes.
| Part | Definition | Notes from the module |
|---|---|---|
| Stimulus | An event arriving at the system or the project | An event to the performance community, a user operation to the usability community, an attack to the security community. For developmental qualities: a stimulus for modifiability is a request for a modification; for testability, the completion of a unit of development. |
| Stimulus source | Some entity — a human, a computer system, any other actor — must have generated the stimulus | The source may affect how the stimulus is treated by the system. |
| Artifact | The target the stimulus arrives at | Often captured as just “the system”, but it is helpful to be more precise: a failure in a data store may be treated differently from a failure in the metadata store; modifications to the UI may have faster response times than modifications to the middleware. |
| Environment | The set of circumstances in which the scenario takes place | Often a runtime state — overload condition, normal operation, a specific mode. But it can also refer to states in which the system is not running at all: in development, in testing, refreshing its data, recharging its battery between runs. |
| Response | The activity that occurs as a result of the arrival of the stimulus | The responsibilities that the system (runtime qualities) or the developers (development-time qualities) should perform. |
| Response measure | The response must be measurable so that the scenario can be tested | For performance: latency or throughput. For modifiability: the labour or wall-clock time required to make, test and deploy the modification. |
Performance scenarios are about time and the system’s ability to meet timing requirements: when events occur — interrupts, messages, requests from users or other systems, clock events marking the passage of time — the system, or some element of it, must respond in time. Performance is often linked to scalability, that is, increasing the system’s capacity for work while still performing well.
Architectural design starts by gathering architectural drivers. Quality attributes are one class of driver; the module names three more.
Among all the requirements, ASRs are those that have particular importance with respect to the software architecture: the most important functionality and the constraints that need to be taken into account, defining the main quality attributes. Put sharply: the set of properties that, if not satisfied by your system, will cause the system to be a failure. ASRs are gathered from requirements documents, by interviewing stakeholders, and by understanding the business goals.
Concerns encompass additional aspects that must be considered as part of architectural design but that are not expressed as traditional requirements. Four types:
| Type | Examples |
|---|---|
| General concerns | Broad issues: establishing an overall system structure, allocation of functionality to modules, allocation of modules to teams, organisation of the code base, startup and shutdown, supporting delivery, deployment and updates |
| Specific concerns | More detailed system-internal issues common across large numbers of applications: exception management, dependency management, configuration, logging, authentication, authorization, caching |
| Internal requirements | Requirements usually not specified explicitly in requirement documents, because customers seldom express them: aspects that facilitate development, deployment, operation or maintenance |
| Issues | Results of analysis activities such as a design review, so they may not be present initially — e.g. an architectural evaluation may uncover a risk requiring changes to the current design |
The design process must consider the list of possible constraints on development: the decisions over which architects have little or no control. Mandated technologies; other systems your system must interoperate or integrate with; laws and standards that must be complied with; the abilities and availability of your developers; non-negotiable deadlines; backward compatibility with older versions.
Constraints can be technical (“the use of open source technologies”) or non-technical (“the system must be compliant with GDPR”, “the system must be delivered by 22 November”).
The community has evolved, over decades of dealing with quality attributes in practice, a body of generally accepted design principles that guide us towards high-quality designs with predictable outcomes. These abstract principles lead to concrete reusable realisations — design concepts, the building blocks from which the structures that make up the architecture are created:
Software architecture life-cycle activities are part of the software project life cycle: they are the translation of requirements into a design into an implementation.
Some level of preliminary documentation (or sketches) of the structures is created as part of architectural design; architectural documentation refers to the creation of a more formal document from those sketches — UML-based or other views such as C4 or 4+1.
How much? If the project is small and has a precedent, documentation may be minimal. If it is large, if distributed teams are collaborating, or if significant technical challenges exist, then architectural documentation will repay the effort invested. The module adds a pointed remark: while documentation is often avoided in software, it is a standard, non-negotiable deliverable in almost every other engineering discipline — in other disciplines a “blueprint” is an absolutely essential step before committing resources.
Note that only the first four are technical, and even those include ensuring compliance — which Chapter 5 will mechanise as governance and fitness functions. The remaining four are the reason the introductory module quotes the “architect elevator” image: the architect rides between the penthouse where strategy is decided and the engine room where code is written.
Architectural thinking is more than “thinking about the architecture”: it is seeing things with an architectural eye. Four aspects:
The nature of knowledge changes as developers transition into the architect role. A developer is characterised by technical depth; an architect by technical breadth. What someone knows is technical depth; how much someone knows is technical breadth. As the module puts it: as an architect, it is more beneficial to know that five solutions exist for a particular problem than to have singular expertise in only one.
In the traditional view, the architect analyses business requirements to extract and define the architecture characteristics, selects which patterns and styles fit the problem domain, and creates components; those artefacts are then handed off to the development team, which creates class diagrams, designs UI screens and writes and tests source code.
The problem is the unidirectional arrow passing through the virtual and physical barriers that separate the architect from the developer. Decisions an architect makes sometimes never make it to the development teams, and decisions development teams make that change the architecture rarely get back to the architect. In this model the architect is disconnected from the development teams — and as such the architecture rarely provides what it was originally set out to do.
In the modern view, both the physical and virtual barriers between architects and developers must be broken down, forming a strong bidirectional relationship. Unlike old-school waterfall approaches to static and rigid architecture, the architecture of today’s systems changes and evolves every iteration or phase of a project. A tight collaboration between the architect and the development team is essential for the success of any software project.
[SAP]: “the set of structures needed to reason about the system; these structures comprise software elements, relations among them, and properties of both.” [FSA] adds three further dimensions beyond structure: the architecture characteristics (qualities the system must support), the architecture decisions (rules for how the system should be constructed) and the design principles (guidelines rather than hard rules).
Component-and-Connector — the runtime view: which components execute, how they interact, what is replicated, what runs in parallel, how data progresses, whether the structure can change while executing. Module — the static view: what functional responsibility each implementation unit carries, what it is allowed to use, what it actually depends on, inheritance relations. Allocation — the mapping from software structures to non-software structures such as the organisation and the development, test and execution environments (deployment structure, implementation structure).
Because given a set of required functionality there is no end to the architectures that could satisfy it: functionality is independent of any particular structure. What matters architecturally is the allocation of functionality to elements. The test is change: the same functionality in one enormous module and in many cohesive modules looks identical from outside, but the cost of changing it differs enormously. Hence a good architecture is one in which the most common changes are localised in one or a few elements.
(1) It specifies a non-domain design consideration, i.e. it is domain independent; (2) it influences some structural aspect of the design; (3) it is critical or important to application success. Selecting which characteristics matter is a key responsibility of the architect.
First, that the definitions provided for quality attributes are not testable (“the system shall be highly available” cannot be checked). Second, that each attribute community developed its own vocabulary: performance speaks of events arriving, security of attacks arriving, availability of faults arriving, usability of user input — possibly all referring to the same occurrence. A QAS gives one common, testable, unambiguous form for all of them.
Stimulus, stimulus source, artifact, environment, response, response measure. For modifiability the stimulus is a request for a modification, and its source is whoever raised it (a stakeholder, a regulation, an internal team). Note that for developmental qualities the response is performed by the developers, not the system, and the response measure is typically the labour or wall-clock time needed to make, test and deploy the modification.
Yes, explicitly. The environment is the set of circumstances in which the scenario takes place; often a runtime state (normal operation, overload, a specific mode) but it can also refer to states in which the system is not running at all: when it is in development, in testing, refreshing its data, or recharging its battery between runs. The environment sets the context for the rest of the scenario.
ASRs are the subset of requirements with particular importance for the architecture — the properties that, if unsatisfied, make the system a failure. Concerns are aspects that must be considered in design but are not expressed as traditional requirements: general concerns (overall structure, allocation of modules to teams, startup/shutdown, delivery), specific concerns (exception management, logging, caching, authorization), internal requirements (things customers never state) and issues (findings from reviews). Constraints are the decisions over which architects have little or no control — mandated technologies, laws and standards, deadlines, backward compatibility — and can be technical or non-technical.
Functionality that is critical to achieve the business goals motivating the development of the system; other criteria are a high level of technical difficulty or the need for interaction among many architectural elements. As a rule of thumb, approximately 10 per cent of use cases or user stories are likely to be primary.
Architectural requirements precede architectural design, which precedes architectural documentation, which precedes both architectural evaluation and architectural implementation. Evaluation influences implementation — it is “testing the design”, performed by architects and stakeholders before the implementation stage, to ensure the decisions taken are appropriate for the critical requirements.
A developer is characterised by technical depth (what someone knows); an architect by technical breadth (how much someone knows). For an architect it is more beneficial to know that five solutions exist for a problem than to have singular expertise in only one — because the architect’s job includes understanding, analysing and reconciling trade-offs between solutions.
It fails because of the unidirectional hand-off: the architect defines characteristics, patterns and components and throws them over a virtual and physical barrier to the development team. Decisions the architect makes never reach the teams, and decisions the teams make that change the architecture never get back to the architect, so the architecture rarely provides what it set out to do. The modern view breaks both barriers and builds a bidirectional relationship: today’s architectures change and evolve every iteration, so tight collaboration is essential.