Architectural Patterns

Architectural Patterns

An architectural pattern is the high-level structure that describes how components are organized and structured. Similar to design patterns, an architectural pattern is a general solution that has been found to work well at solving specific types of problems.

We typically think of patterns as either monolithic, or distributed.

A monolithic pattern is one where the implementation describes a single physical topology (e.g. a self-contained system or application). Many applications that we use are monolithic by design. Note that the ability to utilize remote services doesn’t contradict this definition: a monolithic application could still make remote calls to fetch data from a web service, or a database.

A distributed pattern is one where computation is distributed across different systems (e.g. computation is done remotely). These distinct systems need to communicate with one another, and coordinate the distribution and management of work. This is typically done over a network connection.

Different architectures will exhibit different properties. This means that there is no “perfect” architectural pattern! You need to marry your requirements to an architecture that will adequately support them. Ideally, your architecture would also meet the qualities that we identified earlier.

Having said that… we will only talk about a couple of styles, and we’ll gravitate towards a pattern that should work well for most applications.

Big Ball of Mud

Architects refer to the lack of any discernible architecture structure as a Big Ball of Mud .

A Big Ball of Mud is a haphazardly structured, sprawling, sloppy, duct-tape-and-baling-wire, spaghetti-code jungle. These systems show unmistakable signs of unregulated growth, and repeated, expedient repair. Information is shared promiscuously among distant elements of the system, often to the point where nearly all the important information becomes global or duplicated.
– Foote & Yoder 1997.
Big Ball of Mud Anti-Pattern
Ford, Parsons & Kua. 2017. Building Evolutionary Architectures

A Big Ball of Mud isn’t intentional—it’s the result of a system being tightly coupled, where any module can reference any other module. A system like this is extremely difficult to extend or modify.

Quality Score Comment
Usability Medium It’s possible to hack something together once
Extensibility Low It’s difficult to modify without introducing errors
Scalability Low It’s probably not very efficient
Robustness Low It’s likely not very reliable
Reusability Low No way that you can untangle this!

This obviously won’t work. We need something that is extensible and scalable but doesn’t sacrifice our ability to design interesting features.

Pipes-and-Filters

A Pipes-and-Filters architecture is appropriate when we want to transform data in a sequential manner. It consists of components linked together in a specific fashion:

Pipe and Filter
Pipe and Filter Architecture

Pipes form the communication channel between filters. Each pipe is unidirectional, accepting input on one end, and producing output at the other end.

Filters are entities that perform operation on data that they are fed. Each filter performs a single operation, and they are stateless. There are different types of filters:

  • Producer: The outbound starting point (also called a source).
  • Transformer: Accepts input, optionally transforms it, and then forwards to a filter (this resembles a map operation).
  • Tester: Accepts input, optionally transforms it based on the results of a test, and then forwards to a filter (this resembles a reduce operation).
  • Consumer: The termination point, where the data can be saved, displayed etc.

These abstractions may appear familiar, as they are used in shell programming. It’s broadly applicable anytime you want to process data sequentially, according to fixed rules. Examples include: photo manipulation software, shells. Pipelines aren’t suitable for many current applications which are designed around user-interaction.

Quality How it applies
Usability High - if it supports your requirements, it likely works well
Extensibility High - it’s easy to add more components!
Scalability Low - sequential so likely not very performant (in this form)
Robustness High - high cohesion makes it robust
Reusability Medium - you may be able to extract components

Event-Driven

An event-driven architecture is designed around the production, transmission and consumption of events between loosely-coupled components.

Unlike a pipeline architecture, which tends to assume linear ordering of consumers, an event-driven architecture expects multiple consumers for a particular event, and works best when ordering isn’t critical. Producers and consumers have no knowledge of one another.

Consumers are also independent and have no knowledge of what each other is doing with an event.

Event Loop
Event Loop

This approach can be useful in systems that generate and handle large volumes of data. It is applicable to both monolithic applications and distributed applications (with restrictions) e.g. any system that manages hardware events or interrupts.

Microkernel

A microkernel architecture (aka plugin architecture) is a popular pattern that provides the ability to easily extend application logic to external, pluggable components.

This architecture works by focusing the primary functionality into the core system and providing extensibility through the plugin system. Plugins can be from different developers.

e.g. IntelliJ uses a plugin architecture to allow you to install third-party extensions.

Plugin Architecture
Plugin Architecture

Advantages

  • Great flexibility and extensibility.
  • Can add plug-ins while the application is running.
  • Plug-in modules can be tested in isolation.
  • Good portability.

Layered

This is the simplest approach we can take: we just split our application logic into a couple of distinct classes/areas of responsibility. This is also known as a layered architecture .

Standard layers in this style of architecture include:

  • Presentation: UI layer that the user interacts with.
  • Business Logic: the application logic, or “business rules”.
  • Persistence Layer: describes how to manage and save application data.
  • Data Store: the underlying mechanism to actually save data.
