0% found this document useful (0 votes)
58 views

Software Architecture Patterns

Uploaded by

dobbyg36
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
58 views

Software Architecture Patterns

Uploaded by

dobbyg36
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 28

Software Architecture Patterns

Imagine the artistry of designing a skyscraper—where form meets function, and


engineering brilliance touches the sky. In the world of software, architecture patterns
serve as the architect’s compass, guiding development of robust and maintainable
systems.
Software architecture patterns offer reusable designs for various situations, offering
numerous advantages such as improved efficiency, productivity, speed, cost
optimization, and better planning.

What is an architectural pattern?


An architectural pattern is a set of architectural design decisions that address recurring
design problems in various software development contexts. It offers rules and principles
for organizing the interactions between predefined subsystems and their roles.
Even though an architectural pattern is a rough image or blueprint of your system, it’s
not the actual architecture. Instead, it’s a concept that helps you to understand software
architecture’s elements. There can be countless pieces of architecture that implement
the same pattern. That’s why patterns are known as “strictly described and commonly
utilized.” The success of the system depends on software architecture selection.
Famous examples of architectural patterns are microservices, message bus, service
requester/ consumer, MVC pattern, MVVM, microkernel, n-tier, domain-driven design
components, and presentation-abstraction-control.

Software Architecture Principles

These principles are concerned with the highest levels of the system. Focused on the
flow of data, events, and dependencies through the entire system, the birds eye view of
the system.

• Separation of Concern
• Layers of Isolation
• Cohesion
• Inversion of Control
• Screaming Architecture
• Clean Principles

Separation of Concerns emphasizes the separation of systems into layers or


subsystems based on domains of concerns. Layer of Isolation requires that dependency
flow in one direction.

The principles of Cohesion and Inversion of Control promotes the decoupling of


layers. Cohesion allows for easy reconfiguration of layers. Any changes to layer should
not cascade into another layer. Inversion of control requires the separation of
dependency management. A dependency is managed by another class, which is used
to inject dependencies where they are needed.

Screaming Architecture is an architectural principle that makes systems easier to


manage. The principle aims to improve the readability of code. An implementation
would encapsulate business logic in use-case objects that map directly to the use-case
diagram of the system. Engineers can get a good idea of what the system does by
looking at the use-cases and its dependencies.

Clean Principles was introduced by Uncle Bob as a proposed implementation of clean


code principles with an emphasis on single direction dependency. Clean is a set of
principles used to write code that is easy to understand and maintain. It includes all the
principles mentioned above plus any principles that promotes the understanding of
code. Clean Architecture is the implementation of these principles. Usually done using a
modified version of the layered architecture pattern.

What is the importance of software architecture pattern?

Software architecture patterns hold significant importance for it can solve various
problems within different domains. For instance, instead of depending on a single
server, complex user requests can be easily segmented into smaller chunks and
distributed across multiple servers. In another example, testing protocols can be
simplified by dividing various segments of the software rather than testing the whole
thing at once.

Here are some more reasons why software architecture patterns are vital for any
software application:

• Defining Basic Characteristics of an Application:


o Knowing each architecture’s characteristics, strengths, and weaknesses
becomes important for choosing the right one to meet your business
objectives. It has been observed that architecture patterns help define an
application’s basic characteristics and behaviors. For instance, some
architecture patterns can be naturally used for highly scalable
applications, whereas others can be used for agile applications.
• Maintaining Quality and Efficiency:
o There is a high possibility that any application you build might face quality
issues. According to your software development quality attributes,
selecting an architecture pattern can help minimize the quality issues
alongwith maintaining efficiency.
• Providing Agility:
o It is natural for software applications to undergo numerous modifications
and iterations during software development and even after production.
Therefore, planning a core software architecture beforehand provides
agility to the application and makes future moderations effortless.
• Problem Solving:
o Prior planning and knowledge of a software architecture give a clear idea
of how the application and its components will function. With an
architecture in place, the developing team can adopt the best practices to
resolve complex processes and solve any errors in the future.
• Enhancing Productivity:
o Irrespective of the skills and knowledge one has about a programming
language, framework, or application, there has to be certain standardized
principles. With an appropriate application pattern in place, the company
can quickly grasp the project’s status. In addition, productivity rates
improve when an architecture pattern is in place to clarify the
project scope.

