Up to this point, we’ve assumed that we’re designing a standalone application i.e. for a single computer. Historically, many software applications were designed to be standalone, and much of the software that we use is still standalone.
However, it can be useful to sometimes split processing across multiple systems. Here’s a partial list of the reasons why you might want to do this:
- Resource sharing. We often need to share resources across users. For example, storing our customer data in a shared databases that everyone in the company can access.
- Reliability. We might want to increase the reliability of our software by redunant copies running on different systems. This allows for fault tolerance - failover in case the first system fails. This is common with important resources like a web server.
- Performance. It can be more cost effective to have one highly capable machine running the bulk of our processing, while cheaper/smaller systems can be used to access that shared machine. It can also be cheaper to spread computation across multiple systems, where tasks can be run in parallel. Distributed architectured provide flexibility to align the processing capabilities with the task to be performed.
- Scalability. Finally, if designed correctly, distributing our work across multiple systems can allow us to grow our system to meet high demand. Amazon for example, needs to ensure that their systems remain responsive, even in times of heavy load (e.g. holiday season).
- Openness. There is more flexibility, since we can mix systems from different vendors.
Earlier, we discussed a distributed application as a set of components, spread across more than one machine, and communicating with one another over a network. Each component has some capabilities that it provides to the other components, and many of them coordinate work to accomplish a specific task.
We have already described a number of different distributed architectures. These enforce the idea that distributed systems can take many different forms, each with it’s own advantages and disadvantages. When we’re considering building a service, we’re really focusing on a particular kind of distributed system, where our application is leveraging remote resources.
A client-server architecture is a model where one centralized server provides capabilities to multiple client machines.
- Client − The process that issues a request to the second process i.e. the server. From the point of view of the user, this is the application that they interact with.
- Server − The process that receives the request, carries it out, and sends a reply to the client.
In this architecture, the application is modelled as a set of services that are provided by servers and a set of clients that use these services. The servers need not know about clients, but the clients must know the identity of servers.
- Separation of responsibilities such as user interface presentation and business logic processing (client).
- Reusability of server components and potential for concurrency (single server).
- It also makes effective use of resources when a large number of clients are accessing a high-performance server.
- Limited server availability and reliability.
- Limited testability and scalability.
- Fat clients with presentation and business logic together.
- Limited ability to scale the server (need more processing, “buy a bigger server”).
A multi-tier architecture (also known as 2-tier, 3-tier or layered) is an architecture that separates an application into separate tiers or areas of concerns1. Often the user interface, business logic and data layers end up split apart.
The most common form is this architecture is 3-tier architecture, where each tier is a separate module, deployed on a separate computer. Tiers typically communicate over a network connection.
- The top tier is the Presentation layer, which handles the UI logic. This is typically hosted on a client machine (i.e. where the user accesses it).
- The middle layer is the Application or Logic tier, which handles “business logic”; the rules and state management of the application. This can often include logic for coordinating requests from a client across multiple services as well.
- The bottom layer is the Data tier, which handles access, storage and management of the underlying data. This is often a database, or a wrapper around a database (or some other form of storage).
This is an extremely common architecture for Enterprise applications. It also aligns with the way that websites are traditionally served (Presentation tier is the browser, Logic tier is the web server and underlying code, and the Data tier is a database).
In some cases, the Logic and Presentation tiers are combined, for a 2-tier architecture consisting of just Presentation and Data tiers.
- Enhances the reusability and scalability − as demands increase, extra servers can be added.
- Provides maintainability and flexibility.
- Greater complexity, more difficult to deploy and test across tiers.
- More emphasis on server reliability and availability.
A service-oriented architecture (SOA) is an architectural style that supports service orientation. A service is a discrete unit of functionality that can be accessed remotely and acted upon and updated independently, such as retrieving a credit card statement online.
In other words, services exist independently of clients, and provide services to any client that requires it. Services are loosely coupled to one another, and should act independently.
Principles of SOA, governing how services should be designed:
- Service contract: there should be an agreed upon interface for accessing a service.
- Longevity: services should be designed to be long-lived (long-running).
- Autonomy: services should work independently of one another.
- Service composibility: services can be used to compose other services.
- Stateless: services should not track state, but either return a resulting value or throw an exception if necessary.
Because they are independent entities, we need a supporting infrastructure around services, and applications that are designed to leverage that infrastructure. This includes a repository, where an application can search for services that can meet its needs at runtime.
- A client or any service can access other services regardless of their platform, technology, vendors, or language implementations.
- Each service component is independent from other services due to the stateless service feature.
- The implementation of a service will not affect the application of the service as long as the exposed interface is not changed.
- Enhances the scalability and provides standard connection between systems.
- Even more complexity in setting up a system, since we’re now distributing across multiple tiers.
- Registry and other supporting infrastructure can be complex to setup and maintain.
- Difficulty debugging, profiling and so on.
A microservices architecture arranges an application as a collection of loosely coupled services, using fine-grained services and a lightweight protocol. Some of the defining characteristics of microservices:
- Services are organized around business capabilities i.e. they provide specialized, domain-specific services to applications (or other services).
- Service are not tied to any one programming language, platform or set of technologies.
- Services are small, decentralized, and independently deployable.
A microservice based architecture is really a subtype of SOA, which an emphasis on smaller, domain-specific components with very narrow functions.
- Easier to design, build and deploy small targeted services.
- Redundancy - you can always “spin up” a replacement service if something fails.
- Performance - you can always “scale out” by firing up redundant services to share the workload, as required.
- Extremely difficult to test and debug.
- Practically requires supporting services, like a registry for processes to locate service endpoints.
The terms “tier” and “layer” are sometimes used interchangeably, but they do not mean the same thing. Layer is a logical structuring mechanism, while a tier represents physical structuring. These do not necessarily need to align perfectly, as a logical layer (for example) may be distributed across physical systems, especially at large-scale. ↩︎