Demystifying the SOFTWARE ARCHITECT: In Plain English
By: Probal Dasgupta
Tags: Software Architect, Software Engineering, Software Architecture

A Software Architect creates a comprehensible “whole” out of diverse and disparate elements. Just like the image above.

A Software Architecture is not any of the following personas (although some aspects of each of the following are included in the scope of software architecture):

· Enterprise Architect

· Solution Architect

· Application Architect

· Information Architect or Data Architect

· Security Architect

· Cloud Architect

THE GOAL

In plain English, the goal of a Software Architect is to construct software that:

· Does not break easily.

· Is secure and difficult to hack or misuse.

· Is sustainable (i.e., can evolve and not become irrelevant for a long time).

· Meets necessary and applicable industry standards.

· Interacts easily with 3rd party software, and with which 3rd party software can interact easily.

· Is easy to develop quickly.

· Is easy to maintain — i.e., changes do not take a lot of time.

· Meets the response time requirement of the users.

· Is auditable.

· Reduces the cost of ownership.

THE SCOPE OF SOFTWARE ARCHITECTURE

Based on the goals defined above, the scope — still in plain English — could be stated in the form of the following incomplete list (in no particular order) of issues on which the Software Architect must weigh-in:

· Design choices.

· Platform to be used.

· Programming languages to be used.

· Adopting third-party frameworks or creating one of our own.

· Datastore and the general approach to data modeling.

· Tools to be used.

· Interoperability standards, and the engineering of API and SoA/Microservices design.

· Define security standards and design defense.

· Quality standards.

· Cloud deployment strategy.

· Approach to code composition; UI code composition adopting MVC or an MVC-derived pattern.

· Technical standards, such as naming conventions.

· Coding standards; Readability and refactorability of code.

· Performance and latency in all tiers and platforms.

· Scalability and redundancy; handling spiky traffic patterns, autoscaling.

· Application-specific data model.

· Capacity planning.

· Logging, telemetry, and other instrumentation techniques.

· User interfaces and their maintainability.

A Software Architect must be prepared to defend his/her choices and be ready to be held accountable for the outcome.

HOW IMPORTANT IS DATASTORE?

The unenlightened Software Architect or rookie developer will typically start designing the database schema before most other activities because the generally prevalent thought is that the datastore should be the most stable component since it is the hardest to change. If one considers the three most important components of software to be (a) the User Interface, (b) the Business Logic, and (c) the Datastore, the typical developer might represent their dependency as follows:

Wrong!!! An organization runs on Business Logic, not on its Datastore. So, all components of the software must be dependent on the Business Logic. Business Logic is King. Everything else revolves around it. So, the dependency diagram should be as follows.

Therefore, it is extremely important that the Datastore should be easy to change. Because it is dependent on the evolution of business logic. Therefore, it is critical that the Software Architect has to design the Datastore the right way.

Using a NoSQL database instead of a traditional Relational Database could be a benefit because schema-less databases are more amenable to change. However, NoSQL is not a silver bullet. The Datastore Architecture should consider some of the following topics:

· Dependency inversion. (Datastore imports the definitions from Business Logic).

· How will one organize data persistence? CRUD (Create, Read, Update, Delete)? CQRS (Command Query Responsibility Segregation)? Event Store pattern?

· Compatibility with 3rd Party Tools for reporting, analytics, ETL, etc.

DESIGN MUST BE DOMAIN-DRIVEN

This is often ignored. Ideally, a Data Dictionary or a Domain Definition Dictionary should first be built. But I see system after system developed with no concern for a formal data dictionary. However, this is most important and the Software Architect is expected to ensure this. All the “nouns” and “verbs” of the industry and/or the business should be defined in the Domain Definition Dictionary. E.g., a noun could be “SKU”; a verb could be “port services”. Only after a common dictionary — that both business users and technical personnel agree on and refer to — is created can one begin to define the data mode or write any functional or technical specifications.

This approach is called the Domain-driven Design approach to architecture and design and is highly recommended.

THE APPLICATION OF OBJECT-ORIENTED DESIGN PRINCIPLES