Software architecture pattern vs. design pattern


There is a thin line between an architecture pattern and a design pattern, and most
people get confused between the two. For basics, let’s imagine your team given a task
to build a house and live in it.

To begin with the task, they would first have to plan it out before placing bricks and
cement on an empty ground. Moreover, even after a house is planned, there is more to
making it worth living – they would need basic amenities like kitchen appliances,
beddings, toiletries, and many more. In this analogy, how the house should look
represents architectural patterns, whereas the interior design of the house represents
the design patterns.

In a software system, architecture is considered when you have to create business


logic, database logic, UI, etc., whereas software design patterns are used while
implementing business logic or database logic.
Different types of software architecture pattern
Let’s discuss a few popular architectural patterns that have helped a lot of software
businesses to scale up their businesses:

1. Layered Architecture Pattern

You’ve probably heard of multi-layered, aka tiered architecture, or n-tier architecture.


This architecture has gained popularity amongst designers and software architects alike
for its commonalities with the conventional arrangements of IT communications in many
startups and established enterprises. Often, a layered architecture is classified into four
distinct layers: presentation, business, persistence, and database; however, the pattern
is not confined to the specified layers and there can be an application layer or service
layer or data access layer. Popular frameworks like Java EE utilized this architecture
pattern.

Let’s say a software engineer is building a large application, and you employed all four
layers to your architecture pattern. On the flip side, small businesses may combine the
business and the persistence layers into a single unit, primarily when the latter is
engaged as an integral part of the business logic layer components.
This pattern stands out because each layer plays a distinct role within the application
and is marked as closed. It means a request must pass through the layer below it to go
to the next layer. Another one of its concepts – layers of isolation – enables you to
modify components within one layer without affecting the other layers.

To simplify this process, let’s take an example of an eCommerce web application. The
business logic required to process a shopping cart activity, such as calculating the cart,
is directly fetched from the application tier to the presentation tier. Here the application
tier acts as an integration layer to establish seamless communication between the data
and presentation layers. Additionally, the last tier is the data tier used to maintain data
independently without the intervention of the application server and the business logic.

Usage:
• Applications that are needed to be built quickly.
• Enterprise applications that require traditional IT departments and processes.
• Appropriate for teams with inexperienced developers and limited knowledge of
architecture patterns.
• Applications that require strict standards of maintainability and testability.

Shortcomings:
• Unorganized source codes and modules with no definite roles can become a
problem for the application.
• Skipping previous layers to create tight coupling can lead to a logical mess full of
complex interdependencies.
• Basic modifications can require a complete redeployment of the application.

Diagram:
2. Event-driven Architecture Pattern

If you are looking for an architecture pattern that is agile and highly performant, then
you should opt for an event-driven architecture pattern. It is made up of decoupled,
single-purpose event processing components that asynchronously receive and process
events. This pattern orchestrates the behavior around the production, detection, and
consumption of all the events, along with the responses they evoke.
The event-driven architectural style consists of two topologies – mediator and broker. A
mediator is used when multiple steps are needed to be orchestrated within an event bus
through a central mediator. On the other hand, a broker is used to chain events together
without using a central mediator.

A good example that uses event-driven architecture is an e-commerce site. The event-
driven architecture enables the e-commerce website to react to various sources at a
time of high demand. Simultaneously, it avoids any crash of the application or any over-
provisioning of resources.

Usage:
• For applications where individual data blocks interact with only a few modules.
• Helps with user interfaces.

