The Top 7 Software Architecture Patterns You Need To Know
The Big Ball of Mud refers to an architecture that lacks any structural design and therefore results in complex, tightly coupled, and interdependent code.
It is, unfortunately, the most popular way to design software, and its pervasive adoption is usually unintentional. It emerges due to a general lack of understanding of architectural principles, undisciplined development practices, piecemeal growth and the accumulation of technical debt over time.
You know this kind of systems… We all do!!!
And scarily enough these systems comprise a significant portion of software in the real world! But there is an antidote to them: architectural patterns!
An architectural pattern is a reusable solution to a specific problem that helps define the basic characteristics and behaviour of an application. Choose the right patterns for your problem, and you can avoid reinventing the wheel and potential nasty traps that may cause trouble had you attempted to come up with a new solution.
Let’s explore seven of the most common patterns that every developer, architect, or data scientist needs to be familiar with.
1⃣ — Layers
The Layers Pattern is one of the most used (and abused) patterns that relies on partitioning the code into distinct and independent layers which decreases the coupling between them. Each layer is marked as closed, which means that a request must pass through the layer right below it to go to the next layer. It also has its own responsibility which provides a level of isolation as it enables us to modify a layer’s components without affecting the other layers.
➕ Strengths: It is conceptually easy to implement and the layers are clearly implemented and represented in the code. It promotes separation of concerns, maintainability, reusability and testability.➖ Weaknesses: Each layer introduces abstractions between the highest and lowest layers which can increase the complexity and the performance of the application and can cause what is known as the 'architecture sinkhole' anti-pattern, where requests flow through multiple layers as simple pass-through processing with little or no logic performed within each layer. It can also lead to a monolithic application that is hard to split afterwards.
There are many variants of this pattern, but it typically has four layers: presentation/UI layer, service layer, business logic/domain layer, data access layer. It has also been the inspiration of other major patterns e.g. MVC (Model, View, Controller).
2⃣ — Pipes and Filters
In the Pipes and Filters pattern, each filter is responsible for a single data operation or transformation. Data is streamed from one filter to the next one via pipes and operations occur in parallel. This means that a filter produces output as soon as it becomes available without waiting until all input is consumed. The filters are loosely coupled so they can be reused and reassembled to create new pipelines.
Typical usage of this pattern is the Unix piped command.
➕ Strengths: It promotes performance, reusability/composition, and extensibility.➖ Weaknesses: Reliability can be an issue as if one component fails, the issue is propagated across the pipeline, but can be controlled by having filters for error handling. The parallel running nature of this paradigm can be computationally expensive and can even result in deadlocks when one filter cannot produce output before it processes all the input (e.g. sort operation)
The Batch Sequential Pattern is similar to Pipes and Filters with the main difference being that the former is not streaming the data but usually writes all data to disk for the next stage to read — quite old-school :).
3⃣ — Client-Server
In a Client-Server pattern, the functionality of the system is organised into services, with each type of service delivered from a separate server. Clients access the servers to make use of these services.
A server is permanently active listening for clients’ requests. Once a request is received, the server opens a connection with the client over a specific protocol, processes the request and responds back. The requests are sent beyond process and machine boundaries which means clients and servers may reside on different machines.
➕ Strengths: Having a set of shared and distributed services makes it easier to manage, modify, and reuse software modules. Provides interoperability as client-server applications can be built irrespective of the platform, topology or technology stack.➖ Weaknesses: The server can get overloaded when there are too many requests, causing a performance bottleneck and can become a single point of failure.
A REST (Representational State Transfer) architecture is a client-server architecture where clients are separated from servers by a uniform interface, servers offer addressable endpoints (e.g. by a URL) and communication is stateless (there is no information or memory carried over from the previous request) via HTTP. REST APIs (Application Programming Interfaces) are the industry standard for web-based applications.
4⃣ — Service Oriented Architecture (SOA)
For some organisations, SOA is a stepping stone to replace monolithic applications, providing a more flexible and agile environment. It provides a group of modular services that can ‘talk’ to each other to support applications and their deployment.
The service interfaces provide loose coupling, which means that they can be called with little or no knowledge of how the integration is implemented underneath. Traditionally, SOA involves an enterprise service bus (ESB) as a means of coordinating and controlling these services.
➕ Strengths: SOA produces interoperable, loosely coupled systems that are easier to maintain and scale, and as such it reduces the total cost of ownership (TCO). It also allows IT to respond to the changing market conditions by delivering software in an agile way.➖ Weaknesses: To achieve service integration, the ESB must oversee the messages from start to destination, which results in reduced overall performance. Also determining the version of a SOA system is not possible without knowing what services were running at a snapshot in time.
Not all applications are suited to this type of architecture e.g. situations where strictly enforced response times are required.
5⃣ — Microservices
The microservices architecture pattern takes the approach of building small, single-purpose and self-contained services that communicate with each other for the entire system to work. Each service is deployed independently, thereby providing a high degree of decoupling, and also evolves autonomously while maintaining clear interfaces/contracts with the others.
DevOps can be used to help an organisation to transition from SOA to Microservices, as the latter often operate in containers, which make them more scalable and portable.
➕ Strengths: It promotes highly reusable, maintainable and (unit) testable services which are independently deployable. They provide improved fault isolation as failure of one microservice does not affect the working of others. With the polyglot APIs, developers can easily choose the best technologies and languages based on their needs.➖ Weaknesses: Complex communication between the services makes testing their interactions more difficult. Increased effort is required to catalogue, test, and fix version (in)compatibilities.
The granularity of the APIs provided by microservices is often different from what a client needs. In these cases an API gateway is implemented that is the single entry point for all clients and handles requests in one of two ways: either simply route them to the appropriate service or even orchestrating them out to multiple services.
6⃣ — Event Driven Architecture (EDA)
The Event Driven architecture is the most common asynchronous pattern which is centred around messages that describe events as well as their handling (i.e. their production, detection and consumption). Events are immutable (i.e. they cannot be changed or deleted), and they are ordered chronologically. They are used to trigger or communicate between services in real-time (i.e. a service does not poll for updates but receives them).
Events can be published in several ways, with the two most popular being:
⇾ to a message queue that guarantees delivery of an event to the appropriate consumer, or,
⇾ to a broker, where an event does not target a certain recipient but access to the ‘topic’ is allowed to all interested parties (aka subscribers). Pub/Sub messaging systems are often described as an Event Streaming Architecture.
➕ Strengths: Provides asynchronous communication which allows for events to be queued or buffered and hence avoids blocking. In case of a fault, lost work is recovered by ‘replaying’ events from the past. Broker dependant, it can promote availability, reliability and scalability.➖ Weaknesses: Because of its asynchronous nature EDA systems must carefully handle inconsistent or duplicate events or incompatible versions. Also, they do not support ACID transactions.
One of the most popular practices in EDA is called CQRS (Command Query Responsibility Segregation), which allows for using different models to update and read domain data.
Another important practice that deals with the atomicity of the transactions is Event Sourcing where updates and deletes are never performed directly on the data; rather, state changes of an entity are saved as a series of events — this enables the reconstruction of the state of the application at any point in time.
💡 The EDA was further standardised with the Reactive Manifesto which calls out for designing systems that have these 4 characteristics: Responsive, Resilient, Elastic and Message Driven.
7⃣ — Serverless
All the architectures described above have one common denominator: their dependency on the infrastructure.
In a Serverless Architecture, the cloud providers can easily manage the provisioning of servers on-demand basis. The applications are hosted in containers and are deployed in the cloud. Developers do not have to worry about planning the servers’ resources (memory, CPU, IO), or designing such a topology that results in a highly available and automatically scalable application — the cloud provider takes care of this by essentially virtualising the runtime and operational management. Therefore, such providers usually charge their customers based on the total number of requests, the frequency of requests over a specified period or the total time spent to serve all of the requests.
➕ Strengths: Freedom from infrastructure planning; cost-effectiveness as the pricing is dependent on the number of executions; ease of deployment and continuous delivery.➖ Weaknesses: Serverless is still in its infancy as a technology and there is a small knowledge base amongst the IT community. Vendor lock-in is a high risk as migrating from one platform to another is difficult. Debugging is complex due to the reduced visibility of the backend processes. Serverless is not efficient for long-running processes (dedicated servers or virtual machines are recommended).
Serverless encompasses two different but overlapping areas:
⇾ Function as a Service (FaaS): Custom code that runs in ephemeral containers.
⇾ Backend as a Service (BaaS): Applications that significantly depend on third-party services for backend aspects (e.g. DB, cloud storage, user authentication etc).
There is no one-size-fits-all architecture that can be applied to every project so understanding the pros and cons of the architecture patterns, and some of their most common design decisions is an important part of creating the best design possible. Also it is worth noting that these patterns are not mutually exclusive; in fact, they complement one another!
“Architecture is the decisions that you wish you could get right early in a project.” — Ralph Johnson
I hope this article is a good starting point to your learning journey. The following matrix summarises the patterns explored above:
Thanks for reading!