Monolithic Architecture
Monolithic Architecture.

The characteristics of this approach:

  • Each layer is a module, containing classes with similar functionality.
  • There is no limit to the number of layers, although the four illustrated here are common.
  • Each layer provides services to the layers above it.
  • Dependencies are top-down only. e.g. the UI depends on the underlying business layer, which depends on the persistence layer and so on.

The major characteristic of a monolithic or layered style is that it enforces a clear separation of concerns between layers: the Presentation layer doesn’t know anything about the application state or logic, it just displays the information; the Business Logic layer knows how to manipulate the data, but not how it is stored and so on.

Data is passed back up the layers through dependency inversion: the use of interfaces to decouple the lowest layers from the rest of the application.

Quality Score Comment
Usability Medium It’s probably fine for the initial implementation only.
Extensibility Low It’s difficult to modify without introducing errors
Scalability Low No way to handle more data.
Robustness Low Changing it is likely to break something.
Reusability Low No way that you can untangle this!

A layered architecture is an extremely common way to build an application! It falls out of some organizational styles quite naturally (see Conway’s Law ).

  • Front-end developers and designers work on the Presentation (UI) layer.
  • Back-end and database developers work on the Persistence layer.
  • Full-stack developers integrate these layers.

The layered architecture is similar to Model-View-Controller (MVC) , which leverages the Observer design pattern to separate business logic from the user interface. MVC is focused specifically on user-interfaces; we’ll revisit it again when we talk about designing UIs.

Client-Server

A client-server architecture breaks an application into a client (front-end) and remote server (back-end) which handles data processing and/or storage. This is a logical extension to a layered architecture, where we can move some of the processing (typically the persistance layer) to a different system.

Client-Server Architecture
Client-Server Architecture.

One things that differentiates client-server from other distributed styles is that it relies on a request-response model: the client requests data from the server, which performs the required work, forms a response and returns it to the client. The server cannot initiate a conversation - it can only reply to requests that are initiated by the client.

This model is perfectly fine for specific scenarios. e.g., a web browser contacting a web server. However, it’s not suitable for all types of networked applications.

Quality How it applies
Usability High - in combination with some other pattern
Extensibility Low - it’s a pretty rigid structure
Scalability Low - difficult to add more servers
Robustness Low - single point of failure
Reusability Medium - server can be repurposed

Service

A services-based architecture splits functionality into small “portions of an application”. Each service is independent and separately deployed (i.e. a separate application), and provides specific coarse-grained domain functionality to the clients.

The client application needs to communicate with each service using some lightweight protocol. If data needs to be shared, services can share data via a database.

e.g. a service might handle a customer checkout request to process an order; this could be processed in its entirely by one service, as a single transaction.

Services Architecture
Services Architecture. Diagram from Maveric Systems

Microservices

A microservices architecture is a collection of loosely coupled services.

Services are organized around business capabilities i.e. they provide specialized, domain-specific services to applications. However, these services are small, decentralized, and independently deployable. Each micro-service operates independently, but can coordinate tasks with other services if needed.

What makes them different from services? We allow redundant services! This provides scalability e.g., to handle increased demand or component failures.

Microservices Architecture
Microservices Architecture. Diagram from Maveric Systems
Quality Score Comments
Usability High In combination with some other pattern
Extensibility Low It’s a pretty rigid structure
Scalability Low Difficult to add more servers
Robustness Low Single point of failure
Reusability Medium Server can be repurposed

Peer-to-Peer

A peer-to-peer network attempts to overcome the restrictions of a simple client-server network, by allowing peers to communicate with one another. This approach makes sense in scenarios where you don’t have centralized data to share. Examples are messaging clients, or peer-to-peer file-sharing where there is no single authority.

Of course, “sharing responsibility” means that you need specific mechanisms in place so that peers can communicate. e.g., how to peers “find” one another? how do they authenticate and handle security?

Peer-to-Peer Architecture
Peer-to-Peer Architecture.

In a Peer-to-Peer network, all peers are free to communicate with one another; a server is just another peer and is not strictly required.

Quality Score Comments
Usability High In combination with some other pattern
Extensibility Low It’s a pretty rigid structure
Scalability Low Difficult to add more servers
Robustness Low Single point of failure
Reusability Medium Server can be repurposed

Broker

The Broker pattern (re)introduces a server to act as an intermediary in a distributed network. It can handle peer identification (i.e. contact the server to get a list of peers), resource allocation (what services are available?) and even authentication (as as central sign-in authority).

A broker acts as an intermediary between resources on a network.

Broker Architecture
Broker Architecture.
Quality Score Comments
Usability High In combination with some other pattern
Extensibility Low It’s a pretty rigid structure
Scalability Low Difficult to add more servers
Robustness Low Single point of failure
Reusability Medium Server can be repurposed