Shortcomings:
• Testing individual modules can only be done if they are independent, otherwise,
they need to be tested in a fully functional system.
• When several modules are handling the same events, error handling becomes
challenging to structure.
• Development of a system-wide data structure for events can become arduous if
the events have different needs.
• Maintaining a transaction-based mechanism for consistency can become
complex with decoupled and independent modules.

Diagram:

3. Microkernel Architecture Pattern

This architecture pattern consists of two types of components – a core system and
several plug-in modules. While the core system works on minimal functionality to keep
the system operational, the plug-in modules are independent components with
specialized processing.

If we take a business application’s perspective, the core system can be defined as


general business logic without the custom code for special cases, special rules, or
complex conditional processes. On the other hand, the plug-in modules are meant to
enhance the core system in order to produce additional business capabilities.
Taking the example of a task scheduler application, the microkernel contains all the
logic for scheduling and triggering tasks, while the plug-ins contain specific tasks. As
long as the plug-ins adhere to a predefined API, the microkernel can trigger them
without having to know the implementation details.

Usage:
• Applications that have a clear segmentation between basic routines and higher-
order rules.
• Applications that have a fixed set of core routines and dynamic set of rules that
needs frequent updates.

Shortcoming:
• The plugins must have good handshaking code so that the microkernel is aware
of the plugin installation and is ready to work.
• Changing a microkernel is almost impossible if multiple plugins depend on it.
• It is difficult to choose the right granularity for the kernel function in advance and
more complex at a later stage.

Diagram:

4. Microservices Architecture Pattern

Microservices architecture pattern is seen as a viable alternative to monolithic


applications and service-oriented architectures. The components are deployed as
separate units through an effective, streamlined delivery pipeline. The pattern’s benefits
are enhanced scalability and a high degree of decoupling within the application.
Owing to its decoupled and independent characteristics, the components are accessed
through a remote access protocol. Moreover, the same components can be separately
developed, deployed, and tested without interdependency on any other service
component.

Netflix is one of the early adopters of the microservice architecture pattern. The
architecture allowed the engineering team to work in small teams responsible for the
end-to-end development of hundreds of microservices. These microservices work
together to stream digital entertainment to millions of Netflix customers every day.

Usage:
• Businesses and web applications that require rapid development.
• Websites with small components, data centers with well-defined boundaries, and
remote teams globally.

Shortcoming:
• Designing the right level of granularity for a service component is always a
challenge.
• All applications do not include tasks that can be split into independent units.
• Performance can be affected because of tasks being spread across different
microservices.

Diagram:

5. Space-Based Architecture Pattern

The concept of tuple space – the idea of distributed shared memory is the basis of the
name of this architecture. The space-based pattern comprises two primary components
– a processing unit and a virtualized middleware.

The processing unit contains portions of application components, including web-based


components and backend business logic. While smaller web applications could be
deployed in a single processing unit, the larger applications could split the application
functionality into multiple processing units to avoid functional collapse. Furthermore, the
virtualized-middleware component contains elements that control various aspects of
data synchronization and request handling. They can be custom-written or can be
purchased as third-party products.

A bidding auction site can be considered as a fitting example for this architecture
pattern. It functions as the site receives bids from internet users through a browser
request. On receiving the request, the site records that bid with a timestamp, updates
the information of the latest bid, and sends the data back to the browser.

Usage:
• Applications and software systems that function with a large user base and a
constant load of requests.
• Applications that are supposed to address scalability and concurrency issues.

Shortcoming:
• It is a complex task to cache the data for speed without disturbing multiple
copies.

Diagram:

6. Client-Server Architecture Pattern

A client-server architecture pattern is described as a distributed application structure


having two main components – a client and a server. This architecture facilitates the
communication between the client and the server, which may or may not be under the
same network. A client requests specific resources to be fetched from the server, which
might be in the form of data, content, services, files, etc. The server identifies the
requests made and responds to the client appropriately by sending over the requested
resources.

