My Design Process – Sequence Diagrams
I am currently doing a series of posts where I am documenting my personal software design process. This process has been developed to support the environment that my designs are currently deployed into – a fairly large retail company that needs to control the rate that applications are deployed to the stores in order to limit the number of distractions that threaten to pull the sales staff from their primary role of, well, selling.
I make no claim that this should be viewed as the “one-right way” to design applications. I imagine that I would have a radically different process if I were building publically facing websites for a conference. My goal is to document my process for solving the problems that I am facing.
Currently, my design process flows through the following stages
This stage involves the specification of the high-level components that make up the proposed software system and describes, very generally, how they will interact.
In the requirements gathering stage, all available documentation from the business owners are gathered and recorded. This often includes conversations with program management to ensure that each requirement is understood.
The requirements are then distilled down into individual use-cases that the software system will implement in order to meet the requirements. Also, the components that are likely involved in the use-case are identified.
This stage involves the creation of activity diagrams (aka flow charts) to show how the user and system components will actually interact to implement each use-case. Also, since the interaction of the user and the system are starting to be specified, the structure of the user-interface (aka wireframes) are created at this time as well.
The final, and most time intensive stage, is the creation of sequence diagrams. These diagrams contain the detailed information about how the system operates to implement the process illustrated by the activity diagrams.
A sequence diagram is a tool that identifies the specific, detailed process that an application uses to implement a required set of functionality. There diagrams are created for every swimlane on each activity diagram that we created in our previous step, thus quite a few diagrams are typically required. In the hierarchy of design, this is at the lowest level, lying below activity diagrams and just above the code itself. As a result, it starts to look a lot like a program. However, its role is to establish the messages (or method calls) that are passed between the various objects in the application. It stops short of describing how the messages are processed. When completed, the entire structure of the application becomes known.
As you might imagine, this combination of the number of diagrams and the detail of those diagrams can lead to an extremely large time investment and, if possible, should be avoided. However, there are many times when this level of detailed is desired, especially when the implementation of an application is going to be performed by a team with unknown skill level. Additionally, if the team is highly distributed, then it can be an advantage to have a single team member perform this analysis so that the completed application is more cohesive. One final benefit can be realized if there are doubts about the best way to implement the application. By taking this detailed step, the understanding of how the system is going to function grows considerably. This can lead to a large number of refactorings that increase the organization and clarity of the application’s structure. Since the only things that have to change are the sequence diagrams, the refactoring can be done very quickly, much like reworking a wireframe to meet a customer’s expectation instead of waiting until the applications user-interface is fully coded and then trying to make changes.
So, we’ve talked a lot about what this diagrams are and why they can be useful, so let’s see an example of one and then analyze it. Below is a sequence diagram of how a web application might handle the login process for a user:
We start the diagram in the upper left corner. Notice the filled circle with the arrow leading into the login controller. This is what is called a “found message”, meaning that the diagram doesn’t know where it came from, but it does know how to handle it. In this example, the found message is a POST request to the URL /login. In my designs I use this type of signal to document the URL that the server will be listening on to trigger a certain sequence. The rest of the diagram will the document how the application responds to the request. The found message kicks of a “recursive message” (the object calls itself). Normally, I use this on controllers to document the controller method that will actually handle the request. This method’s signature documents the data that is expected to be transmitted to the controller, in this case the username and password.
The next step in the process is for the controller to call into the model layer and try to retrieve the user via a call to the user model’s “getUser” method. The model layer is normally responsible for managing the business logic for the application. In our example, this involves two messages. First, a call is made to hash the password. We don’t document the method to do that (although we could). We do specify that the hashing is to be done by another method within the UserModel itself though. It returns the hashed password to the calling method, presumably for use later. That “later” happens right away. The model calls the user dao (aka data access object) to retrieve the user from the database. The username is passed through, but the password is implied to be the hashed password from the previous step. If necessary, this can be documented by adding an annotation to the diagram, but I normally try to avoid this, if possible.
The user dao has the responsibility of taking a username and password and returning a User object. This is to be done by interrogating the database and attempting to return the requested instance. The result of this call is sent back to the model object which returns it to the controller.
The controller now has a decision to make. If a user object is returned, then we forward the user on to the home page of the application. If an object wasn’t found (i.e. we received a “null” response), then we send them to an error page where they will probably be given the opportunity to try to log in again.
This example is certainly incomplete, but it illustrates many of the important points of a sequence diagram. One critical point that I haven’t mentioned is what this diagram does not do. Notice that this diagram doesn’t take responsibility for where the request comes from or exactly how the response is handled. Those details are the responsibility of other parts of the application.
The final thing that I should mention is that, as powerful as sequence diagrams are, they are not the only set of artifacts that are generated in this stage. Sequence diagrams are great for the objects in the application that are function providers, such as controllers, models, and daos. However, we don’t have a good way to show the contents of the User object that is retrieved by the dao in our example. This is an important element of the design and needs to be described as well. In order to finish our example, let’s include that as well.
A class diagram is a tool that describes the internal structure of classes and how classes relate to each other. While there are a lot of things that we can describe with these diagrams, this simple example shows most of the elements that I normally use. Notice the User class denoted by the rectangle on the left hand side. Below the name of the class is a list of the data fields contained within the class. The minus sign indicates that they are private to the class. These signs are followed by the name of the field and then the data type that the field will hold. The next section shows the methods on the class. They are described using similar syntax that is used for the fields. Within the parenthesis, we describe the arguments that the method will accept and the types of those arguments. After the parenthesis, we list the type of data returned by the method, if any is present.
The final thing that I want to call out is the linkage between the “Role” class and the “User” class. This line indicates some type of relationship exists between the two objects. Specifically, in this example, the diamond symbol indicates that the User class contains a list of Role objects.
As you might imagine, building out this level of detail throughout an entire application takes a considerable amount of planning and effort. However, it pales in comparison to the effort required in writing the application itself. This fact, when combined with the relative ease in which the design can be adjusted and refactored makes it a valuable step in the process when the designer needs to be very clear about how the application is to be constructed. However, there are many cases when this level of detail isn’t necessary. If the development team has a strong standard for how applications are to be designed, or they are skilled in modern best practices, then the cost of this stage might not be justified.
I hope that the last few weeks have proven helpful. Maybe they have provided insight into a process that you can find application for. They may have also helped you understand why this process is not the correct one for your situation. Either way, I appreciate you taking the time to read this series. Till next time, happy coding!