Software architecture is the set of decisions the software architect makes.
"What decisions does the software architect make?"
The architecturally significant ones.
"What is architecturally significant?"
The architect decides!
"A tautology!" you protest.
Ah, think about it, I counter.
Software architecture is commonly defined in terms of components and connectors (see Definitions of Software Architecture). Components are identified and assigned responsibilities that client components interact with through "contracted" interfaces. Component interconnections specify communication and control mechanisms, and support all component interactions needed to accomplish system behavior.
In creating architectures, we address
- system decomposition into components, subsystems, sub-assemblies or "chunks" (Ulrich and Eppinger, 2004), paying attention to development productivity, and flexibility or extensibility requirements associated with accommodating future functionality at a reasonable cost of change. A good decomposition satisfies the principle of loose coupling between components (or pieces), facilitated by clean interfaces, simplifying the problem by dividing it into reasonably independent pieces that can be tackled separately.
- Once broken down into pieces, we need to ask:
- Do we have all the necessary pieces? The structure must support the functionality or services required of the system. Thus, the dynamic behavior of the system must be taken into account when designing the architecture. We must also have the necessary infrastructure to support these services.
- Do the pieces fit together? This is a matter of interface and relationships between the pieces. But good fit—that is fit that maintains system integrity—also has to do with whether the system, when composed of the pieces, has the right properties.
- cross-cutting concerns. We refer to broad-scoped qualities or properties of the system as cross-cutting concerns, because their impact is diffuse or systemic. It may be a matter of preferring not to isolate these concerns because the decomposition is being driven by other concerns, or it may be that no matter how you might “slice-and-dice” the system, multiple parts are going to have to collaborate to address these cross-cutting concerns. At any rate, to effectively address cross-cutting concerns, they must be approached first at a more broad-scoped level. Many system qualities (also known as non-functional requirements or system properties, often specified in service level agreements) are of this nature. To make the picture more complicated, the system qualities may conflict, so that trade-offs have to be made taking into account the relative priorities of the system qualities.
An architecture is not a simple flat view of the component topology, though an architecture diagram showing the components and relationships among them is a central thinking and communicating tool for the architects and the development team, and others they partner with. Our architecture needs to include:
- Meta-architecture: the architectural vision, style, principles, key communication and control mechanisms, and concepts that guide the team of architects in the creation of the architecture.
- architectural views: Just as building architectures are best envisioned in terms of a number of complementary views or models, so too are software architectures. In particular, structural views help document and communicate the architecture in terms of the components and their relationships, and are useful in assessing architectural qualities like extensibility. Behavioral views are useful in thinking through how the components interact to accomplish their assigned responsibilities and evaluating the impact of what-if scenarios on the architecture. Behavioral models are especially useful in assessing run-time qualities such as performance and security. Execution views help in evaluating physical distribution options and documenting and communicating decisions.
In creating these views, we pay attention to:
- architectural patterns: structural patterns such as layers and client/server, and mechanisms such as brokers and bridges.
- key architectural design principles including abstraction, separation of concerns, postponing decisions, and simplicity, and related techniques such as interface hiding and encapsulation.
- design of architectural mechanisms, where mechanisms deal with the interaction of components to achieve specific system capabilities.
- system decomposition principles and good interface design.
Software architecture must be distinguished from lower-level design (e.g., design of component internals and algorithms) and implementation, on the one hand, and other kinds of related architectures, on the other. For instance, software architecture is not the information (or data) model, though it uses the information model to get type information for method signatures on interfaces, for example. It is also not the architecture of the physical system, including processors, networks, and the like, on which the software will run. However, it uses this information in evaluating the impact of architectural choices on system qualities such as performance and reliability. More obviously, perhaps, it is also not the hardware architecture of a product to be manufactured. While each of these other architectures typically have their own specialists leading their design, these architectures impact and are impacted by the software architecture, and where possible, should not be designed in isolation from one another. This is the domain of system architecting. (As an interesting aside, our software architecting process has usefully been applied, without the software modeling specifics, to system, hardware and organization architecting.)