The functional characteristics of a client and a server is an example of programs that


interact with one another within an application. The functionality of this architecture is
highly flexible as a single server can serve multiple clients, or a single client can use
multiple servers. The servers can be classified by the services or resources they
provide, irrespective of how they perform.

Email is a prominent example of a model that is built using the client-server pattern.
When a user/client searches for a particular email, the server looks into the pool of
resources and sends the requested email resource back to the user/client. This also
helps you to improve the user experience.

Usage:
• Applications like emails, online banking services, the World Wide Web, network
printing, file sharing applications, gaming apps, etc.
• Applications that focus on real-time services like telecommunication apps are
built with a distributed application structure.
• Applications that require controlled access and offer multiple services for a large
number of distributed clients.
• An application with centralized resources and services that has to be distributed
over multiple servers.

Shortcomings:
• Incompatible server capacity can slow down, causing a performance bottleneck.
• Servers are usually prone to a single point of failure.
• Changing the pattern is a complex and expensive process.
• Server maintenance can be a demanding and expensive task.

Diagram:

7. Master-Slave Architecture Pattern

Imagine a single database receiving multiple similar requests at the same time.
Naturally, processing every single request at the same time can complicate and slow
down the application process. A solution to this problem is a master-slave architecture
pattern that functions with the master database launching multiple slave components to
process those requests quickly.

As the title suggests, the master-slave architecture pattern can be pictured as a master
distributing tasks to its slaves. Once the slave components finish their tasks, the
distributed tasks are compiled by the master and displayed as the result.
One must note that the master has absolute control and power over the slave
components, determining their communication and functional priorities. What makes this
pattern unique is that each slave would process the requests simultaneously, providing
the results at the same time. This also means that the slave operations would not be
considered complete until every slave has returned the result to the master.
This pattern is well-suited for applications that can be divided into smaller segments for
executing similar requests. An appropriate example would be a database application
that requires heavy multitasking as its vital component.

Usage:
• Development of Operating Systems that may require a multiprocessors
compatible architecture.
• Advanced applications where larger services have to be decomposed into
smaller components.
• Applications processing raw data stored in different servers over a distributed
network.
• Web browsers that follow multithreading to increase its responsiveness.

Shortcomings:
• Failure of the master component can lead to a loss of data with no backup over
the slave components.
• Dependencies within the system can lead to a failure of the slave components.
• There can be an increase in overhead costs due to the isolated nature of the
slave components.

Diagram:

8. Pipe-Filter Architecture Pattern

A pipe-filter architecture pattern processes a stream of data in a unidirectional flow


where components are referred to as filters, and pipes are those which connect these
filters. The chain of processing data takes place where the pipes transmit data to the
filters, and the result of one filter becomes the input for the next filter. The function of
this architecture is to break down significant components/processes into independent
and multiple components that can be processed simultaneously.
The pipe-filter pattern is best suited for applications that process data in a stream using
web services and can create simple sequences to complex structures. Compilers can
be considered a fitting example having this architecture pattern since each filter
performs lexical analysis, parsing, semantic analysis, and code generation.

Usage:
• It can be used for applications facilitating a simple, one-way data processing and
transformation.
• Applications using tools like Electronic Data Interchange and External Dynamic
List.
• Development of data compilers used for error-checking and syntax analysis.
• To perform advanced operations in Operating Systems like UNIX, where the
output and input of programs are connected in a sequence.

Shortcomings:
• There can be a loss of data in between filters if the infrastructure design is not
reliable.
• The slowest filter limits the performance and efficiency of the entire architecture.
• During transmission between filters, the data-transformation overhead costs
might increase.
• The continuous transformational character of the architecture makes it less user-
friendly for interactional systems.

Diagram:

9. Broker Architecture Pattern