The five famous principles of Object-Oriented Design still apply because the separation of concerns and reducing complexity by operating at a higher level of abstraction remain our guiding principles.

· S: The Single Responsibility Principle (SRP)

· O: The Open-Closed Principle (OCP)

· L: The Liskov Substitution Principle (LSP)

· I: The Interface Segregation Principle (ISP)

· D: The Dependency Inversion Principle (DIP)

These principles apply at different levels — from splitting our code into modules to creating class hierarchies, to breaking our code into microservices and designing APIs.

Experts warn us that these are NOT patterns but principles. They cannot create a good architecture but will prevent us from falling into bad coding practices.

· The development team must understand and apply the SOLID principles.

· If any of the principles is violated, then the code perhaps needs to be refactored.

ADOPTING FRAMEWORKS

To get work done faster, the software architect must adopt existing and proven frameworks. The frameworks are usually produced by third parties, frequently open-source, and sometimes the architect can develop some of his/her own. Frameworks are double-edged swords. Selecting the right ones for the job will save us a lot of time and money. On the other hand, a poor choice of a framework may lead to significant losses in productivity and operational risks.

· Most of the frameworks are general purpose and can be used for a wide variety of application domains (or so they claim).

· Some frameworks pack a significant amount of complex code and algorithms, and some create standards extension by third-party developers — both of which can save us tons of time and money.

· Most language-specific frameworks are distributed as code libraries, which are then bundled with our application. Such frameworks are usually tightly integrated with our programming language for maximum performance.

· Language-neutral frameworks are usually installed separately server-side and work with our application through a set of APIs, usually HTTP-based.

DESIGNING THE USER INTERFACE

The architectural choice here is not as simple as just picking a UI Framework like Angular and a Server-side Framework like ASP.net or Java Spring because UI development is one area where most development cost is sometimes incurred. So, the software architect must pay special attention to this area and architect it right.

Types of UI required might be some or all of the following:

· Web browser

· Mobile

· Desktop (native apps for Windows, Mac and Linux)

· Messaging platforms (SMS, Skype, WhatsApp, etc.)

· Voice (for IVR or VRU applications)

Each of these areas must be addressed by the Software Architect.

SHARED CSS OR PROGRAMMABLE SKINNING?

The path of least resistance is to continue using the traditional recommendation of shared CSS. It is easy; if one changes the CSS, everything changes automatically.

But experts say: shared CSS is a terrible idea for large software systems because of a long list of reasons that is outside the scope of this brief note. A better way might be to make our UI components directly skinnable. One can also break skinning data into two sections — Individual Skin vs. Ambient Skin.

RESPONSIVE DESIGN

Of course, the UI must be Responsive. Right? But Responsive Designs are not a silver bullet; they are insufficient in many cases. For instance, entire end-user workflows may require a different approach for desktop vs. mobile.

INTEROPERABILITY RULES

The application almost always must interact with 3rd party apps, and therefore, consume other parties’ APIs and expose our own to be consumed by 3rd parties.

Here are some API best practices for the architect’s consideration:

· Unified approach to all API calls.

· Error handling.

· Stability and backward compatibility.

· Ability to submit multiple calls in a single request .

· Ability to ship client-side logs to the server alongside with any request.

· Ability to ship server-side logs to the client alongside with any response.

· Ability to programmatically analyze error responses.

· Ability to create new API versions instead of making breaking changes.

· Security.

GOOD OLD SERVICE-ORIENTED ARCHITECTURE (SOA)

SOA, like OOPS, is not likely to fade away as long as using granularity and objects as a way of managing complexity and increasing flexibility remains a design principle. Today’s microservices are based on the concept of SOA.

But while SOA breaks down software into granular software components, Microservices proposes to replace software components with API endpoints, which would contain the target objects or components. This, when first proposed, was very exciting and quite revolutionary. Instead of importing a module and using its functionality directly, one would now be calling an API, usually via HTTP protocol. But, again, there are no silver bullets.

Here are some benefits of Microservices for the architect’s consideration:

· Use containerization to easily deploy services (which is the benefit of containerization, not of microservices themselves).

