Sunday, August 12, 2007
Templates for Patterns and AntiPatterns
Degenerate Form
Degenerate patterns are without discernible templates. Compared to templated patterns, a degenerate pattern contains only a single content section. Here is an example of a degenerate pattern form.
Degenerate Pattern Form
Technical Discussion: What does the author have to say about this set of concepts?
Is it a pattern? It’s a subjective judgment. One can call it a pattern because the author has chosen to do so. We choose not to believe it’s a pattern solely based upon the author’s reputation or other subjective assessments.
Degenerate form provides no particular conventions or guidance upon which practitioners can rely. The readers have to analyze the author’s prose to determine what is problem, intent, context, forces, solution, benefits, consequences, examples, and so forth. In practice, it is likely that one or more of these elements are missing.
Alexanderian Form
Christopher Alexander’s traditional template comprises three sections: name, problem, and solution. The word THEREFORE partitions the discussion. THEREFORE appears between the discussion of issues and the discussion of a design approach. Here is an example of an Alexanderian pattern form.
Alexanderian Pattern Form
Name: What does the author call this concept?
Technical Discussion: What are some related issues?
THEREFORE
What is a common solution for resolving these issues?
The Alexanderian form contains an essential partitioning between motivations and actions. By convention, a diagram is included in the Alexanderian form. This is an important spatial abstraction of the ideas presented in prose. The Alexanderian form is the minimum one should expect for the description of any design pattern.
Minimal Template (Micro-Pattern)
The minimal, rhetorical structure of a software-engineering design pattern includes name, problem, and solution. The name defines unique terminology; the problem lists the context, forces, and/or applicability; and the solution is the pattern content (what to do about solving the problem). This structure is appropriate for low-overhead pattern documentation such as workshop-pattern development or on-site consulting, where expedience and content are key, and formality is not necessary. It’s also concise, which is a requirement of some publication media. Here is the standard template for a micro-pattern.
Micro-Pattern Template
Name: What shall this pattern be called by practitioners?
Problem: What is motivating us to apply this pattern?
Solution: How do we solve the problem?
Mini-Pattern Template
A mini-pattern decomposes the problem or solution to expose the teaching elements. The next most important rhetorical aspects of a pattern are the “teaching” elements. In particular, the context and forces and/or the benefits of consequences. Most important, these sections answer the why questions about the solution, as well as provide information about when to use the pattern. Here are two examples of mini-pattern templates.
Inductive Mini-Pattern
Name: What shall this pattern be called by practitioners?
Context: What are the assumed environment or a priori assumptions for applying this pattern?
Forces: What are the different design motivations that must be balanced?
Solution: How do we solve the problem?
Deductive Mini-Pattern
Name: What shall this pattern be called by practitioners?
Problem: What is motivating us to apply this pattern?
Solution: How do we solve the problem?
Benefits: What are the potential positive outcomes of applying this pattern?
Consequences: What are potential shortcomings and consequences of applying this pattern?
We describe two template forms for mini-patterns: inductive and deductive. The inductive mini-pattern focuses on the applicability of the pattern. The deductive mini-pattern focuses on the outcomes of the solution—benefits and consequences.
Formal Templates
There are many other potential sections added to a pattern and AntiPattern write-up. Citations that include reference to other related patterns and nonpattern technical concepts are also useful and necessary for a full formal treatment. Some of the formal templates used in publications are summarized here. Note that the following are paraphrased summaries of the authors’ original template definitions. The Gang-of-Four (GoF) template is a formal template appropriate for micro-architecture-level patterns [Gamma 94].
Gang-of-Four Pattern
Pattern Name: What is the pattern called?
Pattern Classification: Is the pattern creational, structural, or behavioral?
Intent: What problem does this pattern solve?
Also Known As: What are other names for this pattern?
Motivation: What is an example scenario for applying this pattern?
Applicability: When does this pattern apply?
Structure: What are the class hierarchy diagrams for the objects in this pattern?
Participants: What are the objects that participate in this pattern?
Collaborations: How do these objects interoperate?
Consequences: What are the trade-offs of using this pattern?
Implementation: Which techniques or issues arise in applying this pattern?
Sample Code: What is an example of the pattern in source code?
Known Uses: What are some examples of real systems using this pattern?
Related Patterns: What other patterns from this pattern collection are related to this pattern?
System of Patterns Template
The system of patterns template is a formal template appropriate for multiple levels of patterns, such as idioms level, application level, and system level [Buschmann 96].
Name: What is this pattern called?
Also Known As: What are other names for this pattern?
Example: What is an example of the need for this pattern?
Context: When does this pattern apply?
Problem: What is the problem solved by this pattern?
Solution: What is the underlying principal underlying this pattern?
Structure: What objects are involved and related (structural diagram)?
Dynamics: How does this object collaborate (interaction diagram)?
Implementation: What are some guidelines for implementing this pattern?
Example Resolved: Show how the previous example is resolved using the pattern.
Variants: What are important variations of this pattern?
Known Uses: What are real-world systems using this pattern?
Consequences: What are the benefits and liabilities of using this pattern?
See Also: What are related patterns and how do they differ?
Rethinking the Design Pattern Template
Many practical experiences with formal templates were less than satisfactory for early pattern readers and practitioners. Patterns usually convey a fairly simple idea. However, many readers found the write-ups to be overly concerned with verbose details, issues, and preliminary discussion. This is an interesting reaction, given that pattern templates have come a long way from the enigmatic “degenerate templates.”
What was needed was a way to convey the information quickly and conveniently. One key approach that we used was to leverage the templates upon a robust reference model. Instead of having each pattern be completely independent (in terms of context and forces), the CORBA Design Patterns reference model allowed us to define the horizontal (common) context and forces that occur at each scale of software design. By addressing this commonality in a separate reference-model chapter, we eliminated several pages of verbose preliminary discussion from virtually every pattern.
In addition, we wanted to structure the template for quick comprehension. Given that there are many patterns to learn and assimilate, and most practitioners are under extreme time pressures on the job, we organized the template sections to enable readers to learn a great deal about a pattern, before being subjected to a rambling, free-text discussion.
The CORBA Design Patterns template is organized so that the most concise information-packed sections are first and the free-form sections are last. The template begins with keywords defined in the reference model, followed by a summarized intent and a diagram abstraction of the solution. Applicability is a priority-ordered, bulleted list that quickly covers the most important motivations for using the pattern. The solution summary explains the patterns’ technical approach crisply. Sections following the solution summary make this feasible.
In practice, we found that discussion of variations in the solution section really confuses some readers. A separate section is dedicated to the variations of the solution, so that the solution is explained without the confusing appearance of ambivalence or caveats. Another section that covers alternative software scale describes the patterns’ differences when applied at another level of software design.
CORBA Design Pattern Template
Solution Name:
Solution Type: Is it a software pattern, technology pattern, process pattern, or role pattern (keyword only)?
Intent: What is this pattern all about, in 25 words or less?
Primal Forces: Which of the horizontal forces from the reference model are best resolved by this pattern?
Applicability at this Scale: When does this pattern apply; what are the significant motivations behind this pattern (ordered bullet list)?
Solution Summary: What is the pattern solution and approach (stated clearly, without verbose embellishment)?
Benefits: What are the primary positive results of applying this pattern (bulleted list)?
Other Consequences: What are the primary negative consequences of applying this pattern (bulleted list)?
Rescaling this Solution to Other Levels: How does this pattern differ if applied at other software scales?
Related Solutions: Cross-references and citations to other patterns (including other patterns from other pattern publications) and other technology examples, publication citations, and resources. Basically, point to anything outside this pattern write-up that may be of use to the reader.
Example: A practical example of this pattern in an application context, with source code listing if applicable.
Background: Any other relevant information?
AntiPattern Templates
AntiPatterns are a new form of patterns. A fundamental difference between a pattern and an AntiPattern is that with an AntiPattern a solution has negative consequences. Some consequences may be apparent now (the symptoms) and some may be anticipated (the consequences). There are basic forms of AntiPatterns.
To be useful, AntiPatterns also include a solution. So another way to differentiate between a pattern and an AntiPattern is that there are two solutions (instead of a problem and a solution). The first solution generates negative consequences (forces that must be resolved). The second solution is a migration (or refactoring) from the first solution that provides dramatically improved benefits and much-reduced consequences.
In an ordinary design pattern, the convention is that at least three known uses of the solution must exist. Because there are two solutions, the “rule of three” for AntiPatterns is different from patterns. Since no three solutions are identical, the pattern solution is a best-practices abstraction of the known uses.
AntiPatterns are different because they have two solutions. The first solution, “the AntiPattern,” must conform to the rule-of-three occurrences just as with ordinary patterns. Unfortunately, it’s very unlikely that each of these known occurrences of the AntiPattern were resolved in exactly the same way. The second solution is a best-practices solution, based upon the known solutions to the AntiPattern, if multiple solutions are known to exist. They can be identified in the template as well.
Pseudo-AntiPattern Template
This is a form of degenerate template in which the author describes a bad solution in disparaging terms. Some of the earliest AntiPatterns posted on the Internet had this form. This is not a particularly useful form, because it’s subjective and quite one-sided in its pejorative tone.
Name: What is the AntiPattern called?
Problem: What are its derogatory characteristics?
Mini-AntiPattern
As we discussed, an AntiPattern template differs from a pattern in that it presents two solutions. The first solution is called the AntiPattern problem; the second solution is called the refactored solution. This minimal form can be extended with other template sections as appropriate.
Name: What shall this AntiPattern be called by practitioners (pejorative)?
AntiPattern Problem: What is the recurrent solution that causes negative consequences?
Refactored Solution: How do we avoid, minimize, or refactor the AntiPattern problem?
A mini-AntiPattern can include a cartoon or an anecdote in a sidebar format, along with a discussion of how the AntiPattern is resolved. It’s similar in purpose to micro-patterns and mini-patterns that provide a concise pattern description with a little less formality.
Full AntiPattern Template
The following AntiPattern template is used throughout the remainder of this book to fully document AntiPatterns. A number of simpler AntiPatterns are contained in sidebars in a minimal format called a mini-AntiPattern (previous template). The full AntiPattern template comprises a number of required and optional sections. The core sections are the general form of the AntiPattern and the refactored solution. They provide the solution/solution pair that comprises an AntiPattern.
• AntiPattern Name. The AntiPattern name is a unique noun phrase. It’s intended to be pejorative. The solution name of the pattern introduces new terminology. The name is used for future reference to the principles contained in the AntiPattern. It’s important that every AntiPattern have a name, in order to uniquely identify it. Names are critical as they form the basis for an organization’s terminology when members discuss and document software and architectures.
• Also Known As. This identifies additional popular or descriptive names and phrases for this AntiPattern.
• Most Frequent Scale. Scale is indicated via a keyword from the reference model. This section identifies where this AntiPattern fits into the software design-level model (refer back to Figure 2.7). Each pattern is logically placed where the AntiPattern is most applicable. A secondary consideration for placement concerns the scale of the resulting solution. You can choose from idiom, micro-architecture, framework, application, system, enterprise, or global/industry. Scale also scopes the dimensions of the solution. Some patterns define useful solutions at several scales. The form of the rescaled solutions is also described in the AntiPattern template.
• Refactored Solution Name. This identifies the refactored solution pattern.
• Refactored Solution Type. This section contains keywords from the reference. From SDLM, it identifies the type of action that results from the AntiPattern solution. You can choose from software, technology, process, or role. Software indicates that new software is created by the solution. Technology indicates that the solution entails acquisition of a technology or product. Process indicates that the solution entails pursuing a process. Role indicates that the solution entails assigning responsibility to an individual or group. These four different kinds of solution types are detailed as follows:
1. Software patterns comprise the overwhelming majority of the patterns included in the AntiPatterns catalog. Software patterns involve the creation of new software. The vast majority of the design patterns currently available in the industry are software patterns.
2. Technology patterns solve software problems through adoption of a technology such as Java, as opposed to programming the capability from scratch. Technology patterns are also design patterns in that they result in software design and implementation, although the method of acquisition is different. Technology patterns may involve some programming, for example, creating an object wrapper for a commercial software module.
3. Process patterns provide the definition of activities that are consistently repeatable for a given solution.
4. Role patterns solve software problems by allocating clear responsibilities to organizational stakeholders. Process and role patterns are included because of the significant effect that communication and the human organization have upon software problem solving. Occasionally, we have found that a simple process or clarification of responsibilities provides the most effective action lever for solving a technical problem.
• Root Causes. This section contains keywords from the reference model. These are the general causes for this AntiPattern. Derived from the section on root causes in Chapter 2, choose from: haste, apathy, narrow-mindedness, sloth, avarice, ignorance, pride, or responsibility (the universal cause).
• Unbalanced Forces. This section contains keywords from the reference model, and identifies the primal forces that are ignored, misused, or overused in this AntiPattern. The choices include management of functionality, management of performance, management of complexity, management of change, management of IT resources, and management of technology transfer. Risk is an underlying force for all of these options. Note that management of IT resources pertains to general resource tracking and allocation issues, including finances.
• Anecdotal Evidence. This is an optional section. Often-heard phrases and comedic material associated with this AntiPattern appear in this section.
• Background. This is an optional section. The background can contain further examples of where problems occur or general background information that is useful or interesting.
• General Form of this AntiPattern. This section often includes a diagram, which identifies the general characteristics of this AntiPattern. This is not an example, but a generic version. A prose description explains the diagram (if any) and gives the general description of this AntiPattern. The refactored solution resolves the general AntiPattern posed by this section.
• Symptoms and Consequences. This is a bulleted list of symptoms and consequences that result from this AntiPattern.
• Typical Causes. This is a bulleted section in which the unique causes of this AntiPattern are identified (in addition to the root cases identified previously).
• Known Exceptions. AntiPattern behaviors and processes are not always wrong, and often there are specific occasions when this is the case. This section briefly identifies the primary exceptions to each full AntiPattern.
• Refactored Solutions. This section explains a refactored solution that resolves the forces in the AntiPattern identified in the general form section. This solution is described without variations; those are addressed in the variations section. The solution is structured in terms of solution steps.
• Variations. This is an optional section that lists any known major variations of this AntiPattern. In addition, if there are alternative solutions, they are described here as well. The variations section exists partly in order to enhance clarity in the general form and refactored solution sections. The solutions are explained clearly, without caveats about major options and alternative design points. The variations section includes these extensions, which expand upon the capabilities of the solution.
• Example. The example demonstrates how the solution is applied to the problem by paralleling the details of the solution. This section typically includes the following elements: problem diagram, problem description, solution diagram, and solution description. This section includes one or more examples of the AntiPattern abstracted from experience.
• Related Solutions. This section identifies any citations or cross-references that are appropriate. Any AntiPatterns that are closely related to this one are listed and the differences are explained. Relationships with design patterns from other pattern languages are identified and explained. Detailed citations are contained in the bibliography in the back of the book. The references to related patterns are an important aspect of the AntiPatterns. Each AntiPattern resolves some forces and creates new ones. The new forces can be resolved by related patterns, either at the same or another level. This section also highlights differences between similar patterns.
This section also includes related terminology, references, and resources. Related terminology is explained for two reasons: to distinguish our definitions from other terms using similar names, and to connect related concepts referred to by different names. These two ambiguities are the source of much confusion in software engineering circles. References include well-known terminology, example technologies, and relevant research. The references are particularly useful to experts who can use this information to rapidly relate this pattern to other known work. If an expert reviewer fully understands one or more of the references, then the core ideas of the pattern are already known by a different terminology. (We have encountered this effect in the use of other pattern languages. It sometimes takes significant time to resolve these terminology differences without this useful section.) This section serves as both a reference list and as an “also-known-as” list of synonyms with respect to other work. Resources include pointers to other kinds of information and organizations that address the problem.
• Applicability to Other Viewpoints and Scales. This section defines how the AntiPattern impacts the remaining viewpoints: managerial, architectural, or developer. It also describes the relevancy of the pattern to other levels. If a pattern assumes a different “name” at different levels, then it should be mentioned here. Some key questions addressed in this section include: What happens when the AntiPattern is applied at different levels? How effectively does it resolve the forces at the other scales? What new forces are introduced, and are they resolved as well? How do the key forces influencing the design patterns change with scale? How do the roles of the key design elements change with scale?
The AntiPatterns in Chapters 5–7 utilize this template to document the common dysfunctional practices in the software industry and suggests practical solutions that have been proven effective on at least three known occasions. These chapters discuss software development AntiPatterns, architectural AntiPatterns and managerial AntiPatterns. These levels were chosen to provide a comprehensive coverage of the critical issues involved in software projects.
AnitPatterns:Architectural Scale and Primal Forces