A broker pattern is used for structuring distributed systems with decoupled components.
By invoking remote services, components can interact with others in broker architecture
patterns. Also, the broker is responsible for all the coordination and communication
among the components.
Clients, servers, and brokers are three major components of the broker pattern.
Generally, a broker will have access to all the services and characteristics related to a
particular server. When clients request a service from the broker, the broker redirects
them to a suitable service category for further process.
One of the key benefits of this architecture pattern is how it manages operations, such
as change, addition, deletion, or relocation, related to objects in a dynamic manner.
Lastly, this architecture pattern separates all communication-related code into layers
from the application, allowing applications to run on distributed or single computers.
Because of such advantages, broker architecture has been prevalent.

Usage:
• Used in message broker softwares such as Apache ActiveMQ, Apache Kafka,
RabbitMQ, and JBoss Messaging.
• For structuring distributed systems that have decoupled components.
Shortcomings:
• Shallow fault tolerance capacity.
• Requires standardization of service description.
• The hidden layer may decrease software performance.
• Higher latency and requires more effort in deployment.
Diagram:

10. Peer-to-Peer Architecture Pattern


In the peer-to-peer architectural pattern, individual components are called peers. A peer
can act as a client, a server, or both and change its role dynamically over time. As a
client, a peer can request service from other peers, and as a server, a peer can provide
services to other peers. The significant difference between peer-to-peer and client-
server architecture is that each computer on the network has considerable authority and
the absence of a centralized server. Its capacity increases as more and more
computers join the network.

An excellent example of a peer-to-peer architecture pattern would be file-sharing


networks like Skype, BitTorrent, and Napster. In BitTorrent, peer-to-peer architecture is
used for distributing the data and files on the internet in a decentralized fashion. By
using this protocol, one can transfer large video and audio files with the utmost ease. In
Skype, you use the VoIP P2P architecture pattern to make a voice call and send text
messages to another user. In this manner, you can use peer-to-peer architecture for file
sharing, messaging, collaboration, etc.

Usage:
• File-sharing networks such as Gnutella and G2.
• Cryptocurrency-based products such as Bitcoin and Blockchain.
• Multimedia products such as P2PTV and PDTP.
Shortcomings:
• No guarantee of high-quality service.
• Achieving robust security is challenging.
• Performance depends on the number of nodes connected to the network.
• No way to backup files or folders.
• Might need a specific interface to read the file.

Diagram:
Comparative analysis of different software architecture patterns
So far, we have read about the different types of architecture patterns. Now, which
architecture would you choose for your software type? You need to make the right
choice.
Let’s have a glance at the table below.
MVC MVP and MVVM

MVC (model view controller)

MVC pattern – design pattern facilitates the organization of the application structure
thanks to the division into three layers: View, Controller and Model. Let’s go through
each of the pattern layers in more detail.

Model
Model is the part of the code responsible for contact with data sources. It is an
intermediary between the data source and the view. It deals with storing data and
reading the data, as well as ensuring their consistency and validation. The model
contains the entire business logic of the application. Thanks to the separation of
responsibilities, the code becomes more structured and flexible to modifications
(changing one layer does not necessarily entail changing the code in the other layers).

View (UI logic)

The view is the part of the code responsible for the presentation of the data retrieved
from the model to the user. The view does not have any business logic, it focuses only
on displaying the data, which facilitates subsequent changes in the application. A view
can change the state of the model only if the modification involves a change in the way
data is displayed. If we want to change the appearance of the application, we do not
have to interfere with the code responsible for the logic. So, UI components that are
changed (in terms of updated data) have an impact on view interface for user, but don’t
have an impact on the application logic code.

Controller
The controller handles the interaction with the user. Contains core business logic for
handling events passed from the view. The controller allows access to certain parts of
the application only by authorized users. Depending on the user’s actions, it updates the
model and refreshes the view. It can also transfer control to another controller.

