A Framework for Software Product Line Practice, Version 5.0
This practice area describes the activities that must be performed to define a software architecture. By software architecture, we mean the following:
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 [Bass 2003a].
By making "externally visible properties" of elements1 part of the definition, we intentionally and explicitly include elements' interfaces and behaviors, as part of the architecture. We will return to this point later. By contrast, design decisions or implementation choices that do not have system-wide ramifications or visibility are not architectural.
Architecture is key to the success of any software project. It is the first design artifact that begins to place requirements into a solution space. The quality attributes of a system (such as performance, modifiability, and availability) are, in large part, permitted or precluded by its architectureif the architecture is not suitable from the beginning for these qualities, don't expect to achieve them by some miracle later. The architecture determines the structure and management of the development project as well as the resulting system, since teams are formed and resources allocated around architectural components. For anyone seeking to learn how the system works, the architecture is the place where understanding begins. The right architecture is absolutely essential for smooth sailing. The wrong one is a recipe for disaster.
Architectural requirements: For an architecture to be successful, its constraints must be known and articulated. And contrary to standard software engineering waterfall models, an architecture's constraints go far beyond implementing the required behavior of the system that is specified in a requirements document [Clements 2002c, p. 57]. Other architectural drivers that a seasoned architect knows to take into account include
- the quality attributes (as mentioned above) that are required for each product to be built from the architecture
- whether the system will have to interact with other systems
- the business goals that the developing organization has for the system. These might include ambitions to use the architecture as the basis for other systems (or even other software product lines). Or perhaps the organization wishes to develop a particular competence in an area such as Web-based database access. Consequently, the architecture will be strongly influenced by that desire.
- the best sources for components. A software architecture will call for a set of components to be defined, implemented, and integrated. Those components may be implemented in-house (see the "Component Development" practice area), be purchased or licensed from the commercial marketplace (see the "Using Externally Available Software" practice area), be contracted to third-party developers (see the "Developing an Acquisition Strategy" practice area), or be excavated from the organization's own legacy vaults (see the "Mining Existing Assets" practice area). The availability of preexisting components (ones that are commercial, open source, third party, or legacy) may influence the architecture considerably and cause the architect to carve out a place in the architecture where a preexisting component can fit, if doing so will save time or money or play into the organization's long-term strategies.
Component interfaces: As we said earlier in this section, architecture includes the interfaces of its components. It is therefore incumbent on the architect to specify those interfaces (or, if the component is developed externally, ensure that its interface is specified adequately by others). By interface we mean something far more complete than the simple functional signatures one finds in header files. Signatures simply name the programs and specify the numbers and types of their parameters, but they tell nothing about the semantics of the operations, the resources consumed, the exceptions raised, or the externally visible behavior. As Parnas wrote in 1972, an interface consists of the set of assumptions that users of the component may safely make about it-nothing more and nothing less [Parnas 1972a]. Approaches for specifying component interfaces are discussed in "Example Practices."
Connecting components: Applications are constructed by connecting components to enable communication and coordination. In simple systems that run on a single processor, the venerable procedure call is the oldest and most widely used mechanism for component interaction. In modern distributed systems, however, something more sophisticated is desirable. There are several competing technologies for providing these connections as well as other infrastructure services. Among the services provided by the infrastructures are
- remote procedure calls (allowing components to be deployed on different processors transparently)
- communication protocols
- object persistence
- the creation of standard methods, such as "naming services" or a "service discovery" that allow one component to find another via the component's registered name and/or services it provides.
These infrastructures, which are purchased as commercial packages, are components themselves that facilitate connection among other components. These infrastructure packages, like patterns, represent another class of already solved problems (highly functional component interactions for distributed systems) that the architect need not reinvent. Market contenders are Sun Microsystems' Java 2 Enterprise Edition (J2EE), including Enterprise Java Beans (EJB) (http://java.sun.com/j2ee), and Microsoft's .NET (http://www.microsoft.com/net).
Architecture documentation and views: In order for an architecture to achieve its effectiveness, it must be documented. Here, architectural views come into play. A view is a representation of a set of system elements and the relationships among them [Clements 2002a]. A view can be thought of as a projection of the architecture that includes certain kinds of information and suppresses other kinds; for example
- A module decomposition view shows how the software for the system is hierarchically decomposed into smaller units of implementation.
- A communicating-processes view shows the processes in the software and how they communicate or synchronize with each other but does not show how the software is divided into layers (if it is).
- A layered view shows how the software is divided into layers but does not show the processes involved.
- A deployment view shows how software is assigned to hardware elements in the system.
There are many views of an architecture; choosing which ones to document is a matter of what information you wish to convey. Each view has a particular usefulness to one or more segments of the stakeholder community [IEEE 2000a] and should be chosen and engineered with that in mind.
Aspects Peculiar to Product Lines
All architectures are abstractions that admit a plurality of instances; a great source of their conceptual value is, after all, the fact that they allow us to concentrate on design while admitting a number of implementations. But a product line architecture goes beyond this simple dichotomy between design and codeit is concerned with identifying and providing mechanisms to achieve a set of explicitly allowed variations (because when exercised, these variations become products). Choosing appropriate variation mechanisms may be among the product line architect's most important tasks. The variation mechanisms chosen must support
the variations reflected in the products. The product constraints (see Core Asset Development) and the result of a scoping exercise (see the "Scoping" practice area) provides information about envisioned variations in the products of the product linevariations that will need to be supported by the architecture. These variations often manifest as different quality attributes. For example, a product line may include both a high-performance product with enhanced security features and a low-end version of the same product.
the production strategy and production constraints (as described in Core Asset Development). The variation mechanisms provided by the architecture should be chosen carefully, so they support the way the organization plans to build products.
efficient integration. Integration may assume a greater role for software product lines than for one-off systems simply because of the number of times it's performed. A product line with a large number of products and upgrades requires a smooth and easy process for each product. Therefore, it pays to select variation mechanisms that allow for reliable and efficient integration when new products are turned out. This need for reliability and efficiency means some degree of automation. For example, if the variation mechanism chosen for the architecture is component selection and deselection, you will want an integration tool that carries out your wishes by selecting the right components and feeding them to the compiler or code generator. If the variation mechanism is parameterization or conditional compilation, you will want an integration tool that checks the parameter values for consistency and compatibility before it feeds those values to the compilation step. Hence, the variation mechanism chosen for the architecture will go hand in hand with the integration approach (see the "Software System Integration" practice area).
Products in a software product line exist simultaneously and may vary from each other in terms of their behavior, quality attributes, platform, network, physical configuration, middleware, and scale factors and in a multitude of other ways. Each product may well have its own architecture, which is an instance of the product line architecture achieved by exercising the variation mechanisms. Hence, unlike an organization engaged in single-system development, a product line organization will have to manage many related architectures simultaneously.
There must be documentation for the product line architecture as it resides in the core asset base and for each product's architecture (to the extent that it varies from the product line architecture). For the product line architecture, the views need to show the variations that are possible and must describe the variation mechanisms chosen with the rationale for the variation. Furthermore, a descriptionthe attached processis required that explains how to exercise the mechanisms to create a specific product. The views of the product architectures, on the other hand, have to show how those variation mechanisms have been used to create this product's architecture. As with all core assets, the attached process becomes the part of the production plan that deals with the architecture.
Application to Core Asset Development
The product line architecture is an early and prominent member in the collection of core assets. The architecture is expected to persist over the life of the product line and to change relatively little and slowly over time. The architecture defines the set of software components (and hence their supporting assets such as documentation and test artifacts) that populates the core asset base. The product line architecturetogether with the production planprovides the prescription (harkening to the "in a prescribed way" from the definition of a software product line) for how products are built from core assets.
Application to Product Development
Once it's been placed in the core asset base for the product line, the architecture is used to create product architectures for each new product according to the architecture's attached process. If the product builders discover a variation point or a needed mode of variation that is not permitted by the architecture, they should bring it to the architect's attention; if the variation is within the product line's scope (or deemed desirable to add to the scope), the architecture may be enhanced to accommodate it. The "Operations" practice area deals with setting up this feedback loop in the organization.
We categorize example practices for architecture definition into those concerned with understanding the requirements for the architecture; designing the architecture; and communicating or documenting the architecture.
Understanding the Requirements for the Architecture
Quality Attribute Workshops: Prerequisite to designing an architecture is understanding the behavioral and quality attribute requirements that it must satisfy. One way to elicit these requirements from the architecture's stakeholders is with an SEI Quality Attribute Workshop (QAW) [SEI 2007g]. QAWs provide a method for identifying the quality attributes that are critical to a system architectureattributes such as availability, performance, security, interoperability, and modifiability. In the QAW, an external team facilitates meetings between stakeholders during which scenarios representing the quality attribute requirements are generated, prioritized, and refined (i.e., adding additional details such as the participants and assets involved, the sequence of activities, and questions about quality attribute requirements). The refined scenarios can be used in different ways; for example, as seed scenarios for an evaluation exercise or as test cases in an acquisition effort.
Use of the production strategy: The choice of variation mechanisms is strongly influenced by the organization's production strategy. That strategy describes how the organization plans to build the specific products from the core assets. For example, an organization may decide that an integration team will assemble a product by selecting from existing components. This strategy forces the architecture team to focus on component substitution as a variation mechanism. Mechanisms that require additional coding may not be appropriate in this setting.
Planning for architectural variation: Nokia has used a "requirements definition hierarchy" as a way to understand what variations are important to particular products [Kuusela 2000a]. The hierarchy consists of design objectives (goals or wishes) and design decisions (solutions adopted to meet the corresponding goals). For example, a design objective might be "the system shall be highly reliable." One way to meet that objective is to decree that "the system shall be a duplicated system." That, in turn, might mean that "the system shall have duplicated hardware" and/or "the system shall duplicate communication links." Another way to meet the reliability objective is to decree that "the system shall have a self-diagnostic capacity," which can be met in several ways. Each box in the hierarchy is tagged with a vector, each element of which corresponds to a product in the product line. The value of an element is the priority or importance given to that objective, or to the endorsement of that design decision, by the particular product. For example, if an overall goal for a product line is high reliability, being a duplicated system might be very important to Product 2 and Product 3 but not at all important to Product 1 (a single-chip system).
The requirements definition hierarchy is a tool that the architect can use as a bridge between the product line's scope (see the "Scoping" practice area), which will tell what variations the architecture will have to support, and the architecture, which may support the variation in a number of ways. It is also useful to see how widely used a new feature or variation will be: should it be incorporated into the architecture for many products to use, or is it a one-of-a-kind requirement best left to the devices of the product that spawned it? The hierarchy is a way for the architect to capture the rationale behind such decisions.
Designing the Architecture
Architecture definition and architecture-based development: As the field of software architecture has grown and matured, methods of creating, defining, and using architecture have proliferated. Many example practices related to architecture definition are defined in widely available works [Kruchten 1998a, Jacobson 1997a, Hofmeister 2000a, Bachmann 2000a]. The Rational Unified Process (RUP) is a method used for object-oriented systems. A good resource for RUP is the book The Rational Unified Process: An Introduction [Kruchten 2004a].
Attribute-Driven Design (ADD): The SEI Attribute-Driven Design (ADD) method [SEI 2007a] is a method for designing the software architecture of a product line to ensure that the resulting products have the desired qualities. ADD is a recursive decomposition method that starts by gathering architectural drivers that are a combination of the quality, functional, and business requirements that "shape" the architecture. The steps at each stage of the decomposition are
- Choose architectural drivers: The architectural drivers are the combination of quality, business, and functional goals that "shape" the architecture.
- Choose patterns and children component types to satisfy drivers: There are known patterns to achieve various qualities. Choose the solutions that are most appropriate for the high-priority qualities.
- Instantiate children design elements and allocate functionality from use cases using multiple views: The functionality to be achieved by the product family is allocated to the component types.
- Identify commonalities across component instances: These commonalities are what define the product line, as opposed to individual products.
- Validate that the quality and functional requirements and any constraints have not been precluded from being satisfied by the decomposition.
- Refine use cases and quality scenarios as constraints to children design elements: Because ADD is a decomposition method, the inputs for the next stage of decomposition must be prepared.
Architectural patterns: Architectures are seldom built from scratch but rather evolve from solutions previously applied to similar problems. Architectural patterns represent a current approach to reusing architectural design solutions. An architecture pattern2 is a description of component types and a pattern of their runtime control and/or data transfer [Shaw 1996a]. Architectural patterns are becoming a de facto design language for software architectures. People speak of pipe-and-filter, n-tier, client-server, or agent-based architectures, and these phrases immediately convey complex and sophisticated design information. Architectural pattern catalogues exist that explain the properties of a particular pattern, including how well-suited each one is for achieving specific quality attributes such as security or high performance. Buschmann, Schmidt, and colleagues provide examples of catalogs [Buschmann 1996a, Schmidt 2000a]. Using a previously catalogued pattern shortens the architecture definition process, because patterns come with pedigrees: what applications they work well for, what their performance properties are, where they can easily accommodate variation points, and so forth. Product line architects should be familiar with well-known architectural patterns as well as patterns (well-known or not) that are used in systems similar to the ones they are building.
Service-oriented architectures: One architectural pattern that's very popular now is the service-oriented architecture. A service-oriented architecture is one in which the components are services. A service is a reusable, self-contained, distributed component with a published interface that stresses interoperability, is usually thread-safe, and is discoverable and dynamically bound. Service-oriented systems work by "stringing together" services that have specific, well-defined functionality into chains that do sophisticated, useful work. Services can be locally developed or (in theory) "discovered" on a company's intranet or even on the World Wide Web and bound at runtime. Standards exist for services communicating with each other via messaging based on the Extensible Markup Language (XML), for specifying what services do, and for quality-of-service contracts necessary to insure that a service provides the level of functionality and quality attributes required. A service-oriented architecture's basic variation mechanism is component replacementthat is, choosing different services or stringing together services in a different way to meet the needs of individual products.
Aspect-oriented software development (AOSD): AOSD is an approach to program development that makes it possible to modularize systemic properties of a program such as synchronization, error handling, security, persistence, resource sharing, distribution, memory management, replication, and the like that would otherwise be distributed widely across the system, making it hard to change. An aspect is a special kind of module that implements one of these specific properties that would otherwise cut across other modules. As that property varies, the effects "ripple" through the entire program automatically. As an example, an AOSD program might define "the public methods of a given package" as a crosscutting structure and then say that all those methods should do a certain kind of error handling. This aspect would be coded in a few lines of well-modularized code. AOSD is an architectural approach, because it provides a means of separating concerns that would otherwise affect a multitude of components constructed to separate a different, orthogonal set of concerns. AOSD is appealing for product lines, because the variations can often be represented as aspects. A good starting point for understanding AOSD is provided at http://aosd.net.
Mechanisms for achieving variability in a product line architecture (1): Svahnberg and Bosch created a list of variability mechanisms for product lines that includes mechanisms for building variability into components. Svahnberg and Bosch also include these architectural mechanisms [Svahnberg 2000a]:
- configuration and module interconnection languages: used to define the build-time structure of a system, including selecting (or deselecting) whole components
- generation: used when there is a higher level language that can be used to define a component's desired properties
- compile-time selection of different implementations: The variable #ifdefs can be used when variability in a component can be realized by choosing different implementations.
Code-based mechanisms used to achieve variability within individual components are discussed in the "Component Development" practice area.
Mechanisms for achieving variability in a product line architecture (2): Philips Research Laboratories uses service component frameworks to achieve diversity in its product line of medical imaging systems [Wijnstra 2000a]. Goals for that family include extensibility over time and support for different functions at the same time. A framework is a skeleton of an application that can be customized to yield a product. White-box frameworks rely heavily on inheritance and dynamic binding; knowledge of the framework's internals is necessary in order to use it. Black-box frameworks define interfaces for components that can be plugged in via composition tools. A service component framework is a type of black-box framework that supports a variable number of plug-in components. Each plug-in is a container for one or more services, which provide the necessary functionality. All services support the framework's defined interface but exhibit different behaviors. Clients use the functionality provided by the component framework and the services as a whole; the assemblage is, itself, a component in the products' architecture. Conversely, units in the product line architecture may consist of or contain one or more component frameworks.
Mechanisms for achieving variability in a product line architecture (3): Bachmann and Clements sum up the current approaches for variation mechanisms in product line architectures and sketch an economics-based approach for choosing them [Bachmann 2005a].
Communicating and Documenting the Architecture
Architecture documentation: Recently, in the software engineering community, more attention has been paid to writing down a software architecture so that others can understand it, use it to build systems, and sustain it. The Unified Modeling Language (UML) is the most-often-used formal notation for software architectures, although it lacks many architecture-centric concepts. The SEI developed the Views and Beyond approach to documentation [Clements 2002a], which holds that documenting a software architecture is a matter of choosing the relevant views based on projected stakeholder needs, documenting those views, and then documenting the information that applies across all of them. Examples of cross-view information include how the views relate to each other and stakeholder-centric roadmaps through the documentation that let people with different interests find information relevant to them quickly and efficiently. The approach includes a three-step method for choosing the best views to engineer and document for any architecture, and the overall approach produces a result compliant with the Institute of Electrical and Electronics Engineers' (IEEE's) recommended best practice on documenting architectures of software-intensive systems [IEEE 2000a].
Specifying component interfaces: Interfaces are often specified using a contractual approach. Contracts state pre- and postconditions for each service and define invariants that express constraints about the interactions of services within the component. The contract approach is static and does not address the dynamic aspects of a component-based system or even the dynamic aspects of a single component's behavior. Additional techniques such as state machines [Harel 1998a] and interval temporal logic [Moszkowski 1986a] can be used to specify constraints on the component that deal with the ordering of events and the timing between events. For example, a service may create a thread and assign it work to do that will not be completed within the service's execution window. A postcondition for that service would include the logical clause for "eventually this work is accomplished."
A complete contract should include information about what will be both provided and required. The typical component interface specification describes the services that a component provides. To fully document a component so that it can be integrated easily with other components, the specification should also document the resources that the component requires. In addition, this documentation provides a basis for determining whether there are possible conflicts between the resources needed for the set of components that make up the application.
A component's interface provides only a specification of how individual services respond when invoked. As components are integrated, additional information is needed. The interactions between two components needed to achieve a specific objective can be described as a protocol. A protocol groups together a set of messages from both components and specifies the order in which they are to occur.
Each component exhibits a number of externally visible attributes that are important to its use but are often omitted (incorrectly) from its interface specification. Performance (throughput) and reliability are two such attributes. The standard technique for documenting the performance of a component is the computational complexity of the dominant algorithms. Although this technique is platform independent, it is difficult to use in reasoning about satisfying requirements in real-time systems, because it fails to yield an actual time measure. Worse, it uses information that will change when algorithms (presumably encapsulated within the component) change. A better approach is to document performance bounds, setting an upper bound on the time consumed. The documentation remains true when the software is ported to a platform at least as fast as the current onea safe assumption in today's environment. Cases in which the stated bounds are not fast enough can be resolved on a case-by-case basis. If the product can indeed meet the more stringent requirement on that product's platform, that fact can be revealed. If it cannot, either remedial action must be taken or the requirement must be relaxed.
The biggest risk associated with this practice area is failing to have a suitable product line architecture. An unsuitable product line architecture will result in
- components that do not fit together or interact properly
- products that do not meet their behavioral, performance, or other quality attribute goals
- products that should be within scope but which cannot be produced from the core assets at hand
- a tedious and ad hoc product-building process
These effects, in turn, will lead to extensive and time-consuming rework, poor system quality, and an inability to realize the product line's full benefits. If product teams do not find the architecture suitable for their products and easy to understand and use, they may bypass it, resulting in the eventual degradation of the entire product line concept.
Unsuitable architectures could result from
the lack of a skilled architect: A product line architect must be skilled in current and promising technologies, the nuances of the application domains at hand, modern design techniques and tool support, and professional practices such as the use of architectural patterns. The architect must know all the sources of requirements and constraints on the architecture, including those not traditionally specified in a requirements specification (such as organizational goals) [Clements 2002c, p. 58].
the lack of sound input: The product line scope and production strategy must be well-defined and stable. The requirements for products must be articulated clearly and completely enough for architectural decisions to be reliably based on them. Forthcoming technology, which the architecture must be poised to accept, must be forecast accurately. Relevant domains must be understood so that their architectural lessons are learned. To the extent to which the architect is compelled to make guesses, the architecture poses a risk.
poor communication: The best architecture is useless if it is documented and communicated in ways that its consumersfor example, the product builderscannot understand. An architecture whose documentation is chronically out of date is effectively the same as an undocumented architecture. Clear and open two-way communication channels must exist between the architect and the organizations using the architecture. Architecture documentation that is appropriate for architects and developers may not be good enough for other stakeholders, who, for example, may not understand UML diagrams.
a lack of supportive management and culture: Management must support the creation and use of the product line architecture, especially if the architecture group is separate from the product development group. Failing this, product groups may "go renegade" and make unilateral changes to the architecture, or decline to use it at all, when turning out their systems. There are additional risks if management does not support the strong integration of system and software engineering.
architecture in a vacuum: The exploration and definition of software architecture cannot take place in a vacuum separate from system architecture.
poor tools: There are precious few tools for this practice area, especially those that help with designing, specifying, or exercising an architecture's variation mechanismsa fundamental part of a product line architecture. Tools for testing the compliance of products to an architecture are virtually nonexistent.
poor timing: Declaring that an architecture is ready for production before it really is leads to stagnation, while declaring it too late may allow unwanted variation. Discretion is needed when deciding when and how firmly to freeze the architecture. The time required to fully develop the architecture also may be too long. If product development is curtailed while the product line architecture is being completed, developers may lose patience, management may lose resolve, and salespeople may lose market share.
Unsuitable architectures are characterized by
inappropriate parameterization: Overparameterization can make a system unwieldy and difficult to understand. Underparameterization can eliminate some of the necessary system customizations. The early binding of parameters can also preclude easy customization, while the late binding of parameters can lead to inefficiencies.
inadequate specifications: Components may not integrate properly if their specifications are sketchy or limited to static descriptions of individual services.
decomposition flaws: Without an appropriate decomposition of the required system functionality, a component may not provide the functionality needed to implement the system correctly.
wrong level of specificity: A component may not be reusable if the component is too specific or too general. If the component is made so general that it encompasses multiple domain concepts, the component may require complex configuration information to make it fit a specific situation and therefore be inherently difficult to reuse. The excessive generality may also tax performance and other quality attributes to an unacceptable point. If the component is too specific, there will be few situations in which it is the correct choice.
excessive intercomponent dependencies: A component may become less reusable if it has excessive dependencies on other components.
General software architecture:
Bass, Clements, and Kazman emphasize architecture's role in system development and provide several case studies of architectures used to solve real problems. One is an architecture for the CelsiusTech ShipSystem 2000 product line. Their book also includes an extensive discussion of architectural views.
Hofmeister emphasizes views and structures and provides a solid treatment of building a system from an architecture and its views.
The SEI's Software Architecture Technology (SAT) Web site provides a wide variety of software architecture resources and links.
Shaw and Garlan provide an excellent treatment of architectural patterns (what they call styles) and their ramifications for system building.
Product line architecture:
Bosch brings a dedicated product line focus to the mix. His work is required reading for the product line practitioner.
Software architecture from a strictly object-oriented point of view:
Booch offers a good foundation for architecture definition.
The work of Buschmann and colleagues raises the design pattern phenomenon to the arena of software architecture and is a good staple in any architect's toolbox.
Jacobson, Griss, and Jonsson devote an entire section to architectural patterns for object-oriented systems designed with strategic reuse in mind.
Kruchten's book is a good source for gaining an understanding of RUP.
Smith and Williams' book contains three chapters on principles and guidance for architecting systems (object-oriented or not) in which performance is a concern.
Jackson classifies, analyzes, and structures a set of recurring software development problems, organized according to how the software will interact with the outside world.
Clements and colleagues explain the Views and Beyond approach to architecture documentation.
This Web site is the starting point for an investigation of UML.
1 An element is a unit of software that has identity either at implementation time or at runtime. We use the term component to refer to a unit of software that must be developed or acquired as a unit.
2 The term used by Shaw and Garlan was architectural style, which is synonymous with pattern [Shaw 1996a].