The central views of an architecture model are the component diagrams that show the major components of the system and how they interact with one another to fulfill requirements. If the system is large, each component might have its own high-level design that shows how it is composed of smaller components.
1. Create components and their parts to represent the major parts of the system
A typical component diagram for a large system might include components like these:
- Presentation. The component that provides access to the user, typically running on a Web browser.
- Web service components. Provides a connection between clients and servers.
- Use case controllers. Conduct the user through the steps of each scenario.
- Business core. Contains classes that are based on classes in the requirements model, implements the key operations and imposes business constraints.
- Database. Stores the business objects.
- Logging and error handling components.
Within each component, you can show its sub-components as Parts. A Part is effectively an attribute of its parent component, which is much like an attribute that belongs to an ordinary class. Each part has its own type, which is usually also a component. You can place this component on a diagram and show its parts. It is useful to apply this technique to the whole system. Draw it as a single component, and show its major components as parts. This helps you identify clearly the interfaces of your system with the external world.
When your design for a component uses another component, you frequently have a choice about whether to represent it as a part or as a separate component that you access through a required interface. The major recommendations are as follows:
Use parts in the following situations:
- The design of the parent component must always use the part's component type. Therefore, the design of the part is integral to the design of the parent component.
- The parent component has no concrete existence of its own. For example, you could have a conceptual component called Presentation Layer that represents a collection of real components that handle views and user interactions. In this case, a component is no more than a name given to a collection of parts. All the work is done by the parts, and at run time there is no code or other artifact that represents the component.
Use separate components accessed through required interfaces in these situations:
- The requiring component can be coupled through its interfaces to different providing components at run time.
- The design is such that it would be easy to replace one provider with another.
The use of required interfaces is usually preferable to the use of parts. Although the design can take longer, the resulting system is more flexible. It is also easier to test the components separately. This allows less coupling in their development plans.
2. Add interfaces on the components to show the service that each component provides or requires
By placing interfaces on your components, you can separate and name the major groups of operations that are provided by each component. For example, components in a web-based sales system might have an interface through which customers buy goods, an interface through which suppliers update their catalogs, and a third interface through which the system is managed.
A component can have any number of provided and required interfaces. Provided interfaces show services that the component provides for other components to use. Required interfaces show services that the component uses in other components. Examples include a user interface, a Web service, a .NET interface, or a collection of functions in any programming language.
If you define both provided and required interfaces, this helps you separate the component cleanly from the rest of the design, so that you can use these techniques:
- Place the component into a test harness in which the surrounding components are simulated by the test harness.
- Develop your component independently of the other components.
- Reuse the component in other contexts by coupling its interfaces to different components.
An operation in an interface can represent any way in which a behavior of a component can be invoked. It might represent a Web service request, a signal or interaction of some other kind, or an ordinary program function call. To determine what operations to add, create sequence diagrams to show how the components interact with one another. Each of these sequence diagrams shows the interactions that occur in a different use case. In this manner, you can gradually add to the list of operations in each component's interface, as you explore the use cases.
3. Draw dependencies between the components or their interfaces to show the structure of the system
In addition to the components themselves, you can show the dependencies between them in the component diagram. A dependency arrow between two components shows that changes in the design of one could affect the design of the other. This usually happens because one component uses the services or functions that are provided by the other component, either directly or indirectly.
A well-structured architecture has a clear arrangement of dependencies, in which these conditions are true:
- There are no loops on a code map.
- The components can be arranged into layers in which every dependency goes from a component in one layer to a component in the next. All the dependencies between any two layers go in the same direction.
You can show dependencies directly between components, or you can show dependencies between required and provided interfaces that are attached to the components. By using interfaces, you can define what operations are used in each dependency.
- A dependency arrow can be used to indicate that a required Interface on one component can be satisfied by a provided Interface on another, or more generally just between two components, to show that the design of one depends on the design of the other.
- Inside the parent component, each part shows the provided and required interfaces that are defined for its type. The operations or services that are required by one part can be provided by another. You can use Part Assembly connectors to show how parts are connected with one another.
- You can also show that an interface of the parent component is actually provided or required by one of its parts. You can connect a port of the parent to a port of an internal part using a Delegation relation.
Typically, dependencies are shown between components when the diagrams are first drawn and then replaced by dependencies between interfaces as more information is added. Both versions are correct descriptions of the software, but the version with interfaces provides more detail than the earlier version.
Managing dependencies is most important for the production of maintainable software. The component diagrams should reflect all the dependencies in your code. If the code already exists, make sure that all the dependencies are shown in the diagrams. If the code is being developed, make sure that it does not include dependencies that are not planned in the component diagram. To help you discover dependencies in the code, you can generate layer diagrams. To help you ensure that your planned dependency constraints are met, you can validate the code against layer diagrams.