Benefits
• Division into modules organizing the application code,
• Separation of business logic from view,
• The model is not dependent on the view,
• Makes it easier to find a specific part of the code,
• Easier expansion through modular construction,
• Prevents clutter in the code,
• Facilitates teamwork.
Disadvantages
• Complicated division into modules increasing the complexity of the system,
• Due to the specificity of the Android system architecture (life and activity cycle),
the View and Controller layers are often implemented in an activity, a fragment,
so there is no clear separation between them (therefore only the model layer is
able to perform unit testing),
• Dependence of the view on the model,
• Difficult to test views,
• The pattern can be used in small projects with a small number of screens, where,
if necessary, the business logic is tested.
MVP (model view presenter) architecture pattern

The MVP is an architecture pattern that improves the process of creating application
screens by dividing responsibilities into three layers: view, presenter and model. MVP
was created on the basis of the MVC pattern. In the model view presenter pattern, the
presenter is the same as the controller in MVC with one small difference, there is the
business logic in the presenter. Data is not passed directly from the model to the view
as it is in MVC. The presenter queries the model, the model returns the data to the
presenter, the presenter processes the received data and passes it to the view.

Model
The model represents the part of the code that is responsible for managing business
logic and supporting network or database API (works with the remote and local data
sources to get and save the data). It also definesdomain logic (business rules) for data
means how the data can be changed and manipulated.

View (user interface)


The view in the MVP design pattern is the part of the code responsible for the
presentation of the data provided by the presenter to the user. The view does not have
any business logic, it focuses only on displaying the data, which facilitates subsequent
changes in the application. If we want to change the appearance of the application, we
do not have to interfere with the code responsible for the logic. When user interacts with
UI presenter logic is fired.

Presenter (business logic)

The presenter contains all the business logic of the application and handles the user
interactions. It’s presenting data to the view layer. Depending on actions of the user, it
reads the necessary data through the model, performs the necessary calculations and
passes the result of its calculations to the view. The presenter also handles user
authorizations and authentication, and allows only authorized users to access specific
parts of the application.

Benefits
• Division into modules organizing the application code,
• Separation of business logic from view,
• The model is not dependent on the view,
• Makes it easier to find a specific part of the code,
• Easier expansion through modular construction,
• Facilitates teamwork,
• Is used to solve the problem of large activities, fragments (which have too much
responsibility and a lot of code).
Disadvantages
• Due to the lifecycle of a fragment or activity, or other user interface components,
a reference memory leak may appear for a view that does not exist anymore in
the presenter. In such a case, care should be taken to adequately support
lifecycle methods,
• It often happens that the implementation of a pattern requires the creation of
additional classes and interfaces (often with similar content), which is often
associated with redundant code,
• Too complicated for simple, small applications,
• Requires familiarizing employees with the pattern, which results in higher costs,
• Presenter has a reference to the view so if we want to use it elsewhere we need
to implement that view in for example fragment. This can result in many
unnecessary empty functions implemented,
• Tight coupling between presenter and view (one to one relationship).

MVVM architecture (model view viewmodel)

MVVM design pattern that is designed to facilitate the creation of application screens by
using the division of responsibilities into three different layers: view, model view
(ViewModel) and model (Model). The idea of the MVVM pattern is based primarily on
the view layer (Observer pattern) observing the changing data in the model view layer
and responding to changes through the data binding mechanism. Thanks to the use of
data binding strategy in the view layer, its logic is minimized, the code becomes more
structured and open to modifications, and testing is easier.

It’s great choice for small scale projects. It also works great with two way data binding.
It’s event driven (means view reacts to state changes in View model). Because view
model doesn’t have reference to the view layer unit testing is easier. Major components
of MVVM are provided by Google. Main three components are: Activity or fragment
containing the view model, view model and network and database repositories.

Model (business logic)


The Model is responsible for the business logic, i.e. processing, storage, modification
and delivery of the expected data to the ViewModel.

View (user interface)


