Software Architecture Design Principles: SoC, SOLID

In this article, we are going to learn Software Architecture Design Principles: SoC, SOLID principles.

SOLID Principles

By this article, we are going to understand main design principles that we can use any software architecture.

I have just published a new course — Design Microservices Architecture with Patterns & Principles.

Introduction — Software Architecture Design Principles

In software industry, we can get projects from the easiest to the most complex projects and solutions from our clients. However, it is often that we fall into the trap of designing more complex systems than necessary required systems. So before we start to design, its good to check our design principles that we can apply on every design. These principles are;

  • SoC — Separation of Concerns
  • SOLID

Let’s see details.

Separation of Concerns (SoC)

Think about that what is the motivation the use N-Layer architecture ? Why we separate the layers to N ? What is the underlying principle to create this N-Layer architecture. This principle is Separation of Concerns (SoC).

Separation of Concerns is a design principle in software architecture that advocates breaking down a complex system into distinct, independent, and manageable parts, each of which is responsible for a specific aspect or function of the system.

SoC says that the elements in the software should be unique to them,
not to share their responsibilities with other staff members. While developing the software, we can see the necessary of separating responsibilities with following this principle.

The idea behind separation of concerns is to minimize the complexity of a system by dividing it into smaller parts that can be developed, tested, and maintained independently, without interfering with the other parts. By separating the concerns, developers can focus on developing each part of the system with a clear understanding of its purpose and the interactions it has with other parts.

We can say that, we distinguish between the concepts of layer and tiers with certain responsibilities. If we go further a little more, the namespaces of our software components can also be used as limits to allocate responsibilities.

The dependencies of the components within the software and
the cohesion within the components are two important concepts for the SoC principle. It should always be preferred that the dependency of the components is low and that the relationship of responsibility in a component is high.

So low-coupling, high-cohesion is indispensable. If the dependency of commitment is low, then the control of the software will be easier in our hand, since the liability will be distributed per component. Being close to each other in their responsibilities within the components will becomes re-usability.
We can give an example of SoC that development of Web UI. As you can see that UI element button needs to configure by 3 structure; HTML — CSS — JS.

Separation of concerns is achieved through the use of layers, modules, or services, with each layer or module responsible for a specific function, such as data storage, user interface, business logic, or communication with external systems. This allows developers to focus on their area of expertise, making it easier to write, test, and maintain software, and also makes it easier to add new functionality to the system without disrupting existing code.

Design principles — SOLID

SOLID is one of the main principle groups to use almost every software projects.

This one though, stands for:

  • Single Responsibility Principle
  • Open-Closed Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

So if you think about it, it’s really 5 principles all packed into one.
So let’s unpack them one by one.

Single Responsibility

The idea is that each of your classes (or modules, units of code, functions, etc) should responsible only one functionality. Why ?

Because if you want to change your code, you should first have a good reason, then you should know where is that single point where that reason applies. We can use this base principles when designing the system also.

Open-Closed Principle

When you write your code, the last thing you want to do is go back to it and change it again and again whenever you implement a new functionality. You want the legacy code to work, be tested to work, and allow new functionality to be built on top of it.

In the same way, when we design the system, it should able to extend without changing existing architecture. For example using pub/sub pattern with message brokers is a good example for that. If we want to notify some other microservices, instead of calling their services, we can publish an event and they can consume them from message broker.
By this way, if there will be a new microservice need to consume this event, we don’t change anything for existing architecture, the only thing to subscribe that event from message broker that’s all. So our design open for extensions and close for modifications.

Liskov Substitution Principle

I am not going to give deep explanation about that. Because we are looking this principles from the system design perspective. So we can say that, our systems can be substitute each other easily.
In our case we can use plug-in services that we can shift them easily, For example if we use RabbitMQ as a message broker, we can shift to Kafka easily with abstracting Event Bus implementations.

Interface segregation principle

It basically says split your interfaces — meaning the desired functionality of your code — into pieces that are fully used.

Dependency Inversion Principle

Also known as dependency injection. If you have dependencies in your code, you should allow for them to be injected into your logic.
How do you do that ? Of course By allowing them to be passed in as parameters.

This is a very useful principle that we can use system architecture design.
We don’t want to dependent microservices and direct communication due to possible network problems. In order to broke this dependencies, we can use message brokers and event-driven architecture that provide to de-couple services. As you can see that we understand SOLID principles and how we can use them when architecting system designs.

Design Layered Architecture — E-Commerce App

If we design e-commerce application with Layered Monolithic architecture, you can see the image below:

There is a big single Monolithic Application Server but Application has logical layers which’s are UI, Business and Data Access. And there is one big relational databases.

Design Microservice Architecture — E-Commerce App

If we design e-commerce application with Microservice architecture, you can see the image below:

Product microservice can use NoSQL document database Shopping Cart microservice can use NoSQL key-value pair database and Order microservice can use Relational database as per microservice data storage requirements.

What’s Next ?

Step by Step Design Architectures w/ Course

I have just published a new course — Design Microservices Architecture with Patterns & Principles.

In this course, we’re going to learn how to Design Microservices Architecture with using Design Patterns, Principles and the Best Practices. We will start with designing Monolithic to Event-Driven Microservices step by step and together using the right architecture design patterns and techniques.

--

--

Mehmet Ozkaya
Design Microservices Architecture with Patterns & Principles

Software Architect | Udemy Instructor | AWS Community Builder | Cloud-Native and Serverless Event-driven Microservices https://github.com/mehmetozkaya