11.4 Begin with a Platform-Independent Structure
The way you structure the system is an important architectural decision. You structure the system such that concerns are kept separate. You achieve this structure first from a platform-independent perspective and then refine it with platform specifics. A platform-independent structure is driven by functional requirements (as modeled with use cases).
The tools you use to achieve a resilient structure are classes and use cases. Classes help you keep the elements in a system separate, and use cases help you keep the tasks of each element separate. Accordingly, there are two orthogonal structures in the system—the element structure and the use case structure.
- The element structure identifies where elements of the system are located in the element namespace. It structures the elements hierarchically in terms of layers, packages, classes, and the features within these classes.
- The use case structure defines how you overlay functionality onto the element structure. It comprises slices—both use-case slices and non-use-case-specific slices—that add the actual classes and class features onto the element structure.
You want your structure to be resilient along both structures. This means that if there are changes in requirements, their impact should be localized to a few packages and classes in the element structure. Their impact must also be localized to a few use-case slices. Localized means that there are few changes, and the changes do not propagate beyond those packages or use-case slices that require change.
11.4.1 Element Structure
The element structure for a model is a hierarchical structure of packages and classes. It uniquely identifies each element. Since the goal is to achieve resilient structure, you naturally locate classes that are used for the same purpose together.
You normally use layers as the first-level partitioning in a model. Layers are used to group software elements that are on the same level of abstraction. You place more abstract and reusable elements in lower layers and more concrete or less reusable elements at the top. Normally, two high-level layers are sufficient to refine the functional requirements for the system: the application layer and the domain layer.
The application layer contains elements that realize workflows in the use cases supporting the primary actors of the system. The elements in this layer normally use the elements in the domain layer to realize use cases. You can organize packages in the application layer according to the following criteria.
- Classes that support one or more particular actors.
- Classes that are involved in one or more particular use cases.
- Classes that are involved in some functional area in the system.
The domain layer contains elements representing significant domain concepts. They capture information to be maintained, tracked, or manipulated by the system and the associated behaviors for doing so. These elements are normally shared across use case realizations. They are more reusable and so reside in a lower layer than the application layer. However, since they are shared by use-case realizations, use-case realizations frequently cut across domain elements.
Figure 11-1 depicts the initial structure of the Hotel Management System that realizes the functional requirements of the system. The packages in the application layer are grouped according to actors—the customer, the hotel counter staff, and the management. The packages in the domain layer group classes related to rooms and classes related to reservations.
The structure in Figure 11-1 is an initial one. It is refined further into classes and so forth as you analyze the use cases for the system.
11.4.2 Use-Case Structure
As mentioned, the element structure is simply about identifying elements in a namespace. It is the slices in the use-case structure that overlay the actual content for each element. There are two kinds of slices: use-case slices and non-use-case-specific slices.
The convention is to depict the element structure (comprising layers, packages, and classes) vertically such that at the top you find application-specific layers and packages, and at the bottom you find application-independent ones (as per Figure 11-1). To emphasize the orthogonality of the use case structure, we depict the use case structure horizontally with the non-use-case-specific slices on the left and the use-case–specific slices on the right (see Figure 11-2). The arrows in Figure 11-2 show the dependencies between the use-case slices and non-use-case-specific slices.
Non-use-case-specific slices are derived by exploring the commonalities between use-case realizations. They normally have a close correspondence to the element structure, especially to the lower layers. After all, lower layers in the element structure and non-use-case-specific slices are for the purpose of grouping things that are shared—though shared from a different perspective. This is exemplified by the slices on the left of Figure 11-2. The Hotel Reservation slice adds the domain packages into the element structure. The slices Customer Application and Counter Application add classes to the corresponding packages in the element structure.
The use case slices in Figure 11-2 are derived directly from use cases in the use-case model. Thus, on the right of Figure 11-2, there are use-case slices for Reserve Room, Check In Customer, and Check Out Customer.
Note that the Customer Application and the Counter Application non-use-case-specific slices do not extend the Hotel Reservation non-use-case-specific slices. The former contains classes that depend on or makes use of classes contained in the latter. The former do not extend the latter. Hence, there is no «extend» relationship between them.