The View layer is responsible for the presentation of data, system state and current
operations in the graphical interface, as well as for the initialization and binding of the
ViewModel to the view elements. Here we setup user input interaction. It is an entry
point of user interaction.

ViewModel

Deals with delivering data from the Model to the View layer and handling user actions. It
is worth mentioning that it provides the data streams for View. Think of it as a bridge
between the Model and View. When Model changes ViewModel provides update of data
state to view component through mechanism called LiveData (observable variables).
One view may contain many different view models (one to many relationship).
Benefits
• Solves the memory leak problem that appears in the MVP pattern (the model
view does not have a reference to the view layer),
• No duplication of data,
• Independence of logic from the way data is displayed,
• Thanks to the separation of tasks in different layers, the pattern can be used to
increase the readability of the code and the possibility of independent testing
without the use of additional dependencies,
• No interfaces defining views like in MVP,
• The data binding mechanism increases the flexibility of the view on modifications,
which makes the MVVM pattern work well in large dynamically changing projects.

Disadvantages
• Each view screen has a dedicated ViewModel, which translates into a greater
number of classes (compared to the MVP, there are still fewer of them),
• The view layer must ensure proper binding of variables and methods for each
required view element, as well as observing the state of the ViewModel,
• Creating a correct and complete ViewModel requires analyzing the view layer in
terms of the required data and possible states,
• Proper handling of LiveData (live events from viewModel to view layer) requires
more boilerplate code,
• In some cases data binding mechanism can get complex. You need to be careful
when you modify your UI layout files,
• When used with data binding you need to be aware of clearing binding every
time user leaves the screen, otherwise memory leaks might occur.

MVC vs MVP vs MVVM

A brief summary of architecture patterns


In MVC the Controller handles events, manipulates the Model, which contains business
logic, and the View displays data from the Model. One Controller supports several
Views.

In MVP, the data from the Model is passed to the Presenter and not directly to the View,
and the Presenter passes it to the View. One presenter can refer to one View.
In MVVM, the data from the Model is passed to the ViewModel, which can support
multiple Views. The View knows nothing about the ViewModel, it only requires the data
you need.

Which architectural pattern is the best?

We have to start thinking about what’s the goal of all these mentioned architectures.
The goal is always the same – make your code readable, more maintainable, more
testable and just easier to work on with a team. No matter what kind of pattern you look
at, every single pattern has the same goal and wants to achieve the same things. The
foundation of good software architecture will always be the same few things like:
modular design, like SOLID principles, writing proper abstractions, Single Source of
Truth, a single responsibility principle, separation of concerns – all these things are
important for any type of architecture.

There are several ways to design a system in software engineering, and every design
has its own merits and challenges. So metimes different design approaches try to
achieve similar objectives. When we think about software architecture design, especially
in the object-oriented world, the three most talked about patterns are Clean
Architecture, Hexagonal Architecture, and Onion Architecture.

These three patterns are trying to advocate similar ideas. They all define a loosely
coupled testable system that avoids any direct dependencies in terms of
implementation, yet do so using their own terminology and each with specific nuances.
They all suggest approaches to make software architectures more manageable and
testable, but do so in their own way.

If we look at them all together, they offer some useful architectural takeaways that are
applicable regardless of the design approach you choose to use. We’ll explore them
shortly, but first let’s have a look at what each of these patterns are about individually
and how they compare to one another.

Hexagonal architecture

Hexagonal Architecture is sometimes referred to as ports and adapters architecture.


Alistair Cockburn introduced it in 2005, with the core idea behind it being to make
applications independent of direct dependency from UI and database. This isolation is
supported through the concept of ports and adapters.
Let's take an example. As a developer, you need to design a user related business
logic, which will persist in a database. You want isolation between business logic and
persistence so that both can perform and grow into their core responsibilities.

• A database-specific logic will be wrapped into an adapter class, for example,


