Importance of Software Design
There is a clear difference between Software Architecture and the Software Design. As the developers, we should know little bit about the software architecture and lot more on Software Design.
Software architecture will be defined by usually experienced developers who have good knowledge on existing implementation, and have understanding on the existing issues. It will convert software quality attributes such as scalability, manageability, modularity, extensibility, testability etc.. in to solutions which will meet technical and business requirements.
This is why different architecture patterns like Microservices, Even-Driven, Server-less etc.. exist to solve common architectural problems.
Just because we follow good software architecture patterns like Microservices, Event-Driven does not mean our software design and implementation come out good. We have to adapt good design principals and patterns in our implementations too.
Here I want to emphasis on the Software Design which we tend to forget as we go and we only focus on high level Software Architecture.
Software Design
This will focus on the code level design and evaluate against SOLID principals. I will not write about SOLID as there are tons of articles about it.
- S- Single Responsibility Principal
- O- Open Closed Principal
- L- Liskov Substitute Principal
- I- Interface Segregation Principal
- D- Dependency Inversion Principal
This is where design patterns comes in to picture. There are dozens of existing design concepts for most of the problems we have. We do not need to reinvent the wheel. We can model our problem and decide what pattern to use when. It is not straight forward as it says sometimes, there are pros and cons of different patterns, but choosing the correct pattern is the key. You may refer wikipedia’s “classification and list” section.
Lets talk about few common patterns that we can use. I will take 3 patterns to show case the usage.
Factory Pattern: Imagine you want an instance of an Object (This about any class in your implementation). You have two options, you may directly instantiate it (may be thru something like dependency injection) or you may instantiate it via a factory class.
e.g.
1- $processor = new UserProcessor();
2- $processor = ProcessorFactory-> getProcessor(‘user’);
The second option will be more suitable as it will reduce the coupling and will not need to modify the existing client classes.
If you want to go another level up, read about Abstract Factory Pattern.
Adapter Pattern: Imagine your module interact with external API and you have to call that API several times in your code. How do you implement it? The straight forward way is you implement the client and call the client in all the places. What if API changed and return object type changed? If you did not follow proper adapter implementation, you might have to change everywhere that API used.
Strategy Pattern: Imagine you have to select the run time behavior/algorithm based on certain conditions. How do you implement it? The straight forward way is if/else type implementation and check the conditions and execute the algorithm or logic. What if new condition arises? We will have to go the code and add one more if/else check or we will have to modify existing implementation which will break the open closed principal.
Instead what we can do is to follow the strategy (we can mix that with factory pattern too to reduce the coupling more) let code pick the behavior at the run time.
Conclusion
Likewise there are dozens of patterns for the common problems we have in our implementations. If we can model our design and pick the right pattern, our code will be more modular, re-usable and less modification required for changes. Then it will increase the testability and maintainability too.
☕ Like this content? Support me on Buy Me a Coffee!