Software Planning 101: Part 4/7
The Granularity Scale for Code Structures
To structure the code of your software, it helps to understand the different granularity scales and their discriminating factors. Code is structured from a top-down approach. First based on business aspects, then based on logical drivers, and last on technical properties.
Granularity Scale 1: EnterpriseDiscriminating factor: owner
First, the code is divided based on business owners; what enterprise does own/maintain/write the software? If you don't own the code, you can't structure it anyway. For small/medium companies their might be only a single unit representing all the code. For large enterprises, likely multiple departments or subsidiaries would have their own enterprise unit.
Granularity Scale 2: SuiteDiscriminating factor: goal
Next we look at the specific business goal of the software. Some software is for enabling a product or product group. Other software might be there to help for systems integration or infrastructure. For small companies there might be a single unit representing all the code. For medium and large companies, likely the code has been structured into different units that have each a specific business goal.
Logical ArchitectureIn logical scope we segregate software to minimize conflicts and effort of future changes.
Granularity Scale 3: SystemDiscriminating factor: release
A suite is divided into systems. A system gets released based on a release cycle. Releases are numbered and best practice teaches us that we would like to minimize the amount of conflicts of simultaneous releases.
To minimize conflicts between releases we try to minimizing the chances on developing any releases in parallel. Suppose we have software that needs to be able for users to order books and handle payments. If we structure this into a single system we might end up in the scenario where a change of the system is underway to implement a new user experience/interface and all of a sudden the system needs to handle payments differently. Then it is hard to make this change as we can make the change to the system, but we get conflicts with the already in progress user interface.
Thus we need to minimize the amount of forces/people that can initiate a new release.
Granularity Scale 4: ComponentDiscriminating factor: responsibility
A system is subdivided into components. Each component has its own responsibility. This makes sure, when a requirement changes in the future, only a few - ideally a single - components are affected and the changes to be made are not scattered around.
Example components of a system:
- client - for consuming an API (translating the protocol and represent it in some programming language);
- api - for exposing the service through some protocol;
- service - implementing the business logic;
- store - for persisting the data entities.
Technical DesignWhen looking from technical perspective, we segregate the software to minimize effort and risk for creating the modules, that implement the components, of the software solution.
Granularity Scale 5: ModuleDiscriminating factor: technology
A component gets implemented by one or more modules. A module is bound to dependencies.
It is probably helpful to have an example. An API component can be implemented for HTTP REST (web) and AQMP (messaging). Although there is only a single component that represents the api, we implement two separate modules. If we would have created only one module to implement both technologies, we will introduce the dependency that a web server must have libraries available for AQMP even if the server is not leveraging it.
The mess gets even worse with a store component: would you like to include Cassandra client, Mongo client and MySQL client code while you are only using a local file implementation?
Note: There are many components who don't have any specific technology variances and thus have only a single module that implement the component.
Granularity Scale 6: PackageDiscriminating factor: coherency
Code in a module is divided into packages. A package contains classes that should have a logical and orderly and consistent relation. This helps:
- understanding of a class - as the other classes in the same package communicate the same message of consumption by the system;
- explains how to extend - it becomes easy to see where and how to extend the software;
- understanding of a module as a whole - it is often hard to remember exactly all classes and how they are used in a module, while understanding the packages should be easy.
Granularity Scale 7: FileDiscriminating factor: version
Likely your version control system works by tracking the differences in files. Issues in having multiple modifications at the same time in the same file lead to merge conflicts. Most of the time this can be prevented by applying Separation of Concerns; make sure each concern goes into its own file. If you are using a language that promotes having a package per file, make sure you choose your package structure in such way you also minimize merge conflicts.
Granularity Scale 8: ClassDiscriminating factor: concern
A package contains classes. Each class should handle a single concern. The class is the unit for unit testing. The more concerns handled in a single class, the harder to understand, test and maintain the code.
Granularity Scale 9: MethodDiscriminating factor: test
Finally a class contains methods. Since unit testing is done around the methods of class, the question is: what logic (statements) do you want to test independently?
ConclusionAlthough the structuring of code can be done in many ways, there are specific discriminating factors on different granularities, that if not given respect to, give you software that is hard to understand and maintain. There is always a trade-off between minimizing the amount of elements per granularity while still minimizing the impact of the discriminating factor.
The right structuring is conveying to a developer what statements should be present in what methods. From practical experience, it transforms programming from "trial-and-error" and "hard work" to a more Zen-activity that just materializes the details of a plan that can already be read in the structure.