Domain Analysis and Patterns

James O. Coplien
Bell Laboratories
cope@research.bell-labs.com

Abstract
Domain analysis applies the fundamental abstraction principles of commonality and variation to partition a system into manageable parts. A broad domain analysis recognizes abstractions that transcend applications to cover an entire domain. It enables the designer to talk in terms of families of products at the domain level, instead of individual products at the project level. These families form around shared "common" attributes, and around common sites of parameterization that encode family member variations. It aims to make systems comprehensible, and to reduce maintenance effort by hiding stable design secrets while exposing dynamic design parameters.

Patterns capture recurring design structures that defy regularization and which typically cannot be distilled from any formal technique. They capture both architectural abstractions and engineering techniques for sound implementation. Patterns tend to be used three ways: as nicknames for solution domain constructs; as extralinguistic design concerns; and to express negative variability. Negative variability is a common design problem characterized by sites of parameterization that undermine the assumed commonality for a family. Inheritance with cancellation, a recognized object-oriented design issue, is one specific example of negative variability. In practice, many patterns exist to cope with negative variability in ways that support solid software engineering practices.

Introduction

Good software design is a matter of good abstraction and good engineering. Sound software practice has long employed paradigms as abstraction tools.

Domain analysis is (or should be) a way to formulate abstractions that are unprejudiced by a single paradigm or by the details of a single application. If you think of a paradigm as a partitioning tool, as a knife, then one important aspect of design is to find the right shape of knife to partition the problem. The first important aspect of domain analysis is that it points to the right paradigms. To use those paradigms again and again, you want to know where the "soft spots" of your business lie so that future partitionings will be an easy cut, avoiding the bones and cutting through the natural joints. You can understand where the joints are most likely to lie by taking a broad perspective across the domain. So the second important aspect of domain analysis is to avoid partitioning criteria based on special cases.

Domain analysis is based on the primitives of abstraction: commonality and variation. We try to build software families instead of individual software modules, broadening the scope of design over space and time [Parnas1976]. Family members are grouped as such because of their commonalities; we might group all the large binary objects of a system because they have similar storage requirements. We also study the variations that distinguish individual family members; these become the parameters of reuse [Neighbors1980]. Parameters of variation for large binary objects include the storage compression algorithm they use, and potentially the scheme for maintaining a working set of the object in memory. This abstraction supports reuse and maintainability, two facets of the same problem of generality of design.

Individual paradigms express specific constellations of commonality and variability particularly well. If you can understand that family members all have common structure and behavior, but they also vary in structure and often vary in code implementation, then you use the object paradigm. Subtyping and inheritance capture these forms of commonality and variability well. Other constellations of commonality and variability point to parametric programming, databases, higher-order functions, and other paradigms.

Good patterns capture system structure, but the focus is more on sound engineering and implementation than on abstraction. Some patterns (like Leaky Bucket Counters [Adams+1996]) are abstract in some sense, but not in the sense that domains or modules are abstract. Patterns solve particular problems that arise in design. What kind of problems? The pattern discipline doesn't discriminate; any mature problem/solution pair will do. Good patterns tend to focus on problem/solution structure pairs that would not precipitate from a domain analysis, but on the problems missed by conventional analyses and paradigms; therein lies their value.

The question taken up here is: What, if any, is the interaction between patterns and domain analysis? Most development efforts treat the intersection as incidental or even accidental, and in fact treat patterns informally. There is an important perspective that unifies domain analysis and the most commonly used patterns today, a perspective that goes to the fundamental issues of abstraction.

Patterns have three major uses in conjunction with analysis techniques. The first is as nicknames for important solution domain constructs. These have less to do with abstraction than with good engineering practice: Leaky Bucket Counter [Adams+1996], Window Per Task [Beck1997], and many other patterns are like this. The second use of patterns is to give names to abstractions that seem like they might be expressable in a programming language, but aren't. Think of them as very high level language concepts. Most language idioms (like handle/body [Coplien1991]) are of this form; Iterator [Gamma+1995] and many others are also of this form. This paper won't dwell on this commonly known uses. The third use ties directly to a shortcoming of domain analysis. Patterns help deal with a problem which I call negative variability. The most commonly used patterns (e.g., from GOF [Gamma+1995]) deal with this problem. That is the focus of the next section.

