Today, I would like to introduce you to the world of application design.
Designing applications can be called a multidisciplinary task, and it includes:
- a study of design patterns
- a study of rules for describing architectural schemes
- knowledge of algorithms and data structures
- knowledge of distributed computing principles
I want to consider the question — Why should we build an application architecture? I will ask 4 additional questions, which will hopefully answer the main one:
- What main components (modules) can an application be divided into?
- How to share responsibility between components?
- How to prepare the application for future changes?
- How much will it cost to develop?
The question “Why divide” arises? It will allow us to:
- Reduce the interdependence of parts of the code
- Provide code testability, especially in case of unit tests
- Clearly delineate the responsibilities of each component
- Identify frequently used components and make them common (using polymorphism, in particular)
- Determine which pattern (abstraction) to apply to an application
Once we set goals for the application architecture, we can start preparing Sequence Diagrams. Sequence Diagrams are great for translating business requirements into a distinct set of operations between various application components. Also, given Sequence Diagrams of the app, we could refactor architecture & planned functionality to reduce complexity & increase the app's effectiveness even before writing a single line of code!
After defining the application's tasks and functions, we turn to a Component Diagram with a description of the components themselves and their relationships.
I mentioned polymorphism and design patterns. And on these topics, I would like to elaborate because entering abstractions into the code allows distinguishing components and their roles in the application more clearly.
Among the design patterns, I would like to highlight the following:
- Inversion of Control — which is actually an architectural principle
It is a structural design pattern that provides a simple interface to a complex system of components (classes, libraries, etc.).
This behavioral design pattern defines a family of similar algorithms and places each of them in its own class, after which the algorithms can be interchanged right at runtime.
This is an architectural principle where the framework controls the program control flow, and the custom code is embedded at specific execution points. ReactJS is a good example of IoC principle usage. Much of the application logic is implemented and controlled by the framework itself, so the software developer's sole responsibility is to alter the framework’s behavior at provided points (useState, useEffect, etc.)
With the architectural schemes and TOR in place, you can already begin to plan tasks for implementation and provide for future changes. Here, I used the word “provide” intentionally. Of course, architects are not visionaries, but as they get more experience, they learn to see and predict the vector of the application. In practice, few tasks require a fundamental redevelopment of the application or the business model — unless, of course, we are talking about an MVP or some unique features that need to be implemented.
Also, by applying a certain abstraction, we already have predictable tools to implement most ideas, and this allows us to keep time-to-market on a certain level.
And this is one of the key questions that the architect and architectural schemes are trying to answer. The cost of software development itself depends on many factors, but the main one is the programmer’s time needed to write a working application with which the end-user can already interact. The architect's task is to define — together with the business and project managers — a minimum sufficient set of functions for each stage of project development. The next task is to proceed to develop the schemes of each of the parts (modules) without losing focus on the entire application as a whole and the features implemented in the late stages of development. It is possible because you can create a super tricked-out application on paper but fail to implement it within a reasonable timeframe.
All this time, I’ve been mentioning the architect, and you may have the question — Who is the architect? In my understanding, an architect is a person with a lot of development experience at different levels and with analytical skills. And no matter how controversial it sounds, every member of the team can be an architect at different stages of making architectural diagrams.
For example, flow diagrams should reflect a specific implementation of logic, but if you have a multi-component application, preparing only flow diagrams for the whole logic can take an indecent amount of time. So it is important to delegate this work to the developers of this very logic. It will allow both parallelizing the work and identifying the pitfalls. After all, the developers have expertise in their subject areas and can identify controversial points in the whole architecture.
However, when delegating authority, it is important to give one person or group of people the right to make or reject changes in architecture because only architects have the fullness of the picture.
To summarize, maintaining architecture is a team effort, and the more involved the developers are, the more predictable the development itself becomes.
Of course not! Development requires creativity at every moment, and it is not uncommon to have to change the plan and the code on the fly. Still, at this point, it is crucial to calculate the change in the architectural plans and, if all is well, start the development.
The architecture of the application is a living organism, and we should support it all the time.
Therefore, the architectural plans are developing all the time as the application itself is developing.
That’s all I have. Thank you for your attention.
Previously published at maddevs.io.