Builders, musicians, writers, computer designers, network designers, and software developers all use the term architecture, as do others (ever hear of a food architect?), yet each produces different results. A building is very different from a symphony, but both have architectures. Further, all architects talk about beauty in their work and its results. A building architect might say that a building should provide an environment suitable for working or living, and that it should be beautiful to behold; a musician that the music should be playable, with a discernible theme, and that it should be beautiful to the ear; a software architect that the system should be friendly and responsive to the user, maintainable, free of critical errors, easy to install, reliable, that it should communicate in standard ways with other systems, and that it, too, should be beautiful.
This book provides you with detailed examples of beautiful architectures drawn from the fields of computerized systems, a relatively young discipline. Because we are young, we have fewer examples to emulate than fields such as building, music, or writing, and therefore we need them even more. This book intends to help fill that need.
Before you proceed to the examples, we would like you to consider what an architecture is and what the attributes of a beautiful architecture might be. As you will see from the different definitions of architecture in this chapter, each discipline has its own definition, so we will first explore what is common among architectures in different disciplines and what problems one tries to solve with an architecture. Particularly, an architecture can help assure that the system satisfies the concerns of its stakeholders, and it can help deal with the complexity of conceiving, planning, building, and maintaining the system.
We then proceed to a definition of architecture and show how we can apply that definition to software architecture, since software is central to many of the later examples. Key to the definition is that an architecture consists of a set of structures designed to let the architects, builders, and other stakeholders see how their concerns are satisfied.
We end this chapter with a discussion of the attributes of beautiful architectures and cite a few examples. Central to beauty is conceptual integrity—that is, a set of abstractions and the rules for using them throughout the system as simply as possible.
In our discussion we will use “architecture” as a noun to denote a set of artifacts, including documentation such as blueprints and building specifications that describe the object to be built, wherein the object is viewed as a set of structures. The term is also used by some as a verb to describe the process of creating the artifacts, including the resulting work. As Jim Waldo and others have pointed out, however, there is no process that you can learn that guarantees you will produce a good system architecture, let alone a beautiful one (Waldo 2006), so we will focus more on artifacts than process.
Architecture: “The art or science of building; esp. the art or practice of designing and building edifices for human use, taking both aesthetic and practical factors into account.”
In all disciplines, architecture provides a means for solving a common problem: assuring that a building, or bridge, or composition, or book, or computer, or network, or system has certain properties and behaviors when it has been built. Put another way, the architecture is both a plan for the system so that the result can have the desired properties and a description of the built system. Wikipedia says: “According to the earliest surviving work on the subject, Vitruvius’ ‘On Architecture,’ good building should have Beauty (Venustas), Firmness (Firmitas), and Utility (Utilitas); architecture can be said to be a balance and coordination among these three elements, with no one overpowering the others.”
We speak of the “architecture” of a symphony, and call architecture, in its turn, “frozen music.”
A good system architecture exhibits conceptual integrity; that is, it comes equipped with a set of design rules that aid in reducing complexity and that can be used as guidance in detailed design and in system verification. Design rules may incorporate certain abstractions that are always used in the same way, such as virtual devices. The rules may be represented as a pattern, such as pipes and filters. In the best case there are verifiable rules, such as “any virtual device of the same type may replace any other virtual device of the same type in the event of device failure,” or “all processes contending for the same resource must have the same scheduling priority.”
A contemporary architect might say that the object or system under construction must have the following characteristics.
It has the functionality required by the customer.
It is safely buildable on the required schedule.
It performs adequately.
It is reliable.
It is usable and safe to use.
It is secure.
It is affordable.
It conforms to legal standards.
It will outlast its predecessors and its competitors.
The architecture of a computer system we define as the minimal set of properties that determine what programs will run and what results they will produce.
We’ve never seen a complex system that perfectly satisfies all of the preceding characteristics. Architecture is a game of trade-offs—a decision that improves one of these characteristics often diminishes another. The architect must determine what is sufficient to satisfy, by discovering the important concerns for a particular system and the conditions for satisfying them sufficiently.
Common among the notions of architecture is the idea of structures, each defined by components of various sorts and their relations: how they fit together, invoke each other, communicate, synchronize, and otherwise interact. Components could be support beams or internal rooms in a building, individual instruments or melodies in a symphony, book chapters or characters in a story, CPUs and memory chips in a computer, layers in a communications stack or processors connected to a network, cooperating sequential processes, objects, collections of compile-time macros, or build-time scripts. Each discipline has its own sets of components and its own relationships among them.
In wider use, the term “architecture” always means “unchanging deep structure.”
In the face of increasing complexity of systems and their interactions, both internally and with each other, an architecture comprising a set of structures provides the primary means for dealing with complexity in order to ensure that the resulting system has the required properties. Structures provide ways to understand the system as sets of interacting components.
Each structure is intended to help the architect understand how to satisfy particular concerns, such as changeability or performance. The job of demonstrating that particular concerns are satisfied may fall to others, but the architect must be able to demonstrate that all concerns have been met.
Network architecture: the communication equipment, protocols, and transmission links that constitute a network, and the methods by which they are arranged.
When buildings are designed, constructed, or renovated, we designate key designers as “architects” and give them a broad range of responsibilities. An architect prepares initial sketches of the building, showing both external appearance and internal layout, and discusses these sketches with clients until all concerned have agreed that what is shown is what they want. The sketches are abstractions: they focus attention on the pertinent details of a particular aspect of the building, omitting other concerns.
After the clients and architects agree on these abstractions, the architects prepare, or supervise the preparation of, much more detailed drawings, as well as associated textual specifications. These drawings and specifications describe many “nitty-gritty” details of a building, such as plumbing, siding materials, window glazing, and electrical wiring.
On rare occasions, an architect simply hands the detailed plans to a builder who completes the project in accordance with the plans. For more important projects, the architect remains involved, regularly inspects the work, and may propose changes or accept suggestions for change from both the builder and customer. When the architect supervises the project, it is not considered complete until he certifies that it is in substantial compliance with the plans and specifications.
We employ an architect to assure that the design (1) meets the needs of the client, including the characteristics previously noted; (2) has conceptual integrity by using the same design rules throughout; and (3) meets legal and safety requirements. An important part of the architect’s role is to ensure that the design concepts are consistently realized during the implementation. Sometimes the architect also acts as a mediator between builder and client. There is often some disagreement about which decisions are in the realm of the architect and which are left to others, but it is always clear that the architect makes the major decisions, including all that can affect the usability, safety, and maintainability of the structure.
Software development projects need people who play the same role for software construction that traditional architects play when buildings are constructed or renovated. For software systems, however, it has never been clear exactly which decisions are the purview of the architect and which can be left to the implementers. The definition of what an architect does in a software project is more difficult than the analogous definition for building architects because of three factors: lack of tradition, the intangible nature of the product, and the complexity of the system. (See Grinter  for a portrayal of how a software architect carries out her role within a large software development organization.)
Building architects can look back at thousands of years of history to see what architects have done in the past; they can visit and study buildings that have been standing for hundreds, and sometimes a thousand years or more, and that are still in use. In software we have only a few decades of history and our designs are often not public. Furthermore, building architects have and use standards for describing the drawings and specifications that the architects produce, allowing present architects to take advantage of the recorded history of architecture.
Buildings are physical products; there is a clear distinction between the plans produced by the architects and the building produced by the workers.
On major software projects, there are often many architects. Some architects are quite specialized in disciplines, such as databases and networks, and usually work as part of a team, but for now we will write as if there were only one.
It is a mistake to think of “an architecture” as if it were a simple entity that could be described by a single document or drawing. Architects must make many design decisions. To be useful, these decisions must be documented so that they can be reviewed, discussed, modified, and approved, and then serve to constrain subsequent decision making and construction. For software systems, these design decisions are behavioral and structural.
External behavioral descriptions show how the product will interface with its users, other systems, and external devices, and should take the form of requirements. Structural descriptions show how the product is divided into parts and the relations between those parts. Internal behavioral descriptions are needed to describe the interfaces between components. Structural descriptions often show several distinct views of the same part because it is impossible to put all the information in one drawing or document in a meaningful way. A component in one view may be a part of a component in another.
Software architectures are often presented as layered hierarchies that tend to commingle several different structures in one diagram. In the 1970s Parnas pointed out that the term “hierarchy” had become a buzzword, and then precisely defined the term and gave several different examples of structures used for different purposes in the design of different systems (Parnas 1974). Describing the structures of an architecture as a set of views, each of which addresses different concerns, is now accepted as a standard architecture practice (Clements et al. 2003; IEEE 2000). We will use the word “architecture” to refer to a set of annotated diagrams and functional descriptions that specify the structures used to design and construct a system. In the software development community there are many different forms used, and proposed, for such diagrams and descriptions. See Hoffman and Weiss (2000, chaps. 14 and 16) for some examples.
The software architecture of a program or computing system is the structure or structures of the system, which comprise software elements, the externally visible properties of those elements, and the relationships among them.
“Externally visible” properties are those assumptions other elements can make of an element, such as its provided services, performance characteristics, fault handling, shared resource usage, and so on.
Architecture is a part of the design of the system; it highlights some details by abstracting away from others. Architecture is thus a subset of design. A developer focused on implementing a component of the system may not be very aware of how all the components fit together, but rather is primarily concerned with the design and development of a small number of component(s), including the architectural constraints that they must obey and the rules they can use. As such, the developer is working on a different aspect of the system design than the architect.
If architecture is concerned with the relationships among components and the externally visible properties of system components, then design will additionally be concerned with the internal structure of those components. For example, if one set of components consists of information-hiding modules, then the externally visible properties form the interfaces to those components, and the internal structure is concerned with the data structures and flow of control within a module (Hoffman and Weiss 2000, chaps. 7 and 16).
So far, we have considered architecture in general and looked at how software architecture is both similar to and different from architecture in other domains. We now turn our attention to the “how” of software architecture. Where should the architect focus her attention when she is creating the architecture for a software system?
That’s right—the first concern of a software architect is not the functionality of the system.
For example, if we offer to hire you to develop the architecture for a “web-based application,” would you start by asking us about page layouts and navigation trees, or would you ask us questions such as:
Who will host it? Are there technology restrictions in the hosting environment?
Do you want to run on a Windows Server or on a LAMP stack?
How many simultaneous users do you want to support?
How secure does the application need to be? Is there data that we need to protect? Will the application be used on the public Internet or a private intranet?
Can you prioritize these answers for me? For example, is number of users more important than response time?
Depending on our answers to these and a few other questions, you can begin sketching out an architecture for the system. And we still haven’t talked about the functionality of the application.
Now, admittedly, we cheated a bit here because we asked for a “web-based application,” which is a well-understood domain, so you already knew what decisions would have the most influence on your architecture. Similarly, if we had asked for a telecommunications system or an avionics system, an architect experienced in one of those domains would have some notion of required functionality in mind. But still, you were able to begin creating the architecture without worrying too much about the functionality. You did this by focusing on quality concerns that needed to be satisfied.
Quality concerns specify the way in which the functionality must be delivered in order to be acceptable to the system’s stakeholders, the people with a vested interest in the outcome of the system. Stakeholders have certain concerns that the architect must address. Later, we will discuss concerns that are typically raised when trying to assure that the system has the required qualities. As we said earlier, one role of the architect is to ensure that the design of the system will meet the needs of the client, and we use quality concerns to help us understand those needs.
This example highlights two key practices of successful architects: stakeholder involvement and a focus on both quality concerns and functionality. As the architect, you began by asking us what we wanted from the system, and in what priority. In a real project, you would have sought out other stakeholders. Typical stakeholders and their concerns include:
Funders, who want to know if the project can be completed within resource and schedule constraints
Architects, developers, and testers, who are first concerned with initial construction and later with maintenance and evolution
Project managers, who need to organize teams and plan iterations
Marketers, who may want to use quality concerns to differentiate the system from competitors
Users, including end users, system administrators, and the people who do installation, deployment, provisioning, and configuration
Technical support staff, who are concerned with the number and complexity of Help Desk calls
Every system has its own set of quality concerns. Some, such as performance, security, and scalability, may be well-specified, but other, often equally important concerns, such as changeability, maintainability, and usability, may not be defined with enough detail to be useful. Odd, isn’t it, that stakeholders want to put functions in software and not hardware so that they can be easily and quickly modified, and then often give short shrift to changeability when stating their quality concerns? Architecture decisions will have an impact on what kinds of changes can be done easily and quickly and what changes will take time and be hard to do. So shouldn’t an architect understand his stakeholders’ expectations for qualities such as “changeability” as well as he understands the functional requirements?
Once the architect understands the stakeholders’ quality concerns, what does she do next? Consider the trade-offs. For example, encrypting messages improves security but hurts performance. Using configuration files may increase changeability but could decrease usability unless we can verify that the configuration is valid. Should we use a standard representation for these files, such as XML, or invent our own? Creating the architecture for a system involves making many such difficult trade-offs.
The first task of the architect, then, is to work with stakeholders to understand and prioritize quality concerns and constraints. Why not start with functional requirements? Because there are usually many possible system decompositions. For example, starting with a data model would lead to one architecture, whereas starting with a business process model might lead to a different architecture. In the extreme case, there is no decomposition, and the system is developed as a monolithic block of software. This might satisfy all functional requirements, but it probably will not satisfy quality concerns such as changeability, maintainability, or scalability. Architects often must do architecture-level refactoring of a system, for example to move from simplex to distributed deployment, or from single-threaded to multithreaded in order to meet scalability or performance requirements, or hardcoded parameters to external configuration files because parameters that were never going to change now need to be modified.
Although there are many architectures that can meet functional requirements, only a subset of these will also satisfy quality requirements. Let’s go back to the web application example. Think of the many ways to serve up web pages—Apache with static pages, CGI, servlets, JSP, JSF, PHP, Ruby on Rails, or ASP.NET, to name just a few. Choosing one of these technologies is an architecture decision that will have significant impact on your ability to meet certain quality requirements. For example, an approach such as Ruby on Rails might provide the fast time-to-market benefit, but could be harder to maintain as both the Ruby language and the Rails framework continue to evolve rapidly. Or perhaps our application is a web-based telephone and we need to make the phone “ring.” If you need to send true asynchronous events from the server to the web page to satisfy performance requirements, an architecture based on servlets might be more testable and modifiable.
In real-world projects, satisfying stakeholder concerns requires many more decisions than simply selecting a web framework. Do you really need an “architecture,” and do you need an “architect” to make the decisions? Who should make them? Is it the coder, who may make many of them unintentionally and implicitly, or is it the architect, who makes them explicitly with a view in mind of the entire system, its stakeholders, and its evolution? Either way, you will have an architecture. Should it be explicitly developed and documented, or should it be implicit and require reading of the code to discover?
Often, of course, the choice is not so stark. As the size of the system, its complexity, and the number of people who work on it increase, however, those early decisions and the way that they are documented will have greater and greater impact.
We hope you understand by now that architecture decisions are important if your system is going to meet its quality requirements, and that you want to pay attention to the architecture and make these decisions intentionally rather than just “letting the architecture emerge.”
What happens when the system is very large? One of the reasons that we apply architecture principles such as “divide and conquer” is to reduce complexity and enable work to proceed in parallel. This allows us to create larger and larger systems. Can the architecture itself be decomposed into parts, and those parts worked on by different people in parallel? In considering computer architecture, Gerrit Blaauw and Fred Brooks asserted:
...if, after all techniques to make the task manageable by a single mind have been applied, the architectural task is still so large and complex that it cannot be done in that way, the product conceived is too complex to be usable and should not be built. In other words, the mind of a single user must comprehend a computer architecture. If a planned architecture cannot be designed by a single mind, it cannot be comprehended by one. (1997)
Do you need to understand all aspects of an architecture in order to use it? An architecture separates concerns so, for the most part, the developer or tester using the architecture to build or maintain a system does not need to deal with the entire architecture at once, but can interact with only the necessary parts to perform a given function. This allows us to create systems larger than a single mind can comprehend. But, before we completely ignore the advice of the people who built the IBM System/360, one of the longest-lived computer architectures, let’s look at what prompted them to make this statement.
Fred Brooks said that conceptual integrity is the most important attribute of an architecture: “It is better to have a system...reflect one set of design ideas, than to have one that contains many good but independent and uncoordinated ideas” (1995). It is this conceptual integrity that allows a developer who already knows about one part of a system to quickly understand another part. Conceptual integrity comes from consistency in things such as decomposition criteria, application of design patterns, and data formats. This allows a developer to apply experience gained working in one part of the system to developing and maintaining other parts of the system. The same rules apply throughout the system. As we move from system to “system-of-systems,” the conceptual integrity must also be maintained in the architecture that integrates the systems, for example by selecting an architecture style such as publish/subscribe message bus and then applying this style uniformly to all system integrations in the system-of-systems.
The challenge for an architecture team is to maintain a single-mindedness and a single philosophy as they go about creating the architecture. Keep the team as small as possible, work in a highly collaborative environment with frequent communication, and have one or two “chiefs” act as benevolent dictators with the final say on all decisions. This organizational pattern is commonly seen in successful systems, whether corporate or open source, and results in the conceptual integrity that is one of the attributes of a beautiful architecture.
Good architects are often formed by having better architects mentor them (Waldo 2006). One reason may be that there are certain concerns that are common to nearly all projects. We have already alluded to some of them, but here is a more complete list, with each concern phrased as a question that the architect may need to consider during the course of a project. Of course, individual systems will have additional critical concerns.
What functionality does the product offer to its users?
What changes may be needed in the software in the future, and what changes are unlikely and need not be especially easy to make in the future?
What will the performance of the product be?
How many users will use the system simultaneously? How much data will the system need to store for its users?
What interactions will the system have with other systems in the ecosystem in which it will be deployed?
How is the task of writing the software organized into work assignments (modules), particularly modules that can be developed independently and that suit each other’s needs precisely and easily?
How can the software be built as a set of components that can be independently implemented and verified? What components should be reused from other products and which should be acquired from external suppliers?
If the product will exist in several variations, how can it be developed as a product line, taking advantage of the commonality among the versions, and what are the steps by which the products in the product line can be developed (Weiss and Lai 1999)? What investment should be made in creating a software product line? What is the expected return from creating the options to develop different members of the product line?
In particular, is it possible to develop the smallest minimally useful product first and then develop additional members of the product line by adding (and subtracting) components without having to change the code that was written previously?
If the product requires authorization for its use or must restrict access to data, how can security of data be ensured? How can “denial of service” and other attacks be withstood?
Finally, a good architect realizes that the architecture affects the organization. Conway noted that the structure of a system reflects the structure of the organization that built it (1968). The architect may realize that Conway’s Law can be used in reverse. In other words, a good architecture may influence an organization to change so as to be more efficient in building systems derived from the architecture.
How, then, does a good architect deal with these concerns? We have already mentioned the need to organize the system into structures, each defining specific relationships among certain types of components. The architect’s chief focus is to organize the system so that each structure helps answer the defining questions for one of the concerns. Key structural decisions divide the product into components and define the relationships among those components (Bass, Clements, and Kazman 2003; Booch, Rumbaugh, and Jacobson 1999; IEEE 2000; Garlan and Perry 1995). For any given product, there are many structures that need to be designed. Each must be designed separately so that it is viewed as a separate concern. In the next few sections we discuss some structures that you can use to address the concerns on our list. For example, the Information Hiding Structures show how the system is organized into work assignments. They can also be used as a roadmap for change, showing for proposed changes which modules accommodate those changes. For each structure we describe the components and the relations among them that define the structure. Given the concerns on our list, we consider the following structures to be of primary importance.
components and relations: The primary components are Information Hiding Modules, where each module is a work assignment for a group of developers, and each module embodies a design decision. We say that a design decision is the secret of a module if the decision can be changed without affecting any other module (Hoffman and Weiss 2000, chaps. 7 and 16). The most basic relation between the modules is “part of.” Information Hiding Module A is part of Information Hiding Module B if A’s secret is a part of B’s secret. Note that it must be possible to change A’s secret without changing any other part of B; otherwise, A is not a submodule according to our definition. For example, many architectures have virtual device modules, whose secret is how to communicate with certain physical devices. If virtual devices are organized into types, then each type might form a submodule of the virtual device module, where the secret of each virtual device type would be how to communicate with devices of that type.
Each module is a work assignment that includes a set of programs to be written. Depending on language, platform, and environment, a “program” could be a method, a procedure, a function, a subroutine, a script, a macro, or other sequence of instructions that can be made to execute on a computer. A second Information Hiding Module Structure is based on the relation “contained in” between programs and modules. A program P is contained in a module M if part of the work assignment M is to write P. Note that every program is contained in a module because every program must be part of some developer’s work assignment.
Some of these programs are accessible on the module’s interface, whereas others are internal. Modules may also be related through interfaces. A module’s interface is a set of assumptions that programs outside of the module may make about the module and the set of assumptions that the module’s programs make about programs and data structures of other modules. A is said to “depend on” B’s interface if a change to B’s interface might require a change in A.
The “part of” structure is a hierarchy. At the leaf nodes of the hierarchy are modules that contain no identified submodules. The “contained in” structure is also a hierarchy, since each program is contained in only one module. The “depends on” relation does not necessarily define a hierarchy, as two modules may depend on each other either directly or through a longer loop in the “depends on” relation. Note that “depends on” should not be confused with “uses” as defined in a later section.
Information Hiding Structures are the foundation of the object-oriented design paradigm. If an Information Hiding Module is implemented as a class, the public methods of the class belong to the interface for the module.
concerns satisfied: The Information Hiding Structures should be designed so that they satisfy changeability, modularity, and buildability.
components and relation: As defined previously, Information Hiding Modules contain one or more programs (as defined in the previous section). Two programs are included in the same module if and only if they share a secret. The components of the Uses Structure are programs that may be independently invoked. Note that programs may be invoked by each other or by the hardware (for example, by an interrupt routine), and the invocation may come from a program in a different namespace, such as an operating system routine or a remote procedure. Furthermore, the time at which an invocation may occur could be any time from compile time through runtime.
We will consider forming a Uses Structure only among programs that operate at the same binding time. It is probably easiest first just to think about programs that operate at runtime. Later, we may also think about the uses relation among programs that operate at compile time or load time.
We say that program A uses program B if B must be present and satisfy its specification for A to satisfy its specification. In other words, B must be present and operate correctly for A to operate correctly. The Uses Relation is sometimes known as “requires the presence of a correct version of.” For a further explanation and example, see Chapter 14 of Hoffman and Weiss (2000).
The Uses Structure determines what working subsets can be built and tested. A desirable property in the Uses Relation for a software system is that it defines a hierarchy, meaning that there are no loops in it. When there is a loop in the Uses Relation, all programs in the loop must be present and working in the system for any of them to work. Since it may not be possible to construct a completely loop-free Uses Relation, an architect may treat all of the programs in a Uses loop as a single program for the purpose of creating subsets. A subset must include either the whole program or none of it.
When there are no loops in the Uses Relation, a levels structure is imposed on the software. At the bottom level, level 0, are all programs that use no other programs. Level n consists of all programs that use programs in level n–1 or below. The levels are often depicted as a series of layers, with each layer representing one or several levels in the Uses Relation. Grouping adjacent levels in Uses helps to simplify the representation and allows for cases where there are small loops in the relation. One guideline in performing such a grouping is that programs at one layer should execute approximately 10 times as quickly and 10 times as often as programs in the next layer above it (Courtois 1977).
A system that has a hierarchical Uses Structure can be built one or a few layers at a time. These layers are sometimes known as “levels of abstraction,” but this is a misnomer. Because the components are individual programs, not whole modules, they do not necessarily abstract from (hide) anything.
Often a large software system has too many programs to make the description of the Uses Relation among programs easily understandable. In such cases, the Uses Relation may be formed on aggregations of programs, such as modules, classes, or packages. Such aggregated descriptions lose important information but help to present the “big picture.” For example, one can sometimes form a Uses Relation on Information Hiding Modules, but unless all programs in a module are on the same level of the programmatic Uses hierarchy, important information is lost.
In some projects, the Uses Relation for a system is not fully determined until the system is implemented, because the developers determine what programs they will use as the implementation proceeds. The architects of the system may, however, create an “Allowed-to-Use” Relation at design time that constrains the developers’ choices. Henceforth, we will not distinguish between “Uses” and “Allowed-to-Use.”
A well-defined Uses Structure will create proper subsets of the system and can be used to drive iterative or incremental development cycles.
concerns satisfied: Producibility and ecosystem.
components and relation: The Information Hiding Module Structures and the Uses Structures are static structures that exist at design and code time. We now turn to a runtime structure. The components that participate in the Process Structure are Processes. Processes are runtime sequences of events that are controlled by programs (Dijkstra 1968). Each program executes as part of one or many Processes. The sequence of events in one Process proceed independently of the sequence of events in another Process, except where the Processes synchronize with each other, such as when one Process waits for a signal or a message from the other. Processes are allocated resources, including memory and processor time, by support systems. A system may contain a fixed number of Processes, or it may create and destroy Processes while running. Note that threads implemented in operating systems such as Linux and Windows fall under this definition of Processes. Processes are the components of several distinct relations. Some examples follow.
One Process may create work that must be completed by other Processes. This structure is essential in determining whether a system can get into a deadlock.
concerns satisfied: Performance and capacity.
In systems with dynamic resource allocation, one Process may control the resources used by another, where the second must request and return those resources. Because a requesting Process may request resources from several controllers, each resource may have a distinct controlling Process.
concerns satisfied: Performance and capacity.
Two Processes may share resources such as printers, memory, or ports. If two Processes share a resource, synchronization is necessary to prevent usage conflicts. There may be distinct relations for each resource.
concerns satisfied: Performance and capacity.
The data in a system may be divided into segments with the property so that if a program has access to any data in a segment, it has access to all data in that segment. Note that to simplify the description, the decomposition should use maximally sized segments by adding the condition that if two segments are accessed by the same set of programs, those two segments should be combined. The data access structure has two kinds of components, programs and segments. This relation is entitled “has access to,” and is a relation between programs and segments. A system is thought to be more secure if this structure minimizes the access rights of programs and is tightly enforced.
concerns satisfied: Security.
Table 1-1summarizes the preceding software structures, how they are defined, and the concerns that they satisfy.
Information Hiding Modules
Is a part of
Is contained in
Processes (tasks, threads)
Gives work to
Gets resources from
Shares resources with
Programs and Segments
Has access to
Recall that architects play a game of trade-offs. For a given set of functional and quality requirements, there is no single correct architecture and no single “right answer.” We know from experience that we should evaluate an architecture to determine whether it will meet its requirements before spending money to build, test, and deploy the system. Evaluation attempts to answer one or more of the concerns discussed in previous sections, or concerns specific to a particular system.
There are two common approaches to architecture evaluation (Clements, Kazman, and Klein 2002). The first class of evaluation methods determines properties of the architecture, often by modeling or simulation of one or more aspects of the system. For example, performance modeling is carried out to assess throughput and scalability, and fault tree models can be used to estimate reliability and availability. Other types of models include using complexity and coupling metrics to assess changeability and maintainability.
The second, and broadest, class of evaluation methods is based on questioning the architects to assess the architecture. There are many structured questioning methods. For example, the Software Architecture Review Board (SARB) process developed at Bell Labs uses experts from within the organization and leverages their deep domain expertise in telecommunications and related applications (Maranzano et al. 2005).
Another variation of the questioning approach is the Architecture Trade-off Analysis Method (ATAM) (Clements, Kazman, and Klein 2002), which looks for risks that the architecture will not satisfy quality concerns. ATAM uses scenarios, each describing a particular stakeholder’s quality concern for the system. The architects then explain how the architecture supports each of the scenarios.
Active reviews are another type of questioning approach that turns the process on its head, requiring the architects to provide the reviewers with the questions that the architects think are important to answer (Hoffman and Weiss 2000, chap. 17). The reviewers then use the existing architecture documents and descriptions to answer the questions. Finally, searching the Web for “software architecture review checklist” returns dozens of checklists, some very general and some specific to an application domain or technology framework.
All of the preceding methods help to evaluate whether an architecture is “good enough”—that is, whether it is likely to guide the developer and testers to produce a system that will satisfy the functional and quality concerns of the system’s stakeholders. There are many good architectures in systems that we use every day.
But what about architectures that are more than good enough? What if there were a “Software Architecture Hall of Fame”? Which architectures would line the walls of that gallery? The idea is not as far-fetched as you might think—in the field of software product lines, just such a Hall of Fame exists. The criteria for induction into the Software Product Line Hall of Fame include commercial success, influence on other product line architectures (others have “borrowed, copied, or stolen” from the architecture), and sufficient documentation that others can understand the architecture “without resorting to hearsay.”
What criteria would we add to these for nominees for a more general “Architecture Hall of Fame,” or perhaps a “Gallery of Beautiful Architectures”?
First, we should recognize that this is a gallery of software systems, not art, and our systems are built to be used. So, perhaps we should begin by looking at the Utility of the architecture: it should be used every day by many people.
But before an architecture can be used, it must be built, and so we should look at the Buildability of the architecture. We would look for architectures with a well-defined Uses Structure that would support incremental construction, so that at each iteration of construction we would have a useful, testable system. We would also look for architectures that have well-defined module interfaces and that are inherently testable, so that the construction progress is transparent and visible.
Next, we want architectures that demonstrate Persistence—that is, architectures that have stood the test of time. We work in an era when the technical environment is changing at an ever-increasing rate. A beautiful architecture should anticipate the need for change, and allow expected changes to be made easily and efficiently. We want to find architectures that have avoided the “aging horizon” (Klein 2005) beyond which maintenance becomes prohibitively expensive.
Finally, we would want to include architectures that have features that delight the developers and testers who use the architecture and build it and maintain it, as well as the users of the system(s) built from it. Why delight developers? It makes their job easier and is more likely to result in a high-quality system. Why delight testers? They are the ones who have to attempt to emulate what the users will do as part of the testing process. If they are delighted, it is likely that the users will be, too. Think of the chef who is unhappy with his culinary creations. His customers, who consume those creations, are likely to be unhappy, too.
Different systems and application domains offer opportunities for architectures to exhibit specific delightful features, but Conceptual Integrity is a feature that cuts across all domains and that always delights. A consistent architecture is easier and faster to learn, and once you know a little, you can begin to predict the rest. Without the need to remember and handle special cases, code is cleaner and test sets are smaller. A consistent architecture does not offer two (or more) ways to do the same thing, forcing the user to waste time choosing. As Ludwig Mies van der Rohe said of good design, “Less is more,” and Albert Einstein might say that beautiful architectures are as simple as possible, but no simpler.
Given these criteria, we propose some initial candidates for our “Gallery of Beautiful Architectures.”
The first entry is the architecture for the A-7E Onboard Flight Processor (OFP), developed at the Naval Research Laboratory (NRL) in the late 1970s, and described in Bass, Clements, and Kazman (2003). Although this particular system never went into production, it meets every other criterion for inclusion. This architecture has had tremendous influence on the practice of software architecture by demonstrating in a real-world system the separation of a design-time Information Hiding Module and Uses structures from the runtime Process Structures. It showed that information hiding could be used as a primary decomposition principle for a complex system. Since the U.S. government funded and developed the architecture, all project documentation is available in the public domain. The architecture had a well-defined Uses structure that facilitated incremental construction of the system. Finally, the Information Hiding Module structure provided clear and consistent criteria for decomposing the system, resulting in strong Conceptual Integrity. As an exemplar of embedded system software architecture, the A-7E OFP certainly belongs in our gallery.
Another architecture that we would want to include in our gallery is the software architecture for the Lucent 5ESS telephone switch (Carney et al. 1985). The 5ESS has been a global commercial success, providing core telephone network switching for networks in countries around the world. It has set the standard for performance and availability, with each unit capable of handling over one million call connections per hour with less than 10 seconds of unplanned downtime per year (Alcatel-Lucent 1999). The architecture’s unifying concepts, such as the “half call model” for managing telephone connections, have become standard patterns in the domains of telephony and network protocols (Hanmer 2001). In addition to keeping the number of call types that must be handled to 2n, where n is the number of call protocols, the half call pattern links the operating system concept of process to the telephony concept of call type, thereby providing a simple design rule and introducing a beautiful Conceptual Integrity. A development team of up to 3,000 people has evolved and enhanced the system over the past 25 years. Based on success, persistence, and influence, the 5ESS architecture is a fine addition to our gallery.
Another system to consider for inclusion in our Gallery of Beautiful Architectures is the architecture of the World Wide Web (WWW), created by Tim Berners-Lee at CERN, and described in Bass, Clements, and Kazman (2003). The WWW has certainly been commercially successful, transforming the way that people use the Internet. The architecture has remained intact, even as new applications are created and new capabilities introduced. The overall simplicity of the architecture contributes to its Conceptual Integrity, but decisions such as using a single library for both clients and servers and creating a layered architecture to separate concerns have ensured that the integrity of the architecture remains intact. The persistence of the core WWW architecture and its ability to continue to support new extensions and features certainly qualify it for inclusion in our gallery.
Our last example is the Unix system, which exhibits conceptual integrity, is widely used, and has had great influence. The pipe and filters design is a lovely abstraction that permits rapid construction of new applications.
We have gone to considerable length to describe architectures, the role of architects, and considerations that go into creating architectures, and we have offered several brief examples of beautiful architectures. We invite you now to read more detailed examples from accomplished architects in the following chapters as they describe the beautiful architectures that they have created and used.
David Parnas defined many of the structures we described in several papers, including his “Buzzword” paper (Parnas 1974). Jon Bentley was an inspiration in this work and he, Deborah Hill, and Mark Klein made many useful suggestions on earlier drafts.
Alcatel-Lucent. 1999. “Lucent’s record-breaking reliability continues to lead the industry according to latest quality report.” Alcatel-Lucent Press Releases. June 2. http://www.alcatel-lucent.com/wps/portal/NewsReleases/DetailLucent?LMSG_CABINET=Docs_and_Resource_Ctr&LMSG_CONTENT_FILE=News_Releases_LU_1999/LU_News_Article_007318.xml (accessed May 15, 2008).
Bass, L., P. Clements, and R. Kazman. 2003. Software Architecture in Practice, Second Edition. Boston, MA: Addison-Wesley.
Blaauw, G., and F. Brooks. 1997. Computer Architecture: Concepts and Evolution. Boston, MA: Addison-Wesley.
Booch, G., J. Rumbaugh, and I. Jacobson. 1999. The UML Modeling Language User Guide. Boston, MA: Addison-Wesley.
Brooks, F. 1995. The Mythical Man-Month. Boston, MA: Addison-Wesley.
Carney, D. L., et al. 1985. “The 5ESS switching system: Architectural overview.” AT&T Technical Journal, vol. 64, no. 6, p. 1339.
Clements, P., et al. 2003. Documenting Software Architectures: Views and Beyond. Boston, MA: Addison-Wesley.
Clements, P., R. Kazman, and M. Klein. 2002. Evaluating Software Architectures. Boston: Addison-Wesley.
Conway, M. 1968. “How do committees invent.” Datamation, vol. 14, no. 4.
Courtois, P. J. 1977. Decomposability: Queuing and Computer Systems. New York, NY: Academic Press.
Dijkstra, E. W. 1968. “Co-operating sequential processes.” Programming Languages. Ed. F. Genuys. New York, NY: Academic Press.
Garlan, D., and D. Perry. 1995. “Introduction to the special issue on software architecture.” IEEE Transactions on Software Engineering, vol. 21, no. 4.
Grinter, R. E. 1999. “Systems architecture: Product designing and social engineering.” Proceedings of ACM Conference on Work Activities Coordination and Collaboration (WACC ’99). 11–18. San Francisco, CA.
Hanmer, R. 2001. “Call processing.” Pattern Languages of Programming (PLoP). Monticello, IL. http://hillside.net/plop/plop2001/accepted_submissions/PLoP2001/rhanmer0/PLoP2001_rhanmer0_1.pdf.
Hoffman, D., and D. Weiss. 2000. Software Fundamentals: Collected Papers by David L. Parnas. Boston, MA: Addison-Wesley.
IEEE. 2000. “Recommended practice for architectural description of software intensive systems.” Std 1471. Los Alamitos, CA: IEEE.
Klein, John. 2005. “How does the architect’s role change as the software ages?” Proceedings of the 5th Working IEEE/IFIP Conference on Software Architecture (WICSA). Washington, DC: IEEE Computer Society.
Maranzano, J., et al. 2005. “Architecture reviews: Practice and experience.” IEEE Software, March/April 2005.
Parnas, David L. 1974. “On a buzzword: Hierarchical structure.” Proceedings of IFIP Congress. Amsterdam, North Holland. [Reprinted as Chapter 9 in Hoffman and Weiss (2000).]
Waldo, J. 2006. “On system design.” OOPLSA ’06. October 22–26. Portland, OR.