Table of Contents Previous Section Next Section

5.2 Managing Complexity Using Architecture

One of the key skills of any software architect is the management of software complexity. Of all nontrivial software systems, software complexity is the one of the key characteristics that must be managed. Successful management of complexity leads to improvement in many system qualities such as understandability, maintainability, and modifiability.

Complexity is an interesting phenomenon because it arises from the aggregation of many small design decisions. For system-level interfaces, the effects of complexity are multiplicative because numerous parts of an integrated system are affected by each design addition. For example, it may seem very reasonable to add a few attributes and operators to a subsystem interface. If this uncoordinated practice is repeated on multiple subsystems, the result will be excessive complexity and brittle interdependencies. Another key factor is interpersonal: It is easier to reach consensus on design disagreements by adding complexity than by eliminating overlapping details. This is the chronic failing of formal standards groups that produce "designs by committee."

Creating Complexity

Many key qualities of software systems are directly related to complexity, including cost, maintainability, and extensibility. In practice, successful management of complexity is rare. Poor management of complexity has several causes, including:

  • Lack of priority. Many software practitioners do not appreciate how critically important management of complexity is to the success of any software architecture and system implementation.

  • Lack of architectural sophistication. Design patterns for managing complexity are not commonplace in software education, training, and practice.

Many software projects fail to manage complexity because they do not consider control of complexity to be part of architecture. System-level design details are often delegated to multiple developers, who readily produce unique, uncoordinated designs. Other projects inherit excess complexity from the architecture of a proprietary product. Vendor architectures emphasize flexibility to satisfy the widest possible consumer market. For vendors, management of complexity has low priority, implicitly delegated to application developers.

To successfully manage complexity, one must understand and apply a number of architectural options. The following sections summarize some of the key techniques for managing complexity in software architectures.

For present purposes only, I have labeled these options in terms of familiar analogies. These architectural options are not exclusive. In each analogy, "it" refers to complexity:

  • Sweep it under a rug (encapsulation)

  • Hide it in a crowd (repository architecture)

  • Ignore it (horizontal architecture)

  • Slice it (layered architecture)

  • Dice it (vertical architecture)

"Do not slide through regions where high rates of information exchange are required"

-Rechtin 1997

Complexity comprises implementation details derived from the domain and the technology. By managing complexity, these details can be reorganized in a beneficial way. By organizing complex details, unnecessary dependencies and other factors that compromise system quality can be eliminated.

Option 1: Sweep It under a Rug

Encapsulation is an obvious way to hide implementation details behind an interface. As one of the fundamental properties of object-oriented environments, encapsulation unifies the software's data model and procedural model into object abstractions.

Encapsulation using language-specific mechanisms is not always as effective as might be hoped. When an implementation changes, there are unforeseen impacts on related objects, which must also be modified.

Industrial-strength encapsulation, using CORBA IDL, is a way to increase the effectiveness of encapsulation. Users of X11R6 Fresco experienced the enhanced encapsulation benefits of IDL even in a single-language, nondistributed environment.

Option 2: Hide It in a Crowd

One of the most effective ways to manage complexity is to use a repository architecture. In most cases, the repository is a database, but there are other forms, such as a blackboard. Repository architecture is a design pattern that is highly applicable to system-level architecture with documented benefits and consequences. It is interesting that many software architects and developers fail to utilize this pattern when appropriate, exposing large numbers of fine-grained object instances across system-level boundaries.

A repository architecture manages complexity by consolidating access to many objects through query languages or accessor methods. One query-language statement can consolidate messaging to thousands of objects. An object or relational repository schema provides a common model and access protocol for management of large numbers of objects.

Option 3: Ignore It

By ignoring nonessential differences among complex objects, the common interface abstractions that provide many benefits (e.g., interoperability, adaptability, substitutability, and isolation) can be defined. The concept of "common interface" has many synonyms in the software literature: design reuse, variation-centered design, standards, and so forth. As one of the authors of the first software design pattern book has quipped, "The structure of most design patterns is similar." A metapattern for this similar structure is the common interface.

Because the Java language supports interfaces as a language feature, some software gurus are just discovering the benefits of common interfaces. Java interfaces allow flexible substitution of multiple classes supporting a common interface protocol. Distributed object practitioners have enjoyed the benefits of language-independent common interfaces for years.

Option 4: Slice It

A layered architecture defines levels of abstraction in a system, allowing application software to be isolated from low-level details.

Layering defines sets of horizontal services with common interface abstractions. Multiple application objects and higher-level service objects reuse these services. Layering, as a basic form of software reuse, provides interoperability and portability benefits, in addition to managing complexity.

Layering is a flexible concept that takes many forms. Layering is frequently applied in object wrapping, operating systems, networking, frameworks, standards profiling, and application architectures.

Option 5: Dice It

Layering defines horizontal interfaces and partitions that manage complexity. Definition of vertical partitions is also useful. Vertical partitions can isolate complexity into independent subdomains. Each subdomain can support unique vertical frameworks. Vertical dependencies can be limited to objects in the vertical partition. Cross-domain dependencies (such as interoperability) should be handled through horizontal interfaces.

In practice, most systems contain many unique vertical interfaces. Good architecture has a healthy balance between horizontal and vertical interfaces. Without horizontal interfaces, vertical partitioning is ineffective. Horizontal interfaces enable vertical partitions to interoperate without unnecessary dependencies.

"Nothing is built on stone; all is built on sand, but we must build as if the sand were stone."

-Jorge Luis Borges

    Table of Contents Previous Section Next Section