This post is the first of a series of posts around Software Architecture. In this series we are going to discuss what is Software Architecture, see different architectures and end up on Domain Driven Development and Microservices Architecture.
In this first post, we will try and have a common understanding of terminology, establish the very first concepts of Software Architecture and provide all the knowledge needed in subsequent posts.
If you read something that you disagree with or don't understand, feel free to let me know. I am open to discussion to learn from your experience and change my point of view if proven wrong.
With that said, let's start with some terminology.
Terminology
In software development, terminology can often be a giant monster of ambiguity and conflicting meanings for the same term. For the remainder of this series, we will use the following definitions for these architectural terms.
Functional Code Unit
A functional code unit is any piece of code, method, or class having a purely technical role in the application. A functional component is not related to the domain of the application, it simply represents a technical capability in the application. Functional Code Units include Layers, Factories, Repositories and Value Objects.
Conceptual Code Unit
A conceptual code unit is any piece of code, method or class that reflects a domain concept in the application. A functional component is directly related to the application domain. In other words, it represents a business capability in the application. Conceptual Code Units include components such as User, Product, and Stock.
This separation doesn't mean that a code unit cannot be both Functional and Conceptual. For example, a User class can both represent a domain object and be designed as an Entity. If the User is referred to as a concept, it means the domain User concept. If the User is referred to as a function, then it means the technical characteristics of an Entity.
Package
A package is a grouping of classes, ideally based on some rules.
Module
A module is a functional package that reflects a technical capability in the application. Modules provide functional cohesion. A module should be decoupled and be able to be swapped by another implementation. Modules can exist at any granularity level. For example, a Security module exists at a low granularity level, while a Client module can exist at a higher level.
Component
A component is a conceptual package reflecting a business capability. Components provide conceptual cohesion. A component, ideally, should be decoupled from any other component and module. Examples include a Product module or a Checkout module. Ideally, a module should also reflect a bounded context.
System
A system is a set of applications that somehow work together to provide functionality to a wide range of enterprise requirements. These applications form an enterprise-wide system or an Enterprise Application. Note that these applications may or may not be built upon the same components. Let's see the example of an e-shop. The system is the e-shop as a whole, including the two applications built on top of the same business components. It also includes other 3rd party applications like payment providers.
Architecture
Software Architecture is the set of structures needed to reason about the system, which comprises software elements, relations among them, and properties of both.
Software architecture includes all technical decisions that affect all parts of feature development, frameworks, coding standards, documentation, processes, and pipelines. Architectural decisions are difficult to change later in the project and are the big picture view of the system.
Software architecture prepares the project for change and reuse. It establishes standards for consistency of results and lightness of processes, such as coding standards, development stages and CI/CD.
Finally, software architecture is not the responsibility of only one person, but of a group, or guild of experienced engineers that belong to different engineering teams in the project.
Indications of Bad Architectural Smells
The concept of "smells" exists within the software engineering community for at least the last two decades. The term code smell is informally defined as certain structures in the code that suggest the possibility of refactoring. A large number of smells in a system lowers the quality of the software and make it hard to maintain and evolve.
We perceive smells at various granularities. Smells can be categorized as implementation smells, design smells and architectural smells based on the scope of the impact made by the smells.
Among these types of smells, architectural smells have the most consequences in the system. The impact of these problems is felt in the entirety of the system and requires considerable effort to refactor. However, managing to refactor such a smell can bring significant benefits.
Some bad architectural smells are listed below:
Rigidity
Rigid software is difficult to change because a change can cause ripples among the whole system, triggering the need for more and more changes. Trying to make such a change can quickly become a rabbit hole. When we believe that we managed to complete the fix, we find more code that needs refactoring, which causes more changes, thus dragging us into a vicious circle of change with no end in sight.
Fragility
Fragile software is difficult to change because a change can break in unexpected, unrelated and unpredictable ways. A fragile software system is often also rigid. Each change can cause a chain reaction of errors in seemingly random parts of the system.
Immobility
Immobile software often contains parts that could be useful in other modules or subsystems, but the effort and risk of separating them from the original system are too great. As you might already figure out, when we encounter an architectural smell, most often than not we will encounter more. For example, an immobile system can also be rigid, or fragile. Or even both.
Viscosity
Viscous software discourages engineers to do something the right way. This implies that it's much easier to implement a fix or a feature by making a hack than to develop it properly. The system might be rigid, fragile, or just a big ball of mud. Oftentimes, a viscous system might be well designed but other characteristics might suffer. For example, a well-designed system, with bad naming conventions and incomplete documentation may make it easier to make a hacky change, than to understand what is going on.
Architectural Patterns are not Panacea
No matter how deep we dive and understand software design and architecture, there are no silver bullets or one-size-fits-all solutions. Try to learn as much as possible about different approaches and how different domains solve problems, understand the pros and cons of each approach and what technical issue they try to solve.
Then, when you encounter a new challenge, start by understanding the business domain and the end-user requirements. Only after having those requirements clearly understood you can decide what architectural styles and patterns you should use to better address the problem at hand.
As Roy Fielding said in his doctorate dissertation:
Some architectural styles are often portrayed as ‘silver bullet’ solutions for all forms of software. However, a good designer should select a style that matches the needs of the particular problem being solved.
Top comments (1)
Hello can I be your friend