P.5. Viewtypes and Styles

P.5.1 Viewtypes

DEFINITION

A viewtype defines the element types and relationship types used to describe the architecture of a software system from a particular perspective.

Although no fixed set of views is appropriate for every system, broad guidelines can help us gain a footing. Architects need to think about their software in three ways simultaneously:

  1. How it is structured as a set of implementation units
  2. How it is structured as a set of elements that have runtime behavior and interactions
  3. How it relates to nonsoftware structures in its environment

Each view we present in PartI falls into one of these three categories, which we call viewtypes. The three viewtypes are

  1. The module viewtype
  2. The component-and-connector (C&C) viewtype
  3. The allocation viewtype

Views in the module viewtypemodule views for shortdocument a system's principal units of implementation. Views in the C&C viewtypeC&C viewsdocument the system's units of execution. And views in the allocation viewtypeallocation viewsdocument the relationships between a system's software and its development and execution environments. A viewtype constrains the set of elements and relations that exist in its views.

P.5.2 Styles

Within the confines of a viewtype, recurring forms have been widely observed, even if written for completely different systems. These forms occur often enough that they are worth writing and learning about in their own right. Perhaps they have interesting properties not shared by others. Perhaps they represent a significant and oft-used variation of the viewtype. Our description of each viewtype includes a section on commonly occurring forms and variations. We call these architectural styles, or styles. Styles have implications for architectural documentation and deserve definition and discussion in their own right.

DEFINITION

An architectural style is a specialization of element and relation types, together with a set of constraints on how they can be used.

A style defines a family of architectures that satisfy the constraints. Styles allow one to apply specialized design knowledge to a particular class of systems and to support that class of system design with style-specific tools, analysis, and implementations. The literature is replete with a number of styles, and most architects have a wide selection in their repertoires.

For example, we'll see that modules can be arranged into a useful configuration by restricting what each one is allowed to use. The result is a layered stylea member of the module viewtypethat imparts to systems that use it qualities of modifiability, portability, and the ability to quickly extract a useful subset. Different systems will have a different number of layers, different contents in each layer, and different rules for what each layer is allowed to use. However, the layered style is abstract with respect to these options and can be studied and analyzed without binding them.

For another example, we'll see that client-server is a common architectural style, a member of the component-and-connector viewtype. The elements in this style are clients, servers, and the protocol connectors that depict their interaction. When used in a system, the client-server style imparts desirable properties to the system, such as the ability to add clients with little effort. Different systems will have different protocols, different numbers of servers, and different numbers of clients each can support. However, the client-server style is abstract with respect to these options and can be studied and analyzed without binding them.

DEFINITION

A style guide is the description of an architectural style that specifies the vocabulary of design (sets of element and relationship types) and the rules (sets of topological and semantic constraints) for how that vocabulary can be used.

Some styles are applicable in every software system: decomposition, uses, deployment, and work assignment, for example. Other styles occur only in systems in which they were explicitly chosen and designed in by the architect: layered, communicating-processes, and client-server, for example.

Choosing a system style, whether covered in this book or somewhere else, imparts a documentation obligation to record the specializations and constraints that the style imposes and the properties that the style imparts to the system. We call this piece of documentation a style guide. The obligation to document a style can usually be discharged by citing a description of the style in the literature: this book, for example. If you invent your own style, however, you will need to write a style guide for it.

No system is built exclusively from a single style. On the contrary, every system can be seen to be an amalgamation of many different styles. Some occur in every system, but systems also exhibit a combination of "chosen" styles as well. This amalgamation can occur in several ways.

In the last case, your choice of style-filtered glasses depends, once again, on the uses to which you and your stakeholders intend to put the documentation. For instance, if the shared-data style gives you all the analysis tools you need, you might choose it rather than the other two options. If you need the perspective afforded by more than one style, however, you have a choice. You can document the corresponding views separately, or you can combine them into a single view that is, roughly speaking, the union of what the separate views would be.

All three cases make clear the need to be able to document different parts of a system by using different views. That is, a view need not show the entire system.

P.5.3 Summary: Viewtypes, Styles, and Views

The three viewtypesmodule, C&C, and allocationrepresent the three perspectives that an architect must consider when designing a system: the system as units of implementation, the system as units of runtime execution, and the mapping from software elements to environmental structures. A viewtype restricts the element typesmodules in the module viewtype, for instanceand the corresponding relationship types.

But even within the confines of a viewtype, choices must be made: how the elements are restricted, how they relate to one another, and constraints on their use or configuration. A style is a specialization of a viewtype and reflects recurring patterns of interaction, independent of any particular system (FigureP.2).

Figure P.2. For each viewtype, we describe several styles. You can add to the set by creating your own style or adapting one from another source. And within the confines of a style, choices need to be made: how the element and relation types in a style are bound to elements and relations in a system. These are the views of your architecture.

