Key Principles for Software Architecture and Design

Defining the Architecture is a challenging job. However by focusing on few Key Design Principles, we can make it easy and even interesting. Here are few Key Principles for Architecture and Design:
  • Separation of concerns: Each component in the system should be assigned a specific responsibility. It means that features should not be overlapped among the components and the users should be very clear about where to go for a certain feature. It avoids interdependency of system components, and make the system easy to maintain. Suppose in future, you find scope of improvement for this feature, then you need not to make the changes at multiple places. But you will make the changes in the specific component, owner of the feature, and all other components will get the benefits as they must already be using this for that specific feature. This approach also makes the application easier to understand for a user.
  • Principle of Single Responsibility: Every component should own one specific and distinguished feature. It helps in defining the responsibilities clearly, helps the user to understand the system easily and also help in integration of component with other components. 
  • Don't Repeat Yourself: This principle states that one functionality and hence the piece of code should be implemented in one component only. It should not be repeated across the components. Uses are that the code will be very maintainable. If you find any error, or you want to optimize the code, there is always one place where you can do it. Otherwise there will be a search in whole code to find out the places where similar code has been done for a feature.
  • Principle of Least Knowledge: Any component should not have any knowledge about the internals of other components. It should be aware only about the public interface of others. It helps to avoid the interdependency on internal working of a component and hence help in maintainability. Suppose if one component knows the internals of other component and program itself by assuming some of the typical implementation being done there. After some time, other component gets a make over and hence change the internal implementation. Now the assumptions considered by other component gets failed and hence that component will also be failed. So always stick to the public interfaces and don't work on internal knowledge of other system components. 
  • Prefer Composition over Inheritance: One should always prefer the composition over inheritance, wherever it is possible. Inheritance creates the dependency between children and parent and hence block the free use of the component. Instead, composition provides a great level of freedom by providing the required functionalities without having any hierarchical relation. Inheritance should be used only when the sub-class should actually be a child of parent class and belongs to same type or category. Inheritance should not be  used just to reuse the functionality already implemented in a class. In that case, composition is always good.
  • Avoid doing big design upfront: Avoid doing a big design for whole system, when you are not clear about requirements completely or requirements can change. It will result into loss of time and efforts and moreover will induce ill effects of frequent changes to design, in form of low grade design with unacceptable quality. Of course, multiple changes always degrade the quality. 
  • Identify components you need in the system: Try to identity the components you need in the system to fulfill the requirements. Right way is to study and decide various design patterns which will be useful and applicable for your system design, and then find out the components required with the  implementation of these design patterns. Components can be like, Workflow Engine, UI Generator, Validator, Report Generator etc.
  • Group components in logical layers: Identify the area of concerns and related components. Then group these related components in logical layers. This logical layers will help the user to understand the structure of the system at high level, interaction between these layers and kind of functionality of any layer. So now whenever user is looking for any specific concern, he can directly jumps to related layer and look for the required component there. Always avoid to mix components of different type of concerns in same layer, like UI layer should not have Database Access components. This will make it hard to understand and hence maintain the application. 
  • Define the communication protocol between layers: The next step is to define the type of communication protocol required between different layers. This requires a complete knowledge of deployment strategy and the production environment. This is very important, as it will help you to define object model further during detailed design phase, with the consideration of serialization, marshaling/un-marshaling cost and network traffic.
  • Define Data Format for a Layer: Now we should define the data format for a layer, using which various components will interact with each other. We should try to keep data format same for a layer, so that various components need not to code/decode the data while communicating with each other.
  • Define interfaces for the Layers for loose coupling: Abstraction among the layers is very important. Layers should be abstract from each other and this can be achieved by defining the interfaces for each layer to interact with each other. If we don't keep this relation abstract, any change in one layer can affect the whole system.
  • System Service Components should be Abstract: Code which is required to provide the system services like logging, profiling, and configuration etc should be abstracted in separate components. This code will be heavily used through out the system, so keeping an abstract layer will always help by separating the concern and responsibilities. 
  • Design Exceptions and Exception Handling Mechanism: It always pay if we define the  exceptions used by various layers and their components in advance. Further, the system to handle the exceptions should also be defined. This will help the components to manage the errors or unwanted situation in graceful manner and the exception management will be same throughout the system. If it is same, work is very easy for us to keep the logs of errors or to send notifications for the exceptional situations etc.
  • Naming Conventions: Naming conventions should be defined in advance to have similar kind of names across the system layers and components. It will help the users to understand the system easily and hence will increase the maintainability. It is very painful to define these conventions at later stage when half of the system is ready and then we just keep remembering the various conventions. Names should be defined in a way, that these should express their concern by name itself. So a user should be able to understand the meaning of existence for a component by its name only.

People who read this post also read :


Post a Comment