On an application level, software developers are concerned primarily with managing functionality and performance when developing software to meet the functional needs of their user base. The system level is focused on developing an infrastructure that can effectively manage complexity and change in order to reduce the overall life-cycle costs of developing and maintaining an organization’s software system. The system-level architect is responsible for providing adaptability and ensuring that the overall system design is sufficient to adapt to the evolving needs of an organization.
On the enterprise level, the emphasis is on managing resources, as large-scale systems often involve thousands of interacting components and services. Additionally, the management of change overshadows other primal forces that are addressed at lower levels. The management of resources becomes a major concern as the enterprise scale increases.
At a global level, the emphasis is on leveraging existing standards and effectively utilizing the existing knowledge base to enhance enterprise-level development. However, the enterprise’s control over solutions is reduced as an organization has limited influence of external technologies. Keeping up to date on new technologies and use of IT resources are the primary concerns at the global level.
The pattern language will examine the recurring structures at each level and present detailed solutions for the major problems inherent at each architectural scale. Design patterns excel in establishing reusable architectural designs that balance the primal forces. A natural side effect of good object-oriented architecture, which effectively balances the forces, is that overall software development costs are reduced as maintenance costs are reduced, and services can be used during a major portion of the life cycle.
Software design involves making choices. For example, some of the key choices that present themselves when designing software architecture include:
• Which details to expose and which details to abstract.
• Which features to include and which features to exclude.
• Which aspects to make flexible and extensible.
• Which aspects to constrain and guarantee.
Software design choices are often complex, with numerous issues (or forces) to consider, such as security, cost, adaptability, reliability, and so on.
The primal forces include:
• Management of functionality: meeting the requirements.
• Management of performance: meeting required speed of operation.
• Management of complexity: defining abstractions.
• Management of change: controlling evolution of software.
• Management of IT resources: controlling use and implementation of people and IT artifacts.
• Management of technology transfer: controlling technology change.
AntiPatterns: Root Causes/mistakes in software development