Even within the confines of a style, choices need to be made: how the element and relation types in a style are bound to elements and relations in a system. In the context of viewtypes and styles, then, a view can now be seen as a style that is bound to a particular system. For example, Chapter 4 describes the publish-subscribe style in terms of loosely coupled components whose interfaces allow the reporting of events and subscription to events, but the description of the style in Chapter 4 is independent of any system. If you choose the publish-subscribe style as a design strategy for your system, you will produce a publish-subscribe view by naming the components and the events they report and to which they subscribe.

COMING TO TERMS

Module, Component

In this book, we divide the universe of software architectures into three categories: a module viewtype, a component-and-connector viewtype, and an allocation viewtype. This three-way distinction allows us to structure the information we're presenting in an orderly way and, we hope, allows you to recall it and to access it in an orderly way. But for this strategy to succeed, the distinctions have to be meaningful. Two of the categories rely on words that, it must be admitted, are not historically well differentiated: module and component.

Like many words in computing, these two have meanings outside our field. Furthermore, both terms have come to be associated with movements in software engineering that have overlapping goals.

During the 1960s and 1970s, software systems increased in size and were no longer able to be produced by one person. It became clear that new techniques were needed to manage software complexity and to partition work among programmers. To address such issues of "programming in the large," various criteria were introduced to help programmers decide how to partition their software. Encapsulation, information hiding, and abstract data types became the dominant design paradigms of the day, using module as the carrier of their meaning. The 1970s and 1980s saw the advent of "module interconnection languages" and features of new programming languages such as Modula modules, Smalltalk classes, and Ada packages. Today's dominant design paradigmobject-oriented programminghas these module concepts at its heart. Components, by contrast, are in the limelight with component-based software engineering and the component-and-connector perspective in the software architecture field.

Both movements aspire to achieve rapid system construction and evolution through the selection, assembly, and wholesale replacement of independent subpieces. Both modules and components are about the decomposition of a whole software system into constituent parts. But beyond that, the two terms take on different shades of meaning.

  • A module tends to refer first and foremost to a design-time entity. Parnas's foundational work in module design (1972) used information hiding as the criterion for allocating responsibility to a module. Information that was likely to change over the lifetime of a system, such as the choice of data structures or algorithms, was assigned to a module, which had an interface through which its facilities were accessed.
  • A component tends to refer to a runtime entity. Shaw and Garlan (1996), for example, speak of an architecture of a system as a collection of "computational componentsor simply components" along with a description of their interactions. Szyperski (1998, p. 30) says that a component "can be deployed independently and is subject to composition by third parties." Herzum and Sims (2000, p. 6) say that a component is "a self-contained piece of software with a well-defined interface or set of interfaces. We imply a clear runtime and deployment connotation; that is, the component has interfaces that are accessible at runtime, and at some point in its development life cycle, the component can be independently delivered and installed." The emphasis is clearly on the finished product and not on the design considerations that went into it. Indeed, the operative model is that a component is delivered in the form of an executable binary only: Nothing upstream from that is available to the system builder.

In short, a module suggests encapsulation properties, with less emphasis on the delivery medium and what goes on at runtime. Not so with components. A delivered binary maintains its "separateness" throughout execution. A component suggests independently deployed units of software with no visibility into the development process.

Of course, there's overlap. How can something be independently deployable and replaceable without involving encapsulation? That is, how can components not be modular? But, in fact, you could imagine a well-designed module that isn't independently deployable, because it requires all sorts of services from other modules. You could also imagine a component that didn't encapsulate very much or encapsulated the wrong things. This is why plug-and-play, the current mantra of component-based systems engineering, is more accurately rendered as plug-and-pray. In their IEEE Software article, Garlan, Allen, and Ockerbloom (1995) describe the frustrations of trying to assemble a system from components that were built with subtly conflicting assumptions about their environments.

Our use of the terms in this book reflects their pedigrees. The module viewtype contains styles that reflect primarily design-time considerations: decompositions that assign parts of the problem to units of design and implementation, layers that reflect what uses are allowed when software is being written, and classes that factor out commonality from a set of instances. Of course, all these styles have runtime implications; that's the end game of software design, after all. Similarly, the component-and-connector viewtype contains styles that focus on how processes interact and data travels around the system during execution. Of course, all these runtime effects are the result of careful design-time activities.

This conceptual overlap is one thing, but a given architecture will usually also exhibit a concrete overlap. An element that you document in a module view may well show up in a component-and-connector view as the runtime manifestation of its design-time self. As a component, the element might be replicated many timesacross different processors, for example. As a module, however, it would be very unlikely to be duplicated: Why would you ask someone to produce the same piece of code twice? As a rule, an element shows up once in a module view; a corresponding component might occur many times in a component-and-connector view.

Modules and components represent the current bedrock of the software engineering approach to rapidly constructed, easily changeable software systems. As such, modules and components serve as fundamental building blocks for creating and documenting software architectures.

Категории