image from Software Design Patterns - A Compendium of Knowledge

Software Design Patterns - A Compendium of Knowledge

In our industry, there are battle-tested software design patterns. You can use them to solve problems that you’ll undoubtedly encounter at some point in your product’s lifecycle.

Additionally, there’s no need to reinvent the metaphorical wheel 😀

You can read the other articles in the Tech Lead series here: link.

Why Use Design Patterns?

Here are three major arguments that encourage the use of design patterns:

  1. Developing architecture in the right direction
  2. Defining the main behaviors of the application
  3. Reducing cognitive load

Developing Architecture in the Right Direction

Applications created without a target architecture can be recognized by their strong coupling between components. They are brittle, difficult to change, and lack clear vision or direction.

Choosing an architectural pattern before starting to write code is crucial for the success of the product (although it doesn’t guarantee success 😅). This allows you to answer the following questions regarding system development and refactoring:

  • Is the architecture scalable?
  • What are the performance characteristics of the system?
  • How quickly can we introduce changes?
  • What is required to deploy the system?
  • How much load can we handle?

Using specific patterns helps you avoid the pitfalls of the “big ball of mud” and creates an application that is easy to maintain and develop.

Architectural drivers will certainly help in choosing the right pattern.

Defining the Main Behaviors of the Application

Patterns are tools aimed at ensuring specific system behaviors. They enforce particular features and behaviors. Thanks to them, we can impose a chosen implementation method.

For example, integration patterns require additional technical work within service collaboration. At the same time, their use relieves the burden of implementing individual functionalities. You handle inter-service communication, and everything happens in the background.

You use ready-made solutions that we often benefit from without even realizing it.

Reducing Cognitive Load

Patterns define a stable model of behaviors. They facilitate application design by providing proven solutions. As a result, developers don’t need to spend as much time and effort solving recurring problems, which reduces cognitive load.

Team members know what to expect from a product using a given pattern. We can consistently develop the system with more people, making team onboarding simpler. It also becomes easier to share information with other teams.

Patterns

For this article, I’ve divided the described patterns into four types:

  1. Application Patterns
  2. Integration Patterns
  3. Microservices Patterns
  4. Distributed Transaction Patterns

For each area of patterns, I’ve prepared a brief description and links to further materials.

I deliberately do not describe individual patterns in depth here. The creators I link to have done a better job of that 😉

Remember, each pattern has its strengths and weaknesses. Choosing the right pattern is crucial for the quality of implementing new changes. Therefore, it’s important to thoroughly understand each pattern and then choose the one that best fits the business specifics and project goals.

Without this, you won’t get far!

Application Patterns

Application patterns are fundamental elements of application design. When designing within a single application or module, we focus on internal structure and response to specific requests.

Some of the most interesting application patterns include:

  • CRUD: Often used for simple database operations.
  • N-Layer: Helps organize code with a focus on technical layers.
  • Hexagonal/Clean/Onion: Provide isolation of domain aspects.
  • CQRS: Helps separate read and write queries.
  • Vertical / Feature Driven: Help organize code according to functionality, making maintenance and development easier.

Integration Patterns

As Gregor Hohpe said in one of his presentations:

How your components are connected defines the fundamental properties of your system.

Integration patterns define ways to solve problems between independent services, facilitating communication and addressing main inter-process communication issues.

Moreover, integration patterns enable consistent communication definition within organizations, easing both internal and external collaboration. They also form the communication foundation for many libraries and cloud services.

Knowledge of integration patterns is crucial for creating scalable, efficient, and stable IT systems.

There are over 60 defined integration patterns. Notable ones include:

Recommended Book:

Microservices Patterns

Microservices patterns focus on designing a system divided into many independent deployment units, i.e., microservices.

Each of these services is responsible for managing internal business logic and data structure. Externally, they expose an API for communication with other services. Microservices are usually focused on a specific business area and managed by one team.

Such a division into smaller units allows for better scalability, easier management, faster change implementation, and increased independence of each service. Microservices patterns are increasingly used for large and complex systems where easy management and quick response to changes are essential.

Microservices patterns worth examining closely include:

Recommended Books:

Distributed Transaction Patterns

What are distributed transactions? They are sets of related business operations triggered across different services. The challenge arises from moving beyond the process—distributed transactions are more prone to failure due to network issues. Often, these problems only emerge in production. This is well described in the Fallacies of distributed computing.

The Saga approach allows for solving distributed transaction problems. The pattern was described in 1987 by Hector Garcaa-Molrna and Kenneth Salem in a scientific paper titled Sagas #asnooneexpected 😀

What does the pattern involve?

These transactions are linked in a saga consisting of local transactions. Each of them achieves a business goal and informs the next transaction about its success or failure. In case of failure, the saga can end with compensation, i.e., rolling back changes. This approach makes distributed transactions more independent and resilient to failures.

We can approach the distributed transaction pattern in two ways: through saga choreography-based or orchestration-based.

In choreography-based, each business transaction is responsible for performing its role in the business process. The saga consists of a series of interlinked transactions that exchange messages with other transactions. Each transaction responds to messages from other transactions and makes state decisions based on information received from other transactions.

Orchestration-based involves a component called an orchestrator. It is responsible for coordinating the entire business process. The orchestrator makes decisions based on responses from individual transactions, determines the order of their execution, and informs what should happen in case of transaction failure. Each transaction receives instructions from the orchestrator and performs the specified task accordingly.

Both approaches have their advantages and disadvantages and are used in different cases. In the choreography-based approach, each transaction is more independent, which can lead to better scalability, but it can also be more challenging to understand and debug when something goes wrong. The orchestration-based approach is more centralized and simpler to understand but may be less flexible and harder to scale.

More information can be found in:

Recommended Books:

Why Use Patterns in Your Work?

Patterns are a fundamental element of product design. They enable the definition of the basic characteristics and behaviors of the system.

By knowing the characteristics of different patterns, you can choose the ones that best meet the needs and business goals of your project.

And now the most important part: let me know which patterns are missing from my compilation! 😉

comments powered by Disqus