When abstraction breaks down

Abstraction only "works" if we can presume on the commonalities to hold across all family members. We expect this to be true in domain analysis. Furthermore, we implicitly expect this from the paradigms we use. If we group types together into a subtyping hierarchy, we expect them all to behave the same, at least to the degree that their behavior is compliant with that of the base class. To violate this commonality is a big no-no. Violating the commonality in the behavior of the types of a given hierarchy is called inheritance with cancellation or, more generally, a violation of the Liskov subtituability principle.

In short, we presume that the variabilities do not undermine the commonalities to which they are attached. If a variability does not undermine the commonality of a domain, let's call that a positive variability. But sometimes we want to say "everything is common except," hence the temptation to use inheritance with cancellation.

The most commonly used patterns in contemporary object-oriented programming, those from the Design Patterns book [Gamma+1995], are most often used to handle those cases where the programmer wants to violate the presumed underlying commonality from a (sometimes implicit) domain analysis.

This "everything is common except" problem is not unique to the object paradigm. We find that all List template instances are pretty much the same, except the overhead for List<int> is atrocious [Coplien1991]; we want to violate the commonality in implementation (and we can, using suitable C++ mechanisms). Argument defaulting (both for procedures and for template arguments) is the same. There are noteworthy examples in most paradigms.

For example, take the procedural paradigm. Consider a procedure, whose parameters of variation are the formal parameters. Let's say that most of the time, most the procedure remains common. However, for some small number of values of the first argument, let's say, some steps of the procedure should be omitted or otherwise specialized. This is exactly what the Template Method pattern [Gamma+1995] does by factoring out the parts of an algorithm that undermine the commonality it would otherwise hold over its domain of inputs. The Strategy pattern [Gamma+1995] is the same, except the granularity is more coarse, extending to the scope of the entire algorithm. The State pattern [Gamma+1995] is similar, incorporating context information that controls the selection of algorithm. Other patterns, like Pree's Unification meta-pattern [Pree1995], have the same property.

And let's return firmly to object-oriented programming. The C++ model ties inheritance and subtyping (commonality in signature and structure) together into one programming concept, the class. In cases where the behavior remains common but we want to attack the presumed commonality of data structure, we use patterns like Bridge [Gamma+1995], Envelope/Letter [Coplien1991], or some other variation of the Handle/Body idiom [Coplien1991]. The pattern exists to let us express violations of the commonality model that drive overall architecture abstraction.

In short, design patterns are often used to capture negative variability, a form of abstraction breakdown in domain analysis. By capturing these unusual combinations of commonality and variation, they complement conventional paradigms. Much of the focus is on negative variation in implementation rather than in behavior (as with Bridge), but there are behavioral applications as well (as in Template Method). Today, patterns are used largely in a bottom-up way, in concert with specific paradigms, rather than being viewed as abstraction tools at the domain analysis level. Yet these patterns fit perfectly into slight extensions of the general models of design suggested by domain analysis.

Operational Differences

For completeness, there are many other differences in emphasis and culture between domain analysis communities and pattern communities. This section explores some of the differences in focus and goals between the two; these differences are summarized in Table 1.

Novelty versus Maturity: Domain analysis seeks new paradigms (ways of dividing up the application space) from, literally, the first principles of commonality and variability. Patterns seek solutions whose value is proven by experience, though the document the principles underlying the solution.

Contrasting Domain Analysis and Patterns
Domain AnalysisPatterns
Discovery of paradigms: noveltyApplication of proven designs: maturity
Master plan (each domain designed as a whole)Emergent (each pattern is an individual)
Artifact is primaryQuality is primary
CircumspectCircumspect and Introspective
Principles (coupling, cohesion)Values (aesthetics, quality of life)
Meaning (dictionaries)Meaning (pattern names), interpretation (rationale), significance (resulting context)