UserDataAdapter
• User specific business logic class, such as “User”
• A contract between User and UserDataAdapter so that they can interact with
each other — eg.: IUserDataPort. This contact is a port.

As Cockburn explains, the word “hexagon” was chosen not because the number six is
important, but rather to allow the people designing the architecture to have enough
room to insert ports and adapters as required, ensuring they aren’t constrained by a
one-dimensional layered drawing.

Onion architecture

Jeffrey Palermo introduced the concept of Onion Architecture in 2008. He wanted to


develop a design approach for complex business applications by emphasizing the
separation of concerns throughout the system. This pattern took important steps beyond
hexagonal architecture as it expanded on the idea of defining a Core Business Layer
within an application and the various layers surrounding it, so that the core layer is
independent of outer layers and their dependencies.

The central layer — the domain model — contains all business rules. At the next level
are domain services, which are like contracts of repositories and other dependencies.
The outermost layer contains the user interface and connectivity to external
infrastructure.
In short, the key difference between onion architecture and hexagonal architecture is
that onion architecture introduces different layers, along with the core business layer, in
the application and moves connections to external dependencies such as databases
and UI to the outer circle. This means they can be more easily replaced if needed.

Clean architecture

Robert Martin introduced Clean Architecture in 2012. The core concepts are similar to
Onion Architecture, but it has a slightly different terminology. Here, the domain model is
referred to as an “entity”. Entity contains business-specific rules and logic, while the
application operation specific logic sits in the use case. These use cases orchestrate
operations on top of entities to direct them to execute their business rules to achieve the
goals of the use case.

At first glance, Clean Architecture provides a better understanding of boundaries and


provides a clearer separation of concerns compared to Onion Architecture. They are
very closely related and advocate similar ideas, but with different layers. Clean
architecture makes it distinctly clear why each layer exists and what their respective
responsibilities are. That’s why it’s also known as screaming architecture — it makes
everything explicit.

What do architectural patterns teach us?


As we have seen, all three architectural styles share the principles of loose coupling and
attempt to minimize moving parts by properly layering the application.

What, then, are the key takeaways that these three patterns offer us? What
fundamental architectural principles should we bear in mind?

Centralized business rules


Putting business-specific rules in a centralized place is something suggested by both
Clean and Onion Architecture. Although they use different names for very similar
concepts, they both encourage us to think about business logic in the same way.

Application specific rules

Again, both Clean and Onion Architecture point in similar directions; they suggest that
there should be a layer where you manage application specific logic sitting next to
enterprise rules.

This layer will contain operation-specific orchestration and related logic for the
application.

Dependency rule

All three patterns are aligned on this principle; it emphasizes that source code
dependencies should only point inward. The outer layer can only refer to the inner layer
and not vice versa.

Isolation between different layers


All three patterns strongly advocate that different parts of the application should be able
to grow in isolation with each other and that there should be proper abstraction between
each layer of the application.

Most importantly, the core business rules should be independent of:

How you persist it:

• Your choice of databases should not affect the core domain


• If you switch the type of database, e.g.: SQL to NoSQL, there should not be any
change in your business logic.
• Interactions between domain and persistence will follow a defined standard and
will be independent of persistence details.

How you expose it:

• UI logic and use cases should not ever motivate you to alter the core domain.
• Whether you expose it via JSON, XML, or GraphQL, the core should not be
affected.

Which framework you are using:

• Ideally, the core domain should be independent of the framework being used.
This may not be very straightforward, but it can be achieved through careful
abstractions.
• As an example,. if you change from Springboot to Micronaut in Java, Zin to
Martini in Golang, WebAPI to Nancy in .NETCore, there should be no change in
terms of how you define the core domain.

What your external dependencies are:

• The core domain should not be affected with infrastructure and related
dependencies. For example, if you’re using AWS Kinesis and you need to
replace it with Kafka streams, the core domain should be not at all affected.
• Email, SMS, and Events are a few examples of such dependencies

You might also like