I want to make clear at the outset that I am not an expert in 4+1. I have spent the last few months working with systems engineers on an Australian rail project, and this is the sum total of my systems engineering experience. I am reasonably happy with my understanding of both concepts as they apply to my specific situation, but this approach my not apply to your problem domain. I am using StarUML for my modelling, and this pushes the solution space in certain directions.
2008-09-08: So that was then, and this is now. What would I change after having a months to reflect? Well, I think it depends on what you are trying to get out of your modelling. The approach I initially outlined is probably OK if you are trying to cover all bases and get a lot of detail. However, now we might step back into the product space for a while. What we want to do is get away from the detail. We want to simply communicate the high level concepts clearly.
Overview
The first view of the 4+1 model for architectural descriptions is the Logical View. A classical computer science or software engineering background may not be a good preparation for work on this view. It has its roots more in a systems engineering approach. The purpose of this view is to elaborate on the set of requirements in a ways that encourages clear identification of logical functions, and also identifies interfaces to other systems or subsystems.
The general approach that I have been using for this view is a combination of data flow and robustness diagrams. The elements in these diagramming techniques are similar, but both are constrained in my approach to serve separate specific purposes. Robustness diagrams are drawn by working through an input requirements specification. Out of this process "functions" are discovered and added to a functional data flow diagram. Finally, a context diagram is constructed as an extraction from and elaboration on the function data flow diagram with external systems identified.
Interfaces are identified at every stage through this process, including Human-Machine Interfaces. The structure of robustness diagrams make it relatively simple to identify missing interfaces and other gaps in the analysis. The interfaces identified are reflected in the following Process View.
The first thing I would change here is that I would split the concept of a 4+1 Logical View and the systems engineering -style functions diagram. The second thing I have been doing has been to try and limit my content to a single diagram. I'm trying to contain the urge to reach for crinkly goodness in favour of saying "just enough".
Context and Function Data Flow Diagrams
Data Flow Diagrams are very simple beasts, made simpler by the constraints I apply. For the purpose of identifying functions we use only circles for functions, and directed associations for data flows. Other features such as data sets or identified interfaces are banned in the function and context data flow diagrams. Data flows are labelled with the kind of data being transferred, and functions or systems are named.
Some guidelines for the definition of functions:
- An interface is not a function. You should not be able to draw a function that has the same name on an "in" data flow as it has on an "out" data flow.
- Configuration data and HMI are part of a function, not functions in their own right. We are setting out to describe the behaviour of the system in its completed state, not to design the system in this view.
- Non-functional requirements are implicit in this view. Don't try to draw these. This view may even help you distinguish between functional and non-functional requirements. Non-functional requirements and functions that are separate from their configuration data will want to be involved with every function and every control. Functional requirements, much less so. We are trying to come up with a set of functions and functional interfaces in order to maximise functional cohesion and minimise coupling. However, again we are not designing the system. The design in later views will follow the lines of these functions, and will elaborate upon them with solutions to non-functional problems.
- Functions are not use cases, nor necessarily collections of use cases. A use case can span multiple functions. A function is a cohesive set of requirements that is further elaborated in its robustness diagram or diagrams.
- The border between adjacent functions is always a data flow, never a control flow.
The set of functions should describe the system as the user sees it, rather than how it is built. The data flow diagrams describe relationships between systems or functions, not between software or hardware components. As additional constraints on these data flow diagrams I do not allow the expression of data stores at this level. They are described in the Robustness diagrams where necessary. This level is purely about flows of data between units of behaviour. The flows do not discriminate between REST-style, database-style, API-style, or any other style of interface. How the interface itself is expressed is a design decision.
Unfortunately, I was on crack when I wrote some of this. In particular, I have the concept of functions almost completely backwards. What I ended up calling functions are more like subsystems and... well.. you get it. I have since been corrected in a number of areas.
Here is how I would draw the robustness diagram today:
The context diagram remains the same, and I would continue to include it. I have started to prefer describing interfaces in terms of the information they communicate rather than trying to preemptively name and collate them based on other factors.
Here is a functions diagram, where I have collected the functions back down into a single figure. The key interfaces are still present, and I have two basic sets of data in the system. I have the ledger data, which is based on pure accounting principles. The other data is the historic share prices.
The share prices are acquired and stored based on live stock quotes. This information is combined with ledger-based accounts data for the shares investments in order to generate appropriate reports.
Other sources of data for accounts include the daily sales ledger acquired from the Point of Sale system, internal movement of warehouse stock, and input from stocktake operations.
Per-function Robustness Diagrams
Robustness diagrams consist of boundaries, entities and controls. Boundaries are interfaces to your system, including Human-Machine Interfaces. These are functional interfaces, so typically identify broadly the kind of information required or the kind of control exerted through the boundary. The set of boundaries may be refined over subsequent iterations of the logical view, but do not be concerned if they do not line up exactly with the set you expected. Multiple functional HMI boundaries may be combined into one user interface to provide the best user experience. Multiple software or protocol interface boundaries may be described in a single Interface Control Document.
The process of building up the functions involves reading through requirements one at a time. Look for vocabulary in the requirements that suggest a system boundary or entity. A single requirement may appear as an entity in your diagram, or many requirements may map to a single entity. Only a poorly-written requirement should require multiple controls to be associated with it. Entities are identified when one requirement talks about producing some kind of data to be used by another. An entity is a set of data, such as a database. We are not trying to build an Entity-Relationship model (I draw entity relationships outside of the main 4+1 views). Once the data set is identified it is not elaborated further in this view. An entity may be temporary or permanent data.
Some guidelines for the Robustness Diagram
- Controls bind everything together. Boundaries do not touch boundaries or entities. Entities do not touch entities. A control must be present between these end-points.
- Don't try to chain too many controls together. In general I would expect only a simple flow from one or more boundaries or entities through a control to one or more boundaries or entities. Requirements-mandated validation behaviour is a common exception to the rule, where the validation is subservient to a main control. It is especially useful to do this if the validation interacts with a different set of boundaries and entities to the main control. However, don't attempt to describe an algorithm by linking controls together.
- Don't go overboard with entities, controls, or boundaries. If your diagram has the same controls always operating on two different entities or boundaries, they may need to be collapsed into a single entity or boundary. If two similar controls are relating the same set of entities and boundaries, consider collapsing them into a combined control. The main goal is to establish how concepts from the requirements specification are related by behaviour. The requirements themselves are still there to describe the behaviour in detail.
- Be suspicious of a control that is not driven from anywhere, and entities that influence behaviour without obvious input. Look for missing functional HMIs and other unidentified system boundaries.
- Don't try to make every requirement a control. Many requirements are there to clarify or constrain a general functional behaviour. The goal of controls in these diagrams is to capture specific uses in order to bind the concepts held in boundaries and entities functionally together. We thereby discover the functionality required of known boundaries, as well as possible missing boundaries and implied behaviours.
- As entities are data, they are candidate points for splitting a large robustness diagram into multiple functions with a data flow between. However, avoid splitting cohesive controls apart unnecessarily. Try to describe a logical set of behaviour in a single diagram and as a single function.
- Robustness diagrams are not use cases, and are not collaboration diagrams. Directed associations can be useful in showing control flow from start entity or boundary through controls to their destination. However, ordering is not implied, nor is frequency or method of control flow. The level of granularity of these diagrams only allows us to say something like: "When data changes across this interface or in this entity, this kind of behaviour will happen involving these other boundaries and entities".
- Do not try to identify functions in the robustness diagrams themselves. Functions and controls are different concepts. Functions are behaviour with information exchange with the next function. Controls are behaviour that pass control flow to other controls and update entities or boundaries.
- Do not design internal structure into the system. Boundaries are system boundaries, not function boundaries. Entities are identified naturally as domain concepts out of requirements. They are not invented.
This is where I would really start to split from my original description of the Logical View, and move closer to what Phillipe originally suggested. That is, an object-oriented approach. We design a set of classes that we would implement if we didn't have to worry about any of the details. We wouldn't worry about redundancy. We wouldn't worry about performance. We wouldn't worry about client/server separations that might be present in the real system. This is an idealised software-centric view of the solution.
Example
The example I am using is of a very simple accounting system. I haven't written up formal requirements, but let us assume that the following model is based on a reasonable set. I will work though this example top-down, generally the opposite direction to the direction the model would have been constructed. The source model can be found here (gunzip before opening in StarUML).
The context diagram shows the Accounts system in context with an adjacent Point Of Sale System to acquire a daily sales ledger, and a broker system to provide live stock quotes. If I were presenting this in a formal document I would re-draw it in visio. Packages would be drawn as "function" circles, and interfaces would be removed (leaving only data flows). Since we are all friends, however, I'll leave it in the model form for the purpose of this entry.
Already we can see potential gaps. We get live stock quotes from the broker's system, but no buy or sell orders. Is this intentional, and the buy/sell orders are handled entirely within the Broker System or have we missed it from our analysis?
The set of identified functions tracks these external interfaces down to a lower level. The stock quotes from that broker system are being fed into a shares investing function. Sales receives the daily sales ledgers. Inventory is a self-contained function within the system. Tax Reporting uses data from all of these functions.
It is possible that a general ledger function could appear at this level, but so far in this analysis we have not determined the need for it. The system is used for shares investing, sales and inventory tracking. Any general ledger containing the combined records of all of these activities is a design decision at this stage. Tax reporting requires that we bring the data together in some form, but whether we mine it directly or convert for easy access remains unconstrained by this logical systems engineering process.
The Sales function has two main controls: To import the daily sales ledger from the POS System, and to generate reports. A historical sales ledger entity naturally appears out of this interplay, and becomes the boundary between this function and the Tax Reporting function. We discover a HMI is required to request report generation. Does this HMI need to do anything else? Is printing required? If so, do we need to be able to cancel print jobs? Are these functions of the system?
The Inventory Function has four controls. Two are relating to scanning stock in and out of stores. Another two are related to stock-take. Stock is re-scanned and compared against Inventory. Once all stock is scanned, the Stocktake HMI accounts for any shrinkage. Both the Inventory Scanning and Stocktake HMIs can take a number of forms. There might be keyboard and VDU entry. With any luck there will be a bar code or rfid scanner in the mix. The same scanner might be used as part of both HMIs, and both HMIs may be combined into the one end user HMI.
Investment Tracking involves recording of purchase and sales of stocks. We can run reports based on the book value of these stocks (ie, how much we paid for them), or based on their current market value. In order to do the latter we need to keep a historical record of stock prices. Maintenance of this record has a clear entity boundary at Historical Stock Prices that makes it a candidate for separation into a new function. We still might do that if this function becomes unruly. I have placed a kind of stub interface in here for periodic execution of stock quote updates. However, this obviously needs more thought. Where is the HMI to control how often these updates occur? Can they be triggered manually?
I foreshadowed that the entities from other functions are referred to directly in the Tax Reporting robustness diagram, and here it is so. This does not directly imply that the Tax Reporting function has access to the databases of the other functions. It simply means that the set of information (or relevant parts of that set) is transferred to the tax reporting function at appropriate times. This could be by direct database access, by an ETL into the tax reporting function's own database, by a RESTful data exchange, or by some other means.
And this is how I would do it, now. This is a very basic diagram, but you can see that it is software centric in that it balances data with functionality, and views the world as a set of classes and objects. I would generally start with an entity-relationship or a class diagram relating to domain-specific vocabulary from the requirements specification, and work from there.
To some extent I find this diagramming technique freeing. I don't have to worry about the borderline between software and systems engineering. I don't have to worry about components. I can just draw as I might have in my youth. It will feel familiar to software developers, and a software developer should be able to judge whether or not it works to convey the appropriate information.
Conclusion
The Logical View is somewhat airy-fairy, and the temptation is always there to introduce elements of the design. Resist this temptation and you should end up with a description that clearly separates constraints on your design and design decisions you have made. The set of interfaces your system has with other systems is a real constraint, so all of these interfaces appear here in some form and behaviour required of those interfaces is identified. You may need to round trip with the process view (and other views) in order to fully finalise this view.
It is likely that the Logical View (especially its robustness diagrams) will identify some inconsistency in vocabulary in your source requirements. It is worth putting at least a draft of the design together before finalising requirements for any development cycle.
I think that the context and function diagrams are quite useful in helping flesh out the scope of a system as part of a software architecture. The Object-Oriented nature of the real Logical View is a help to software developers, and the description of the problem domain vocabulary in this context should help stakeholders get a feel for how the system will address their problems.
previous in series, next in series
Benjamin