Master Plan versus Emergent Design: Domain analysis has a "kerplop" feel, where the design emerges as a monolith from a thorough analysis phase. The process is very front-loaded. Once the dictionaries and structures are defined, and application engineering underway, the overall structure cannot evolve naturally. Each pattern--in the ideal case--is an individual transformation that incrementally moves the system in a direction where new problems unfold. The solution unfolds over time via piecemeal growth [Gabriel1996].

Artifacts versus Quality in its own right: Domain analysis seeks to produce a functional artifact that matches the structure of the domain. This is one of the reason that patterns leave the decision to the designer, since humans can appreciate aesthetics in ways computers cannot.

Many domain analysis techniques are driven more by business structures than by use cases. This helps makes architectures that are maintainable, but the approach doesn't focus on usability.

Patterns, of course, can touch on all of these areas, but have no special properties well-suited to either one over the other. Patterns are moving in a direction that integrates all three elements of the Vitruvian triad: utility, durability, and aesthetics, though they fall short of this goal at their current stage of maturity.

Circumspect versus Introspective: Domain analysisis an investigative technique that gathers data and makes decisions based on business objectives to produce artifacts. Its goals are largely economic and utilitarian. Of the principles of the Vitruvian triad, it focuses most on firmeness and, as a second effect, on utility (whereas Use Cases [Jacobson1992] focus first on utility and secondly on firmeness). Patterns focus first on utility and secondly on delight, but the acknowledge the impermanence of all things and leave firmeness to the processes of repair and incremental growth.

Principles versus Values: Domain analysis is based on design principles: coupling and cohesion, hiding design secrets, commonality and variability. It is independent of any social value system.

Patterns have this as well, but aspire to ascend to the level of values [Coplien1996]. For example, patterns may explore the nature of goodness itself rather than counting on predefined measures. The goodness of a pattern may depend on the value system in which it is embedded or in which it is applied.

Meaning versus Significance: Domain analysis develops a dictionary of the business vocabulary and explores the meanings of all design components. The goal isn't to interpret, but to formally separate commonalities from variabilities. Domain analysis is indiscriminate with respect to purpose. The structure captured by domain analysis is context-free with respect to purpose.

Patterns are more hermeneutic in nature, going beyond the level of meaning to levels of business significance and, in particular, significance to the quality of human life. The significance often goes beyond the business level into echelons of social, ethical, and moral interpretation. A pattern has a purpose which can be objectively evaluated for success. The interpretation of a pattern can often be found in its rationale, and much of its significance can be found in its resulting context.

References

[Adams+1996] Michael Adams, James Coplien, Robert Gamoke, Robert Hanmer, Fred Keeve, and Keith Nicodemus. Fault-tolerant telecommunication system patterns. In James O. Coplien and Douglas C. Schmidt, editors, Pattern Languages of Program Design--2, pages 549-562, 1996. Reading, MA, Addison-Wesley.

[Beck1997] Beck, Kent. Task Window pattern. Smalltalk Best Practice Patterns, Prentice-Hall, 1997.

[Coplien1991] Coplien, James O. Advanced C++ Programming Styles and Idioms, 1991. Reading, MA: Addison-Wesley.

[Coplien94a] Coplien, James O. "Evaluating the Software Development Process." Dr. Dobb's Journal, Vol. 19, No. 11, October, 1994.

[Coplien1996] Coplien, James O. Software Patterns, 1996. New York: SIGS Publications.

[Gamma+1995] Gamma, Erich, Richard Helm, Ralph Johnson and John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, ©1995.

[Gabriel1996] Gabriel, 1996. Patterns of Software: Tales from the Software Community, 1996. Oxford University Press.

[Jacobson1992] Jacobson, Ivar. Object-Oriented Software Engineering, 1992. Reading, MA: Addison-Wesley, ©1995.

[Neighbors1980] Neighbors, J. M. "Software Construction Using Components". Tech Report 160, Department of Information and Computer Sciences, University of California, Irvine, 1980.

[Parnas1976] Parnas, D. L. On the Design and Development of Program Families, IEEE Transactions on Software Engineering, SE-2:1-9, March 1976.

[Pree1995] Pree, Wolfgang. Design patterns for object-oriented software development. Reading, MA: Addison-Wesley , ©1995.


Domain Analysis and Patterns - 23 AUG 1997
Generated with Harlequin WebMaker