Haste
Hasty decisions lead to compromises in software quality, as shown in Figure 2.3. Software projects are often subjected to severe schedule-related stress. At project inception, managers are pressured to trim budgets and schedules to make unrealistic targets. As successive project deadlines are missed, anything that appears to work is considered acceptable, regardless of quality. The usual victim of a slip in project delivery is testing.
Unit test coverage for each component.
•
Repeated testing of success paths and error states for integrated components.
•
Regression testing.
In this environment, long-term architectural benefits are sacrificed for expedience.
Quality, object-oriented architecture is the product of careful study, decision making, and experimentation. At a minimum, the object-oriented architecture process includes farming of requirements, architecture mining, and hands-on experience. Ideally, object-oriented architecture comprises a set of high-quality design decisions that provide benefits throughout the life cycle of the system.
Significant domain experience for the architect is essential as object-oriented architectures are competently defined and defended. With appropriate domain experience and design patterns, quality object-oriented architectures are defined rapidly. However, it’s always a mistake to make object-oriented architectural decisions hastily.
Apathy
Apathy is not caring about solving known problems. That is not to say that all solutions are known or fully achievable, but apathy is a basic unwillingness to attempt a solution (Figure 2.4). Apathy about object-oriented architecture leads to a lack of partitioning. A key aspect of object-oriented architecture is proper partitioning. For example, object-oriented architecture partitions the system into class categories and defines their interfaces and connections.
The critical partitioning decision in OO architecture is between stable reusable design and replaceable design. The stable design remains with the system throughout its life cycle, as individual software modules are modified, replaced, and added. Replaceable design details are best allocated to profiles, vertical specializations, and metadata.
Neglecting the critical partitioning means that the core of the architecture must change in response to subsystem level changes. This means that subsystem-level changes impact all of the modules in the entire system. Thus, the sin of apathy leads to poor support for change. In addition, poorly partitioned architectures make application interoperability and reuse difficult.
Narrow-Mindedness
Narrow-mindedness is the refusal to practice solutions that are otherwise widely known to be effective (Figure 2.5). An example of this is the use of metadata in software systems. Metadata is self-descriptive information in a software system that enables the system to change dynamically.
Many object-oriented systems are built with virtually no metadata. Without metadata, the application software contains hard-coded bindings, relationships, and assumptions about system configuration. For example, the number of servers or clients and their locations can be made transparently variable with straightforward use of metadata services. CORBA standards include various public metadata services, such as the Naming Service, the Trader Service, and the Interface Repository.
Sloth
Sloth is the “healthy sign” of a lazy developer or manager, who makes poor decisions based upon an “easy answer” (Figure 2.6). Distributed object technology enables application developers to define system-level interfaces quickly using the ISO Interface Definition Language (ISO IDL). Automatically generated interface stubs and skeletons make the task of constructing a distributed system relatively easy. The ease of creating and changing interfaces leads to the deadly sin of sloth—lack of configuration control.
Figure 2.6 Sloth usually ends with sudden clarity.
Although sloth is more commonplace in small-scale, object-oriented projects, the habit of frequent interface change is difficult to overcome. The more interfaces change, the more the interface semantics become unclear to developers. Ultimately, developers and maintainers spend more than half of their time performing system discovery—trying to understand how the system works. The system loses any notion of architecture long before this point is reached.
Proper configuration control starts with the first phase of prototyping. Ideally, system-level interfaces are kept stable during active software development and modified only infrequently. Stable interfaces enable parallel development, effective documentation, and reduced software obsolescence.
Avarice
Greed can take many forms, but it leads to inappropriate software development decisions. Architectural avarice means the modeling of excessive details, which results in excessive complexity due to insufficient abstraction (Figure 2.7).
Figure 2.7 Addicted to complexity.
Excess complexity leads to many software problems and project challenges. Overly complex systems are expensive to develop, integrate, test, document, maintain, and extend. In some cases, development steps are skipped (such as testing) in order to make up for the lost time and money. This can lead very quickly to project failure.
Ignorance
Ignorance is intellectual sloth. It’s the result of failing to seek understanding. It keeps people stupid (Figure 2.8), and eventually leads to long-term software problems. The sin of ignorance (implementation dependency) often occurs in the migration of applications to distributed architectures. In ignorance, one assumes that system-level interfaces are extracted from fine-grain definitions of existing application objects. For example, when an IDL interface is reverse-engineered from existing C++ header files, implementation-dependent interfaces are created and promulgated throughout a system and its subsystems.
Figure 2.8 Some people are too ignorant to change.
If an object’s interface is unique and there are no other implementations that support the same interface, then the interface is implementation-dependent. Every client or object that uses this interface is dependent upon unique implementation details. When this occurs repeatedly on a systemwide scale, a brittle system is created. This sin is also evident when vendor-proprietary interfaces are used without proper layering and wrapping for isolation.
Pride
The sin of pride is the not-invented-here syndrome (Figure 2.9). Object technology supports many reuse opportunities through the integration of commercial packages, freeware, and wrappered legacy applications.
Figure 2.9 Pride goeth before the fall.
Often, developers unnecessarily invent new designs when knowledge from preexisting systems, products, and standards are readily applied through architecture mining. Reinvention involves many unnecessary risks and costs. New software must be designed, coded, debugged, tested, and documented. New architecture must be prototyped and evolved before it is proven to provide software benefits.
AntiPatterns Reference Model
Patterns and AntiPatterns are related concepts, as shown in the figure. The essence of a design pattern is a problem and a solution. The problem is usually elaborated in terms of its context and applicable design forces. The role of the solution is to resolve the design forces in a way that generates some benefits, consequences, and follow-on problems. These new problems lead to the applicability of other patterns. Patterns are commonly occurring solutions that are frequently observed in practice. To be considered a pattern, the solution needs to be observed at least three times. Because no three occurrences can be identical, the design pattern is an abstraction of these experiences.AntiPatterns : perspectives
•
Development AntiPatterns comprise technical problems and solutions that are encountered by programmers.
•
Architectural AntiPatterns identify and resolve common problems in how systems are structured.
•
Managerial AntiPatterns address common problems in software processes and development organizations.
These three viewpoints are fundamental to software development, and many problems exist in each.
How to Kill a Software Project
•
Focus on the technologies, not the problems and scenarios.
•
Fail to maximize return on investments; for example, developing proof-of-concept prototypes is more effective than adding additional content to an existing prototype.
•
Change project focus from the larger scale to the smaller scale.
•
Don’t maintain consistency between releases.
•
Isolate team efforts from other groups within an organization.
•
Rewrite existing clients, servers, and applications.
•
Change the purpose of the system, so that the models describe the wrong focus and objects.
Where Did AntiPatterns Come From?
What Is an AntiPattern?
AntiPatterns are a method for efficiently mapping a general situation to a specific class of solutions. The general form of the AntiPattern provides an easily identifiable template for the class of problems addressed by the AntiPattern. In addition, the symptoms associated with the problem are clearly stated, along with the typical underlying causes of the problem. Together, these template elements comprise a comprehensive case for the existence of a particular AntiPattern. This form reduces the most common mistake in using design patterns: applying a particular design pattern in the improper context.
AntiPatterns provide real-world experience in recognizing recurring problems in the software industry and provide a detailed remedy for the most common predicaments. AntiPatterns highlight the most common problems that face the software industry and provide the tools to enable you to recognize these problems and to determine their underlying causes. Furthermore, AntiPatterns present a detailed plan for reversing these underlying causes and implementing productive solutions. AntiPatterns effectively describe the measures that can be taken at several levels to improve the developing of applications, the designing of software systems, and the effective management of software projects.
AntiPatterns provide a common vocabulary for identifying problems and discussing solutions. AntiPatterns, like their design pattern counterparts, define an industry vocabulary for the common defective processes and implementations within organizations. A higher-level vocabulary simplifies communication between software practitioners and enables concise description of higher-level concepts.
AntiPatterns support the holistic resolution of conflicts, utilizing organizational resources at several levels, where possible. AntiPatterns clearly articulate the collaboration between forces at several levels of management and development. Many problems in soft ware are rooted in managerial and organizational levels, so attempts to discuss developmental and architectural patterns without taking into account forces at other levels would be incomplete. For this reason, we have gone to great lengths in this book to bring together all relevant forces at many levels to both describe and address core problem areas.
AntiPatterns provide stress release in the form of shared misery for the most common pitfalls in the software industry. Often, in software development, it is much easier to recognize a defective situation than to implement a solution. In these cases, where the power to implement an AntiPattern solution is lacking, an individual subjected to the consequences of the AntiPattern forces can find solace in knowing that his or her dilemma is, in whole or part, shared by many others throughout the industry. In some such cases where the AntiPattern has severe consequences, the AntiPattern can also serve as a wake-up call for a victim to set his or her sights on other employment opportunities in the industry and to start preparing his or her resume.
AntiPatterns: AntiHype
Remember some of these major trends?
Structured programming was supposed to improve software productivity and remove most software defects.
•
Artificial intelligence was supposed to make computers much more capable.
•
Networking technologies were supposed to make all systems and software interoperable.
•
Open systems and standards were supposed to make application software portable and interoperable.
•
Parallel processing was supposed to make computers much more powerful and scaleable.
•
Object orientation was supposed to solve the problems of software productivity and adaptability, and make software highly reusable.
•
Frameworks were supposed to make software highly reusable and software development much more productive.
These claims sound like a broken record; every new software fad makes similar promises. History has not been written on many of today’s software buzzwords, but their claims sound very similar to what we have heard before. Current examples include:
The Internet
Component software
Distributed objects
Business objects
Software reuse
Scripting languages
Software agents
let us quickly point out, it’s not just the vendors who are at fault. There are several things that we (application developers and managers) can do to mitigate software problems. Regardless of the vendor hype, it’s really how you use the technology that determines your success.
The Truth about Software Technology
Eighty-four percent of software projects are unsuccessful, and virtually all deliver stovepipe systems. Why?
Vendors will tell you:
•
Our new technology changes the whole paradigm.
•
Business objects will make your ordinary developers productive (substitute any buzzword for “business objects”).
•
We’ll have all the features you need in six months.
•
We make no warranty express or implied regarding our software, its merchantability, or its fitness for any particular purpose. Vendors rarely guarantee that their software does anything useful at all. And if it does something bad; it’s not their fault. Proprietary technologies change every 4 to 18 months. Rapid technology changes can dominate software maintenance costs and impact software development. After purchasing a software license, vendors expect to make four times as much money from the same customer for training, consulting, support, and maintenance. Is it possible that the more difficulties application developers have, the more money vendors make?
Software gurus will tell you:
•
Their new method improves upon anything they said in the past.
•
Their tools fully support software development, including code generation.
•
You need more tools!
•
You need more training!
•
You need more consultancy!
We gurus disagree with each other, and we change our minds—frequently. Gurus also sell new ideas that contradict those they sold people in the past. Gurus’ methodologies are too generic for any real project. The important details of their methods are hidden in their expensive products; for example, production-quality code generation from modeling tools is still years away. More often, they never state the important details at all.
The bottom line is, regardless of the excitement and hype, software technology is in the Stone Age
Wednesday, August 08, 2007
Providing Systemic Properties (Ilities) and Quality of Service
really nice article
http://www.objs.com/aits/9901-iquos.html
Design Patterns and Layers
We divide the different patterns on the market today into three types: creational patterns, structural patterns, and behavioral patterns. The creational patterns deal with creation, initialization/deinitialization, and configuration of classes and objects.
The structural patterns take care of the structural issues, such as interface implementation between classes. The behavioral patterns describe how you handle dynamic interactions between classes and objects. Figure 5-8 shows some of the most well-known patterns divided into these three categories.
Figure 5-8: Some well-known patterns divided into categories
The bold patterns in Figure 5-8 are the ones used in the enterprise architecture presented in this chapter. We will, however, briefly describe all of the patterns in the figure and tell you roughly what they are intended to solve. We will start with the creational patterns that you use to solve creation-related problems in your enterprise application. Secondly, we will look at the structural patterns that help keep an structure easy to maintain and exchangeable. Finally, we will look at the behavioral patterns that help you with different behaviors you want your enterprise application to perform.
Note
For more information about design patterns, we recommend Design Patterns: Elements of Reusable Object-Oriented Software by Eric Gamma et al. (Addison-Wesley, 1995. ISBN: 0-201-63361-2.).
Creational Patterns
In this section, we will describe the creational patterns that solve creation-related problems in your enterprise application: the Abstract Factory, Factory Method, Singleton, Prototype, and
Builder patterns.
Abstract Factory
The function of the Abstract Factory is to provide a creation interface for related objects. The Factory Method is the "concrete factory" that actually creates the object. For example, our enterprise application in Chapter 9 needs to be able to work with different data providers. For instance, we need to be able to create an Oracle connection if we want the application to work with an Oracle database. We also need to be able to create a SQL Server connection if we want to work with Microsoft's SQL Server. However, we do not want to have to use different codes depending on which data provider we are using. The Abstract Factory simplifies the interface for the data access component by grouping all these different create methods within a single class. In Chapter 9, you will see the Abstract Factory pattern in action together with the Factory Method to ensure that our example enterprise application can easily switch between different data providers.
Factory Method
This is the pattern that serves as the "Concrete Factory" to the Abstract Factory pattern, and it defines an interface for creating an object. The Factory Method itself does not create the class, but passes the creation of the object down to subclasses. This pattern is also known as a Virtual Constructor pattern.
Instead of creating an object directly, you ask the Factory Method class to create the object for you. By doing so, you do not need to know how you create the specific object. The strength of this approach is that you can easily switch between related classes without changing the code that uses the class, as long as the different classes that the Factory Method returns to you support a known interface. The Factory Method in turn lets subclasses decide which class to instantiate. The Factory Method lets a class defer instantiation to subclasses. For example, in a data access layer, you can use the Factory Method pattern together with the Abstract Factory pattern to create data connections and data commands. For example, instead of creating a SQL Server connection directly in the data access component, you ask the createConnection method (Factory Method) to create an IdbConnection interface of type SQL Server. The createConnection method creates the connection and returns it via the interface IdbConnection. By using this abstraction, the data access component does not need to know how to create a database connection.
Singleton
The Singleton pattern is used to ensure that only one instance of an object exists, and that you have a global point of access to it. Sometimes it is important to be sure that you have only one instance of a class and that instance is accessible throughout the system. The Singleton pattern solves this by making the class itself responsible for keeping track of its own instance.
Prototype
A Prototype is a factory for cloning new instances from a prototype. This pattern is useful when a system should be independent of how its products are created, composed, and represented, and when the classes are instantiated at runtime.
This pattern can also be used when you want to avoid building class hierarchies of factories that parallel the class hierarchy of products.
Builder
A Builder separates the construction of complex objects from their representation so the construction process can be used to create different representations. All you need to do as a user of the Builder class is feed it with types that should be built and the content. This is a pattern for step-by-step creation of a complex object so that the same construction process can create different representations. The methods that are used for constructing complex objects are put together into a single class called a Director. This Director class is responsible for knowing how to create the complex objects.
The Builder pattern is similar to the Abstract Factory. The main difference between them is that when an Abstract Factory class method creates its own object, the client of the Builder class instructs the Builder class on how to create the object and then asks it for the result. How the class is put together is up to the Builder class.
Structural Patterns
Structural patterns focus on how classes and objects are constructed to work together in larger structures. Rather than composing interfaces or implementations, structural patterns describe ways to compose objects to realize new functionality.
Adapter
The Adapter pattern is intended to provide a way for a client to use an object whose interface is different from the one expected by the client.
This pattern is suitable for solving issues such as the following: You want to replace one class with another and the interfaces do not match, or you want to create a class that can interact with other classes without knowing their interfaces at design time.
Let's take a look at an example: A client wants to make a specific request of an object (MyObject). This is normally accomplished by creating an instance of MyObject and invoking the requested method. In this case, however, the client cannot do so because the interfaces do not match. The client is working with an interface named IClient and MyObject has an interface named IMyObject. The Adapter pattern helps you address this incompatibility by providing classes that are responsible for redefining the interface and data types on the client's behalf. What the pattern does is actually work as a translator between the client and MyObject. To achieve this, you create a custom Adapter class that defines methods using the interface IClient, which the client expects. Also, the Adapter class subclasses the MyObject class, and provides an implementation of the client requests in terms that the MyObject expects. Adapter overrides the method and provides the correct interface for MyObject. By using the Adapter pattern, you do not need to modify the client to be able to talk to the MyObject class via the interface.
Adapter is a structural pattern, and you can use it to react to changes in class interfaces as your system evolves, or you can proactively use the Adapter pattern to build systems that anticipate changing structural details.
Bridge
The Bridge pattern is used for the abstraction of binding one of many implementations to a class. Normally, an interface inheritance is tightly bound to the class that has implemented the interface. When you implement an interface, the class then needs to implement all methods and properties in the interface. This is the conventional way to reuse an interface, but sometimes you will want to do stuff that you cannot do with traditional inheritance. The Bridge pattern breaks the direct and permanent link between the interface and the class that implements the interface, which results in plug-and-play functionality for either the interface or the class that implements the interface. This can give you serious trouble if you are not using it the right way, but if you can control it (and know what you are doing), the main benefits of using the Bridge pattern instead of a traditional interface implementation are as follows:
You can implement only parts of interfaces.
You can provide an implementation that is not restricted to only one class module.
You can assign an implementation to an object at runtime.
You can easily modify, extend, or replace an interface during the Application Management (AM) phase without needing to mirror all the changes to the interface wherever the interface is implemented.
Composite
The Composite pattern is intended to allow you to compose tree structures to represent whole-part hierarchies, so that clients can treat individual objects and compositions of objects uniformly. Tree structures are often built where some nodes are containers of other nodes, and other nodes are "leaves." Instead of creating a separate client code to manage each type of node, the Composite pattern lets a client work with either, using the same code.
Decorator
A Decorator pattern extends an object transparently and makes it possible to add extra functionality without having access to the source code.
Extending an object can be done with inheritance and subclassing the object. This is, however, sometimes impractical, and the Decorator pattern is intended to give you an alternative way to extend the behavior of an object, without needing to create a new subclass. By using the Decorator pattern, you can easily add behaviors to specific instances of objects, which is difficult to do if you subclass your components directly. Examples of Decorators are the custom attributes in .NET. The attributes you will be using for your serviced components, like object pooling, are actually Decorators to classes—they extend the classes' behavior.
Facade
The Facade pattern simplifies the interface for a subsystem and decouples the client from the underlying subsystem. With the many classes and subsystems you use in the enterprise application, it is important to isolate the different layers and reduce coupling. The Facade pattern is intended to provide this via a unified interface to a set of interfaces in a subsystem. The facade defines a higher-level interface that makes the subsystems easier to use. There are several benefits to using Facade patterns. First, this pattern provides developers with a common interface to the underlying layers in the enterprise application, leading to more uniform code. Since the Facade object controls access to the underlying layers, the underlying system is isolated and provides a layer of protection from complexities in your subsystems as they evolve. This protection makes it easier to replace one subsystem with another because the dependencies are isolated. This pattern is used in the enterprise architecture illustrated in this chapter.
Flyweight
Many fine-grained objects can be shared in an efficient way by using the Flyweight pattern. The Flyweight pattern maintains the state of fine-grained objects internally or retrieves the state for the objects from the outside when they should be used.
This pattern is useful for situations in which you have a small number of different objects that might be needed a large number of times—with slightly different data that can be externalized outside those objects. The Flyweight pattern is intended to enable sharing to support large numbers of fine-grained objects more efficiently and reduce resource usage. The pattern references the intrinsic data of an object that makes it unique and extrinsic data that gets passed in as parameters. This pattern is useful for applications in which you may need to display icons to represent folders or some other object and do not want to add the overhead of creating new icons for each individual folder.
Proxy
One object approximates another. Smart proxies represent an implementation of the Proxy pattern, and you use these when you are creating distributed applications in which the calls will pass many address spaces so that performance can be kept at a high level. An implementation of a smart proxy is actually a wrapper around the object you want to use that organizes write and read operations from the real object in an effective way.
Behavioral Patterns
Behavioral patterns mainly focus on describing and solving complex communications between objects. The behavioral patterns are designed to take care of difficult control flows through the application and let you focus on the way objects are connected instead of on the flow of control between different objects. In this section, we will discuss what we consider to be the most important behavioral patterns.
Chain of Responsibility
The Chain of Responsibility pattern is very useful when you want a workflow for the incoming requests that x number of objects should be part of. The idea with this pattern is to decouple the sender from the receiver(s). This pattern is useful for help and print functionality in your enterprise application, because the call will be raised higher and higher until a handler is found for the particular action.
Command
The Command pattern is intended to encapsulate a request as an object. For example, consider a Windows application with menu items that need to make requests of objects responsible for the user interface. The client responds to input from the user clicking a menu item by creating a Command object and passing this object to an Invoker object, which then takes the appropriate action. The menu item itself makes the request to the invoker without knowing anything about what action will take place. By using this pattern, you are able to later change the action of the clicked menu item without changing the client itself. Practical uses for the Command pattern are for creating the mentioned dynamic menus, toolkits for applications, queues and stacks for supporting undo operations, configuration changes, and so on.
Interpreter
The Interpreter pattern is a language interpreter for a defined grammar that uses the representation to interpret sentences in the language. This pattern is useful in search queries, for instance. Instead of defining complex algorithms, you define a grammar that the user can use (AND/OR queries). Then you define a language interpreter that will interpret the sentences the user is writing.
Iterator
The Iterator pattern provides a client with a way to access the elements of an aggregate object sequentially, without having to know the underlying representation. An Iterator pattern also provides you with a way to define special Iterator classes that perform unique processing and return only specific elements of the data collection without bloating the interface with operations for the different traversals. The Iterator pattern is especially useful because it provides the client with a common interface so the caller does not need to know anything about the underlying data structure.
Mediator
Applications with many classes tend to become fragile, as communication between them becomes more complex. The more classes know about each other, the more they are tied together, and the more difficult it becomes to change the software. The Mediator pattern is intended to define an object that encapsulates how a set of objects interacts. This pattern promotes loose coupling by keeping objects from referring to each other explicitly, and lets you vary their interaction independently.
Memento
Client code often needs to record the current state of an object without being interested in the actual data values (for example, supporting checkpoint and undo operations). To support this behavior, you can have the object record its internal data in a helper class called Memento. The client code uses the Memento object for storing its current state, and restoring the previous state of the client is done by passing the Memento object back to the client object. The Memento object supports the client object with functionality to store its internal state, and it does this by violating encapsulation without making the object itself responsible for this capability.
Observer
The Observer pattern is useful when you need to present data in several different forms at once. This pattern is intended to provide you with a means to define a one-to-many dependency between objects, so when one object changes state, all its dependents are notified and updated automatically. The object containing the data is separated from the objects that display the data, and the display objects observe changes in that data.
This pattern is also know as the Publish-Subscribe pattern and is used frequently in SQL Server and also between components in Component Services, allowing such components to subscribe to events and publish events to each other.
State
The State pattern is useful when you want to have an object change its behavior depending on its internal state. To the client, it appears as though the object has changed its class. The benefit of the State pattern is that state-specific logic is localized in classes that represent that state.
Strategy
The Strategy pattern is very useful for situations in which you would like to dynamically swap the algorithms used in an application. If you think of an algorithm as a strategy for accomplishing some task, you can begin to imagine ways to use this pattern. The Strategy pattern is intended to provide you with a means to define a family of algorithms, encapsulate each one as an object, and make them interchangeable. Strategy lets the algorithms vary independently from clients that use them.
Template Method
The Template Method is a simple pattern. You have an abstract class that is the base class of a hierarchy, and the behavior common to all objects in the hierarchy is implemented in the abstract class. Other details are left to the individual subclasses. The Template Method pattern is basically a formalism of the idea of defining an algorithm in a class, but leaving some of the details to be implemented in subclasses. Another way to think of the Template Method pattern is that it allows you to define a skeleton of an algorithm in an operation and defer some of the steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
Visitor
The Visitor pattern uses an external class to act on data in other classes. This is a useful approach when you have a polymorphic operation that cannot reside in the class hierarchy. This pattern is also a useful way to extend the behavior of a class hierarchy, without the need to alter existing classes or to implement the new behavior in every subclass that requires it.
Phew! We just covered a lot of patterns, and even more exist on the market.
The Enterprise Application and Its Layers
All enterprise applications have problems that will recur. All of them also require many steps to complete their tasks; therefore we will show you how to use patterns in your enterprise applications to make them stable and robust and changeable. Later in this chapter and in the next one, we demonstrate how to implement some of these patterns.
The architecture pattern and the framework we will describe here have many parts in common with the patterns from Sten and Per Sundblad. Their architecture has a common rule for all layers: Keep chunky calls rather than chatty ones. This is something we highly agree with because it results in less overhead between calls in layers, especially when the different layers are in different processes. (For more information about architectures for other kinds of application, please visit Sten and Per's homepage at http://www.2xSundblad.com.)
Here we will look at an enterprise application pattern. The enterprise architecture is based on chunky interfaces, which means that a call to an object is returning a chunk of data. This kind of approach is sometimes called services-based architecture, and is not as resource intensive as a chatty interface. A chunky interface returns chunks of information back to the caller, whereas a chatty interface follows a more traditional object model in that you have objects with properties you can read and set. A chatty interface is more resource- and time-consuming than a chunky one because each call to the object results in a round-trip to the server, and possibly to each layer in the application. See Figure 5-9 for a chatty- and a chunky-designed object.
Figure 5-9: An overview of the chunky and the chatty techniques
The architecture we are describing here is based on the chunky approach. In all communication where functions are returning data (more than a single value), datasets are used as the carriers. Figure 5-10 shows an overview of the enterprise architecture and the different building bricks you should use to achieve a flexible and scalable architecture for an enterprise application.
Figure 5-10: An overview of the enterprise architecture
The following sections describe the layers for this architecture.
UI Layer
The layer closest to the end user is the UI layer. This layer contains UI controls to handle interactions with the user. Here is where all the Web controls in a Web application will be located. These controls take care of rendering the data to suit the client. They also take care of validating the input from the user, such as checking for valid dates in a date field. The rule is to catch data errors as soon as possible, and this layer helps to do so.
Facade Layer
The next layer, the facade layer, has mainly two purposes for the enterprise architecture:
The first is to encapsulate and isolate the business layers for the clients. By making clients independent of the internal structure in your application, you can change the internal structure in the future as long as you are keeping the same interface via your Facade classes to your clients.
The second is that the Facade class acts as a guardian for the application. It should authenticate all users who are calling the Facade methods in the application. When a facade has granted access to a user, the rest of the application should be able to trust the facade calls. We recommend that when you are designing an enterprise application with many different roles, try to keep the different roles and their methods in different Facade objects. This setup is easier to maintain.
One of the most difficult tasks when designing Facade classes for an application is to specify how many Facade classes you will have and what functionality should be put in into each of them. A good rule when deciding how many Facade classes an application should have is that every use case generates one Facade class. Each Facade class then exposes methods that cover all the actions the actors on the use case require. This is not always true, however; sometimes the same Facade class is used for several use cases and sometimes a use case employs many different facades. The latter scenario is one you should avoid, since it messes up the internal structure and makes it more difficult to maintain the application in the future.
The Facade class can either be a traditional class or a class that has been enabled for use as a Web service facade. We believe that all Facade classes should be developed in a Web service project in .NET to ensure that you can easily switch on the Web service support for a particular method by adding an attribute to it. By adding the Webmethod attribute, you are still able to call the method as a standard call and also able to call the method as a Web service, as shown in Figure 5-11. This makes it easier to scale out the application to meet future needs with as little impact as possible.
Figure 5-11: The facade layer can handle direct calls and Web service calls
Business Layer
Even if you know that an incoming request is validated and granted by the facade layer (all incoming calls pass a facade layer), you often need to apply business rules to the request to decide if it is a valid action from a business perspective. The business layer contains code for verifying business rules and taking action with them.
Handling transactions in an easy and secure way is of high importance in enterprise applications. An enterprise application deals with data that is important to a particular company, and it would be dreadful if data integrity is jeopardized by lack of transaction support. .NET supports transactions via .NET Enterprise Services, which in turn uses COM+. Transaction support is also started here if the requested method requires it. You separate transaction-related business rules from the other business rules and place them in their own classes to ease the maintenance. It is also crucial that all starts and stops of transactions are collected in the business layer. Transaction support on classes managed as .NET Enterprise Services should only be enabled when necessary because it is a performance hit. Figure 5-12 shows the dataflow in the business layer.
Figure 5-12: The dataflow for the business layer, including transaction support
Data Access Layer
Closest of the layers to the data is the data access layer. This layer contains functions that are doing the fetching and storing in the database. Data passing in .NET can mostly be done in two ways: by using data readers as data carriers between the layers and the client, or through datasets. The difference between these techniques from a performance point of view is not so big, but of importance from a scalable and distributed point of view. The data reader is marshaled by reference. The actual data is not returned to the caller; instead a reference to the data reader is returned. Each time the client moves forward in the data, a request is sent back to the server. You can easily imagine how this would slow performance on the network between the client and server or between the application layers.
The dataset, on the other hand, is marshaled by value. The data is returned to the client based on XML. This makes it more suitable for distributed applications and also fit to be used for Web services. We strongly recommend using only datasets for transporting data between the client and the server. You should also use datasets between the different layers, even if you have installed all layers on one machine initially. You never know when you need to scale out an enterprise application on more machines and thereby introduce cross-process problems, which will give you performance hits if you were using data readers between the layers. This will be true when you need to separate the different layers due to security reasons—for instance, placing the Web server in the DMZ and the application layer and data layer on the inside, as you see in Figure 5-13.
Figure 5-13: The enterprise architecture divided among different machines
Specific Data Access Components
The data layer contains data access components that are based on an abstract database class that makes it easy (or at least easier) to switch between different database providers. The abstraction from the specific calls that are for SQL Server or Oracle does not matter for your data access components in the data access layer. They are working towards the same data factory. In our example in Chapter 9, you will see that we have database classes for SQL Server and how we can switch easily to a database class for Oracle. The database class for SQL Server will use stored procedures in the data source layer, whereas our database class for Oracle will use plain SQL strings. Since the base classes are quite easy to exchange, we can later implement a class for using MySQL, or some other database engine.
Note
The performance of exchangeable data access components versus vendor fixed data access components has a price. If you try to keep the data access components exchangeable to use different data source providers, you cannot for instance use stored procedures in the data source layer to achieve better performance compared to plain SQL queries. Instead, you need to write the code that does the retrieval and storing of data from the data source layer as plain SQL to be able to more easily switch between different providers.
With the architecture of the enterprise application as we have outlined it in this chapter, you will be able to start with all layers on the same machine, with the possibility to scale the application by adding extra servers to the cluster (see Chapter 4, which discusses the different techniques to scale an enterprise application). If the need arises for placing different layers on different machines, you can easily do so.
Thursday, July 26, 2007
evaluate that design
Now that you have designed your application, you will need to evaluate that design. Design evaluation involves examining the properties of your application to determine whether design decisions meet non-functional requirements. In other words, design evaluation requires you to review your design to determine whether it meets common system requirements such as performance, maintainability, scalability, and so on. Evaluating the design at this early phase will enable you to find and correct design mistakes before they become expensive to fix.
objectives
Evaluate the logical design of an application.
Evaluate the logical design for performance.
Evaluate the logical design for maintainability.
Evaluate the logical design for extensibility.
Evaluate the logical design for scalability.
Evaluate the logical design for availability.
Evaluate the logical design for security.
Evaluate the logical design against use cases.
Evaluate the logical design for recoverability.
Evaluate the logical design for data integrity.
Evaluate the physical design of an application. Considerations include the design of the project structure, the number of files, the number of assemblies, and the location of these resources on the server.
Evaluate the physical design for performance.
Evaluate the physical design for maintainability.
Evaluate how the physical location of files affects the extensibility of the application.
Evaluate the physical design for scalability.
Evaluate the physical design for availability.
Evaluate the physical design for security.
Evaluate the physical design for recoverability.
Evaluate the physical design for data integrity.
-------------
Evaluation of the Logical Design
Once you have a logical design for a proposed system, you need to evaluate the design based on a set of standard evaluation criteria. In general, this means evaluating the design for performance, maintainability, extensibility, scalability, availability, recovery, data integrity, and use-case correctness. Typically, you can group these evaluations into run-time evaluation (performance, scalability, availability, recoverability, and security), architectural evaluation (maintainability and extensibility), and requirements evaluation (business use case).
Performance Evaluation
Although the logical design of a system is high-level, there are performance considerations that can be evaluated. For instance, you need to evaluate the system tiers and abstraction layers in the design.
As you review the logical design, ensure also that the design is not over-designed into too many tiers. Typically, designing an enterprise application into three logical tiers is sufficient. Creating additional logical tiers is usually an indication of a poor design unless there is very wellthought-out reasoning for the additional tiers.
The levels of abstraction for particular entities should be reviewed to ensure that there are very specific reasons to abstract out particular parts of the design. In particular, you should be looking for extraneous levels of abstraction. Additional levels of abstraction can affect performance by forcing the flow of data across too many objects. By removing unnecessary levels of abstraction, you can ensure that the design has a high level of performance.
The level at which you can do a performance evaluation of the logical design is generally limited to finding redundancies. The level of detail required to determine other performance problems is just not available in the logical design.
Tip Creating multiple levels of abstraction beyond the three tiers of an application is usually an indication of a performance issue in a logical design.
Scalability Evaluation
The logical design review is also when you should be evaluating the design for scalability. Scalability simply refers to the ability to adapt to an increasing load on the system as the number of users increases. In a logical design, the most important piece of handling scalability is to ensure that you have a separate, logical middle (or data) tier. Remember, the logical design does not specify how you will actually deploy an application on a physical computer but, instead, is a robust design that can accommodate scalability concerns. You can address these concerns by ensuring that the logical design keeps the entire middle (or data) tier as a discrete part of the design.
Availability and Recoverability Evaluations
Your logical design should also take into account the availability and recoverability of your project. High availability is the characteristic of a design that allows for failover and recovery from catastrophic failures. This includes failover ability, reliable transactions, data synchronization, and disaster preparedness. Because the logical design is a fairly high-level view, not all of these availability concerns can be dealt with at the logical level. Instead, try to ensure that your entities can deal with availability solutions.
Your entities should be able to handle high-availability solutions in several ways:
By using reliable transactions (for example, database transactions, Microsoft Message Queue [MSMQ] transactional messaging, or distributed transactions such as Enterprise Services or distributed transaction coordinator [DTC] transactions)
By dealing with catastrophic failure by supporting rebuilding corrupted files and configurations in case of a failure to save data outside a transaction
By allowing for failover to different databases or other data services (for instance, Web services) centers in case of catastrophic hardware failure
Security Evaluation
In evaluating the security of an enterprise application’s logical design, you will need to ensure that the application will be able to protect its secrets. Enterprise applications typically will need to access security information to do their work. If you have an enterprise application that uses a database, you will need to ensure that the connection information the application uses is securely placed. This is important because you do not want ill-intentioned people to have access to your application’s data. For example, let’s say that you have an enterprise application that is used to access sales information for sales people. A copy of this application might be installed on the laptop of a salesperson. If this laptop is subsequently stolen, how do you ensure that the data the application uses is secure?
You can secure data in your enterprise application in several ways: by encrypting sensitive data such as configuration, by limiting local caches of data to only when absolutely necessary, and by using authentication to prevent unauthorized access to the software itself.
For almost every application, sensitive data such as logon information to a Web service or database server that allows access to sensitive data can be dangerous in the wrong hands. By encrypting this data based on Microsoft Windows Data Protection (more often called the data protection application programming interface [DPAPI]), you can encrypt data so that it is decipherable only by a specific user. DPAPI allows you to encrypt data without having shared secrets, as is common with System.Security.Cryptography.
More Info The data protection API
To learn more about the data protection API, visit Microsoft.com at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/windataprotection-dpapi.asp.
In addition to encrypting data, allow for local caches of data to be minimized on computers. Keeping local copies of data is dangerous because those caches are susceptible to access by people who do not have permission through your application. Caches can also be copied out to easily hidden pieces of hardware, such as to USB memory devices, for use outside of your control. If you do need to keep local caches, such as for an application that is not always connected to a company’s servers, then protecting any sensitive data in the cache becomes paramount. Let’s say you have a cache of data that contains medical record information. Protecting that data from inappropriate users is your responsibility.
Finally, review the logical design to ensure that unauthorized usage of your application is not possible. Depending on what kind of application you are designing, this can be very important. Suppose you are writing a medical application. Even if you encrypt the sensitive data and never keep a local cache, if you allow people to use your application without verifying their identity, your application will be open to divulging information to which some people should not have access.
Quick Check
How should you handle securing sensitive configuration data?
What kind of authentication should you use in enterprise applications?
What should be your policy about caching sensitive data?
Quick Check Answers
You should use encryption to make configuration data such as database connection strings protected from unauthorized access. Your applications should allow decryption of the data but should not allow users to view the configuration information directly.
Using Microsoft Windows authentication (that is, domain authentication) in your applications is preferable to manual authentication methods because the security of identities and passwords can be maintained in a single place.
Sensitive data should not be cached if at all possible because getting access to those caches—through a variety of means—might compromise your data.
Maintainability Evaluation
Ninety cents out of every development dollar are used to maintain a system, not to build it. That makes the maintainability of a system crucial to its long-term success. Evaluation for maintainability starts here, in the logical design.
The maintainability in the logical design is based on segmenting elements of the logical design into specific tiers. Specifically, each element of the logical design should belong to one—and only one—tier of logical design. The most common problem with a logical design in the realm of maintainability is with entities that cross the data–user interface boundary. For example, if you have an entity in the logical design that is meant to store data about a customer, that same component should not also know how to expose the customer as a Windows Forms control. Separating each of those pieces of functionality will ensure that changes to the user interface and the data layer are discrete. Intermingling user interface and data tiers inevitably creates code that becomes increasingly difficult to maintain.
Extensibility Evaluation
While reviewing the logical design, it is important for you to determine extensibility in your design from two distinct perspectives: Can my design be extended with other components? Are my components extensible?
You should evaluate the logical design and determine which entities in your design can be built on top of other components. Usually, this means determining which classes to extend from the Microsoft .NET Framework itself. Look also at what classes you could use in your own code to build these new objects upon. For example, if you look at a customer entity in the logical design, you might have a common base class that performs data access for your entity objects. On the other hand, you might derive those classes from a class in the .NET Framework (for instance, the Component class) to get built-in behaviors.
It is important for you to look for ways to reuse existing code to complete your design instead of rebuilding everything from scratch. Finding components inside the .NET Framework, as well as in your existing code base (if any) to use as the basis of your components, will improve the quality of your project (that is, old code usually means better code) as well as decrease development costs.
In your logical design, look for ways to ensure that the code you write is extensible. One of the reasons you write object-oriented code is to enable code reuse. The more of your application that can be designed to be reused, the better the investment in the technology you are going to make.
Data Integrity Evaluation
Your logical design should also suggest how the data that the enterprise application will work with remains integral during the full life cycle of the application. This means that you will need to ensure that the database not only has a full set of schema, including primary keys, foreign keys, and data constraints, but that the client code determines the correct type of data concurrency to use for your application.
The decision that you make about what type of concurrency to use (optimistic versus pessimistic) will affect the overall safety and performance of your data tier. Typically, optimistic concurrency will perform better but will increase the chance that data will change between updates. Optimistic concurrency implies that data will remain unchanged between retrieving data and saving changes. If data has changed during that time, you will need to determine the best way of handling those changes. Optimistic concurrency generally performs better because there are fewer database and logical locks on the data, so more clients can access data concurrently.
Alternatively, choosing pessimistic concurrency ensures that the data a client is changing cannot be changed by other clients during the time that the client is working with that data. In all but the most severe case, optimistic concurrency is the right decision because it scales out better and performs well.
Business Use-Case Evaluation
At the point of the logical design review, you will need to review the business use cases to ensure that what you have designed continues to meet those needs. You might assume that, because the design was initiated from the use cases, this evaluation is not necessary, but that would be wrong. Much like a conversation that communicates across a room is changed by each listener, it is very common and easy for a designer to make assumptions about what the use cases are. This review of the use cases against the design will almost always find inconsistencies (or ambiguities) that need to be addressed in the logical design.
Evaluation of the Physical Design
The physical design of an enterprise application includes how the project is going to look when deployed. The big difference in this evaluation and the evaluation of the logical design is the number of concrete details of the design, which include the client deployment (for instance, how the application will be delivered to the client computers) as well as the network topology of what code and data will exist on which type of computer.
Much like the logical design, the evaluation of the physical design is broken up into a series of evaluation categories. These include performance, maintainability, extensibility, scalability, availability, and security evaluations.
Performance Evaluation
The performance evaluation of the physical design starts with a review of the different aspects of the design. These aspects include content, network, and database implementations. Each of these design aspects can adversely affect the final performance of the enterprise application.
You should review the network implementation of the project. The performance of an enterprise application can be greatly improved or destroyed based on how the data tier is implemented in the physical design. Look at how the middle tier is implemented to determine whether access to the tier is helping or hurting performance. There are no firm rules about the right implementation, but separating your data tier into a separate class of computer in the physical design is not always necessary. Typically, you would choose to separate the user interface tier and the middle tier into separate computers if the middle tier will tax the application by using a lot of memory or processor cycles. Because of the added expense of remotely accessing the data across computer boundaries, it is often more economical performance-wise to keep the middle tier on the same computer as the application.
You need to review the database implementation to ensure that it is performing adequately. As part of your performance evaluation, check the database operations to make sure they are performing as expected, both in isolation and under load testing. If this evaluation finds fault, there are many solutions for tuning the database, too numerous to explain in this training kit.
Scalability Evaluation
The scalability evaluation of the physical design is much like the evaluation of the logical design; you need to determine whether the system can adapt to handling higher-sized loads. You do this by reviewing the physical design to ensure that all components—custom components written for the project as well as first and third-party components used in the project—are compatible with moving from an in-process usage to a middle-tier scenario. This usually entails ensuring that all components can handle being moved across process boundaries in an efficient manner.
Availability and Recoverability Evaluation
In reviewing the availability of your enterprise application, you will need to determine what level of availability is required for the application. For example, if you are running a critical customer relationship management system (CRM), it becomes very important for you to handle failover to different computers and even data centers if you have a catastrophic failure such as hardware failure, interruption of Internet access, and so on. Alternatively, if you are building a small internal application, availability is not crucial to your success. The actual availability requirements should be evaluated at this time. This includes more than just ensuring that the deployment strategy takes this into account; you should also consider support for how to have backup databases and Web servers available with the correct version of the code and data. There are different strategies, but usually you will want to use a failover clustered database server for local availability.
The other side of availability is recoverability. Even if you do not need to support failover to new computers, data centers, and so on, you will likely need to support recovering from a failure. This means you need a strategy for backing up any data in the system. This includes database data, event data such as MSMQ and Event Logs, and any other transient data that is crucial to your business.
Security Evaluation
When reviewing the security of your physical design, be aware of the physical design of any communication between your enterprise application and any servers. For example, if your application will access a database server, you will need to ensure that access to that server is secure. Within your organization, this might not be a problem because firewalls and other security measures should keep unapproved people out. But as people are becoming more and more mobile, you will need to deal with an application that can be run outside of your network. In that case, you should use a virtual private network (VPN) to provide safe access to your internal servers. You should never expose your servers to the bare Internet just to allow these remote applications to work. If using a VPN isn’t possible, creating proxies to the server, for instance through Web services, is acceptable but often incurs a performance penalty and the expense of securing the Web servers.
Real World
Shawn Wildermuth
Securing enterprise applications in this world of mobile professionals is becoming increasingly difficult. Many organizations I have dealt with have tried to avoid creating a VPN so that mobile professionals could work by using all sorts of other solutions such as Web services, terminal services, and so on. In almost every case, it was easier simply to support a VPN.
If your design uses ClickOnce deployment, you will need to ensure that the Web servers exposing the application are secured like any other Internet-facing server. If you think that a server with a ClickOnce application is not apt to be hacked, you are just inviting trouble.
Maintainability Evaluation
In reviewing the maintainability of the physical design, pay attention to the common-sense approach of the code base. This means that components should use common directory structures and have directory structures of the project mirror namespace usage as much as possible. The key to maintainability is making the code base easy to navigate.
Extensibility Evaluation
The physical makeup of any enterprise application can strongly affect how extensible it is. In general, your review of the extensibility should include a review of what controls and other components are written as part of the project. The location of these controls and components should be as centrally located as possible, so they can be used in applications, if necessary. Writing the same control for different applications is just tedious. Alternatively, copying components from one project to the other breaks the ability for each application that uses a particular component to get the benefits of bug fixes and improvements to the control.
Data Integrity Evaluation
Finally, do an evaluation of the data integrity for your physical design. Unlike the evaluation of the logical design, this evaluation should include an appraisal from the user interface down to the database. Data constraints that are included in the database schema will ensure that the data stays consistent, but you should also include that same constraint higher in the code to reduce the need to go to the database just to find a data inconsistency. For example, if you have a check constraint in the database to ensure that social security numbers are nine digits, your user interface should have validation of that fact so that if an invalid social security number is entered, it is easier to report that to the user to fix than to wait for the data to be sent to the database just to receive a failure message.
Tip Data validation is not the same as data integrity. Whereas data validation might be required in the user interface of an application, the integrity of relationships between different entities in your data should be maintained both in the data tier of your application and at the database.
Lesson Summary
Evaluating the physical design should ensure that the deployed project meets the full requirements of a project.
This physical design evaluation will review the performance, scalability, availability, recoverability, security, maintainability, extensibility, and data integrity of the designed system.
talk with Microsoft Architect Pat Helland
give to someone who wants to become an architect?
PH: Architecture is a very interesting area. I liken it to building
architecture. If you stand back and think about what a building
architect has to do, they first of all have to think about a business need
and understand how to create a building that fulfills that business need.
Even more, the building needs have a feel to it. When someone
walks up and looks at the building, some emotion is conveyed.
At the same time, we have to deal with a myriad of pragmatics. How
is the air going to flow through the building? How will the people move
through the elevators? How do you make it comfortable and meet all
other environmental considerations? You have the functioning of the
building—the utilitarian object—combined with the fulfillment of the
business needs, combined with the effect that particular structure will
have on human beings.
Looking at that from the standpoint of software, you see exact
parallels. The primary goal of a software architect is to understand
and work out how to fulfill the business need. At the same time, you
want the software to relate to people and their feelings about using it.
While you are doing that, you have to deal with all of the transcendent
aspects of the darn thing functioning correctly and pragmatically.
A building architect dealing with a large building may not be an
absolute expert on elevators or airflow. But he has to know enough
about those areas to interact with the experts and bring it together into
a cohesive whole. Similarly, a software architect needs to know enough
about the different aspects of the larger system that they are putting
together to interact with the respective specialists.
Pat Helland has almost 30 years experience in
scalable transaction and database systems.
In 1978, Pat worked at BTI Computer
Systems where he built an implementation
language, parser generator, Btree subsystem,
transaction recovery, and ISAM (Indexed
Sequential Access Method).
Starting in 1982, Pat was chief architect
and senior implementor for TMF (Transaction Monitoring Facility)
which implemented the database logging/recovery and distributed
transactions for Tandem’s NonStop Guardian system. This provided
scalable and highly-available access to data with a fault-tolerant
message based system including distributed transactions and
replication for datacenter failures.
In 1991, Pat moved to HaL Computer Systems where he
discovered he could work on hardware architecture. He drove the
design and implementation of a 64-MMU (Memory Management
Unit) and a CC-NUMA (Cache Coherent Non-Uniform Memory
Architecture) multi-processor.
By 1994, Microsoft called and asked Pat to work on building
middleware for the enterprise. He came and drove the architecture
for MS-DTC (Distributed Transaction Coordinator) and MTS (Microsoft
Transaction Server). Later, Pat became interested in what today is called
SOA (Service Oriented Architecture) and started a project to provide
high-performance exactly-once-in-order messaging deeply integrated
with the SQL database. This shipped in SQL Server 2005 as SQL Service
Broker. Later, Pat worked on WinFS and did a stint in DPE evangelizing
to our largest enterprise customers.
For the past two years, Pat had been working at Amazon on the
catalog, buyability, search, and browse areas. He also started and
ran a weekly internal seminar series. He is excited to return home to
Microsoft and to work in the Visual Studio team!

