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.
Table 1-1. Structure summary
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