Fundamental Software Architectural Patterns

Fundamental Software Architectural Patterns

Patterns are essentially reusable solutions to common problems. When faced with a problem it is reasonable to consider a catalog of patterns in order to find suitable existing solutions before designing something entirely new. Then, it will be a matter of adapting these patterns to the scope of our reality.

Think of it this way: Using a design pattern is having other systems architects on your team from whom you gain knowledge without investing too much time, certainly a smart move. Although there are dozens of architectural patterns, in this post I propose to explore the most common in a language-agnostic approach.

How are architecture patterns different from design patterns?

Layers Pattern

Strictly speaking, in this model each module must be assigned to one and only one layer, where upper layers are allowed to use lower layers (one way only). It is a design that is justified in terms of maintainability, offering that different teams can collaborate on different modules in parallel. Here is an example of this scheme:

Layered Pattern Example

An API should communicate with the business logic services who at the same accesses the data layer. They all can potentially have a common set of libraries to reuse components.

Client Server Pattern

Twitter, Medium, the Email, File Sharing, mobile applications and the Web in general work with this mechanism. User applications consume the data and are responsible for providing an interface to interact with them, so the client and server often cooperate to achieve a common system. However, this pattern has a strong separation of concerns and responsibilities.

Client — Server Example

Pipe and Filter Pattern

It is a widely used pattern in data analysis and data transformation use cases. A more everyday example of this pattern is when we use the unix pipe function to combine commands, the essence is the same.

Here is an example diagram of this pattern

Pipes and Filter Example

Here we can define:

  • Filter: it is the component that reads the data, transforms it and returns the transformed data
  • Pipe: It is a connector that transports the data from one filter to the next and must ensure that the data is not modified along the way

It is a pattern that can become computationally expensive due to its nature of data analysis, but it offers performance at a higher level of architecture due to the ability to post-process data, clean it, classify it, removing the workload from other more “real time” pieces.

SOA Pattern

This type of architecture can be implemented in various ways. Traditional SOA systems rely heavily on the SOAP protocol, which works by exchanging XML messages, while more “modern” SOA applications encourage the use of microservices connected by lightweight messages on a protocol like HTTP.

Here is an over-simplified example showing a single view of a SOA system. In practice, SOA architectures are complicated and involve many architectural components. This diagram shows two services attached to the service registry. The services should then check the registry to look up connection information for other services they want to call.

SOA Generic Example

This architecture promotes the interoperability and scalability of a system, but it also entails the complexity of distributed systems for their definition and integration, since it is often difficult to control the modifications in the messages, which can affect the consumers of the different services.

Pub Sub Pattern

All communications in this pattern take place on the event bus so all components must be connected to it. The choice of technology for this bus is critical to its successful operation. Here is an example of a pub-sub system that connects different types of devices to an event bus.

Publish Subscribe Example

This architecture promotes reusability and performance in the exchange of data due to the ease of access to them, optimizing how they are produced and consumed in the event bus. However, it is difficult to think about the performance of these systems given the asynchronous nature of communication. Ultimately, the event bus is the bottleneck in the good or bad performance of the system.

Shared Data Pattern

Shared Data Pattern Example

Although this scheme has its own name, we commonly see it as part of other larger systems, for example when in a SOA architecture different services access a common database. Then, we can define access types in reads and writes with different rules and permissions that optimize and protect data access. Today the complexity of this architecture is facilitated, for example, by cloud services such as AWS RDS, which is responsible for provisioning a database with reading replicas, scalability and backups facilitating the management and provisioning of such characteristics

This pattern promotes reliability through data consistency as well as scalability and performance if the data is partitioned correctly. On the other hand, there is also a single point of failure in case the system is not managed correctly.

p2p Pattern

Each system, also called a peer, sends requests to other peers in the network and at the same time receives and services requests from other peers, which are part of the network. This is a great difference when compared to a traditional client server network where a client must only send a request and wait for the server to process.

p2p Network Example

We can find examples of this architecture in file-sharing networks such as Gnutella, cryptocurrency protocolos such Blockchain and its Bitcoin implementation.

Service Broker Pattern

The broker is the message-routing component of your system. It passes messages from client to server and from server to client. These messages are requests for services and replies to those requests, as well as messages about exceptions that have occurred. The requests are coded as calls to the broker’s API. The broker is responsible for error handling in response to these exception reports.

Servers publish their capabilities (services and characteristics) to a broker. Clients request a service from the broker, and the broker then redirects the client to a suitable service from its registry. Good examples of Message broker softwares are Apache ActiveMQ, Apache Kafka, RabbitMQ.

Here’s an example on how this pattern would look in a diagram:

Broker Pattern Example

This pattern is recommended if the relation client-server is not fixed because there are many suitable servers or the availability of the servers changes over time. Also if the choice of server depends on some criterion that is complex enough to be delegated to a separate component. By the contrary setting up or building the broker piece is a challenging task which is usually taken over one of the providers mentioned above.

Discover your architecture

Bonus: Architecture missmatch

This situation can happen at the conceptual level when an architectural scheme is not aligned with the most important attributes of the desired system or when the selected technology is not the right one. For example, if there are indications that the architecture is associated with a pub-sub model, using a relational database as the main mechanism to exchange messages will notoriously affect the final result.

Studying these patterns, and fundamentally taking the time required to define an architecture properly, can save us from many problems, so we should not underestimate this activity at the beginning of each new project that we have to face. Even the selection of technologies is a decision that should come after having the architecture defined and not the other way around as often happens.