· Services can be coded in different languages, as the services are technology and infrastructure agnostic. This is almost the only reliable way to mix different programming languages in a single server-side environment.

· Each service can be scaled independently (true, with a caveat: scaling a “flat” microservices infrastructure is easy; scaling daisy-chained microservices could be extremely challenging).

· Services can be developed independently from one another (true, although this can be achieved without the complexity of microservices: independent development of components can be facilitated through a basic discipline in applying SOLID principles).

Here are some problems with Microservices for the architect’s consideration:

· API calls over the network are slow and may lead to network congestion.

· Managing dependencies could be hard; there is no compile-time detection of incompatibilities.

· It is much harder to test and monitor than traditional architectures.

· It is nearly impossible to carry transactions across API calls.

· Insufficient scaling of one service leads to scalability problems and frequently to a failure of other services that depend on it — what is known as “backpressure.”

In the final analysis:

· Microservices are infrastructure and deployment strategy, not an architecture pattern.

· They are great for building APIs and integrations.

· But microservices could be dangerous if not appropriately architected.

· Therefore, one must be cautious in architecting microservices into our SOA structure.

· If not necessary, one should stick to standard SOA.

SECURITY

Security is integral to software architecture. Ad-hoc design elements will break architecture if one tries to retrofit security later.

The Known Unknowns:

· It is not possible to accurately predict all security risks. The caliber and value of protected information may say something about the level of potential threats when comparing to similar systems currently in operation, but no one knows how often a system will be attacked. Hackers are people, and their interests, whether financial or political, will change.

· It is not possible to eliminate all security risks. Future risks are always unknown.

· It is not possible to ensure that people operating the security of a software solution will correctly set properties. Such mistakes can be malicious (e.g., malicious system admin) or accidental (e.g., sophisticated technology). Automating security to the highest degree possible is a good practice, but it is impossible to completely remove human intervention.

· It is imperative to think about the future of security maintenance at design time, especially with respect to interoperability and maintainability quality attributes.

Some best practices:

· Defense-in-depth. Always have more than one technology procedure for defense. E.g., multiple vendors for firewalls.

· Establish zones of trust.

· Least Privilege. At the start, nobody can do anything. Then give rights as necessary. E.g., install a firewall and then open ports as necessary. Need to establish a reason or need that you have a need for access. Every resource is denied unless explicitly given.

· Simplicity. The more complex something, the more likely users will try to avoid it. E.g., auto generated passwords are too complex to manage. If users don’t like it, they will find a way around (e.g., by writing down a password on a piece of paper and keeping it on a desk).

· Balance risk with cost. If security costs more than the data one is trying to protect, it is not worth the price. What is the opportunity cost? What are the tradeoffs among alternatives? One needs to balance risk. Every system has a different tolerance level. Every company has a different tolerance for risk.

· Protect information in transit and in storage. Many passwords are stored secure but transmitted clear or with weak encryption mechanisms.

· Never believe you are totally secure.

FINAL POINTS

A software architect should:

· Be Technologically agnostic. The less enlightened Software Architects recommend technologies, methodologies, and approaches for solving problems that they know best (or wish to explore for their own learning), not because they are the best solution for the problem.

· Simplify. Not make it more complex, because that appears smart.

· Sometimes the biggest problems are not technical problems. One must look beyond the technology.

· Know that EVERYTHING one designs will ultimately fail. So, one must design with that in mind.

· Quantify requirements. “Fast” is not a requirement; “3 sec response time” is. Similarly, “Responsive” is not a requirement, “extensible” is not a requirement. If one cannot quantify it, it is not a requirement.

· Avoid the allure of the LATEST TECHNOLOGY just because they are exciting and make large claims. One needs to challenge its usefulness.

· Similarly, avoid reusing old constructs and old practices just because they are proven. One needs to challenge its usefulness as well.

[Acknowledgment: The diagrams in this article have been drawn based on schematics provided by Anatoly Volkhover in his wonderfully educative book “Become an Awesome Software Architect,” which has also been the source of some other ideas. The graphic, in the beginning, is taken from Katie Watters on Pinterest.]