Presentation can be found at Software design principles for evolving architectures.
Layered architecture is the most common architecture. It is also known as n-tiered architecture. For several years now, many enterprises and companies employed this architecture in their projects and it almost became the de facto standard therefore it is widely known by most architects, developers and designers.
Layers or components within layered architecture are organized into horizontal layers. Each layer performs a specific role in the application. Depending on your needs and the complexity of your software you can have N layers, however most applications work with 3-4 layers. Having too many layers is not good and leads to complexity, because you have to maintain all those layers. In a conventional layered architecture, you can find presentation, business or service, data access layers. Presentation layer deals with the look and feel of the application. UI related work happens here. We usually have Data Transfer Objects to carry on the data to this layer. Then with View Models output is rendered to the client. Business layer is responsible for executing certain business rules for requests. Data access layer is responsible for any database operations, every request to or from database passes through this layer.
Layers don’t have to know about what other layers do, in example: business layer doesn’t have to know how data layer is querying the database, instead business layer expects some data or not at all when it invokes certain method in data layer. This is where we meet separation of concerns. This is a very powerful feature. Each layer is responsible for its own purposes.
The key concept in layered architecture is managing dependencies. If you apply dependency inversion principle and use TDD (test driven development), your architecture becomes more robust. You need to ensure that you have all test cases for all possible uses cases.
If you have redundancy such that even though you are not doing any business processing but just calling business layer which invokes data layer, this is called layers of isolation. For some features if you call directly data layer from presentation layer, then any change made in data layer affects both business layer and presentation layer.
An important concept in layered architecture is that, if a layer is open or closed. If the layer is closed every request should go through this particular layer. If the layer is open, requests can pass through the layer to the next one.
Layers of isolation help reduce complexity of the whole application. Some of the features and functionality doesn’t need to go through all the layers; this is where we need open-closed approach to simplify implementations.
Layered architecture is solid general purpose architecture when you are not sure which architecture is most suitable for you. It is a good starting point. You need to watch out for architecture sinkhole anti-pattern. This anti-pattern describes how requests passes through without doing anything or with little processing. If your requests pass through all the layers without doing anything, this is a sign of sinkhole anti-pattern. If you have 20 percent of requests just passes through layers and 80 percent of requests does real processing, it is fine, however if the ratio is different then you are having sinkhole anti-pattern syndrome.
Moreover, layered architectures can become monoliths and hard maintain code the base.
Layered Architecture Analysis:
Agility: Overall agility is the ability to respond to a change in a constantly changing environment. Due to the nature of monoliths, it might become hard to reflect the changes through all the layers, developers need to pay attention to dependencies and isolation of layers.
Ease of Deployment: For larger applications deployment can become a problem. A small requirement might require the whole application to be deployed. If continuous delivery is done right it might help.
Testability: With mocking and faking, each layer can tested independently hence makes testing easy.
Performance: While layered applications can perform well, since requests has to go through multiple layers, it might have performance problems.
Scalability: It is not very easy to scale layered applications, because of tight coupling and monolithic nature of the pattern. However, if layers are built as independent deployments, scalability can be achieved. But it might be expensive to do so.
Ease of development: This pattern is particularly very easy to develop. Many companies adopt this pattern. Most developers know, understand and learn easily how to work with it.
Event Driven Architecture
Event driven architecture is a popular distributed asynchronous architecture pattern which is used to create scalable applications. This pattern is adaptive and can be applied to small or large scale applications. Event driven architecture can be applied with mediator topology or broker topology. It is essential to understand the difference to be able to select the correct topology for the application.
Mediator topology requires orchestration between multiple events. For example: imagine a trading system, each request should pass through certain steps such as validation, order, shipping and notifying buyer etc. Some of these steps can be done manually and some can be done in parallel.
There are usually 4 main types of components within the architecture which are event queues, mediator, and event channel and event processor. Client creates an event and sends to event queue, mediator receives the event and passes it over to event channels. Event channels pass the event to event processors in which event are processed.
Event mediator doesn’t do or knows any business logic, it just orchestrates the events. Event mediator knows the necessary steps for each event type. Business logic or processing happens in event processor. For event channels, message queues or topics can be used to pass the event to event processors. Event processors are self-contained, independent and decoupled from the architecture. Ideally each event processor should be responsible for only one event type.
Usually enterprise service bus, queue or hub can be used as event mediator. Choosing the correct technology, implementation reduces risk.
Broker topology unlike mediator topology doesn’t use any central orchestration. Simple queue or hub can be used between event processors and event processor knows the next event processor to handle the event.
Event driven architecture is relatively complex pattern to implement because of distributed and asynchronous nature. You can face with many problems such as network partitioning, failure of mediator, reconnection logic and so on. Since this is a distributed pattern, and it is async if you need transactions, you are in trouble; you will need a transaction coordinator. Transactions in distributed systems are very hard to manage. It is not easy to have a standard unit of work pattern here.
Yet another challenging concept here is contracts. Architects claim that services contracts should be defined up front and it is expensive to change.
Event Driven Architecture Analysis:
Agility: Since events and event processors are decoupled and can be maintained independently, agility of this pattern is high. Changes can be done quickly and easily while not affecting the overall system.
Ease of deployment: Since this architecture is decoupled, it is easy to deploy as well. Components can be deployed independently and can be registered at mediator. Broker topology is rather simple.
Testability: While testing independent components is easy, testing the overall application can be challenging. So end to end testing is hard.
Performance: Event driven architecture can perform very well since it is asynchronous. Moreover, event channels and event processes can work in parallel since they are decoupled.
Scalability: Event driven architecture can scale very well, since components are decoupled, components can scale independently.
Ease of development: It is not easy to develop this architecture. Contracts should be defined well, error handling and retry mechanisms should be done properly.
Microkernel architecture pattern
Microkernel architecture pattern is also known as plugin architecture pattern. This pattern is ideal for product based applications and consists of two components: core system and plug-in modules. Core system usually contains minimum business logic but it does ensure to load, unload and run the necessary plugins. Many operating systems use this pattern hence the name is microkernel.
Plugins can be independent from each other and hence decoupled. Core system has a registry where plugins are registered and core system knows where to find them as well as how to run them.
Even though this pattern is very suitable for desktop application, it can also be used in web applications. In fact, many different architectural patterns can be a plugin of the whole system. For product based application where you will have new features and functionalities added to system in time, microkernel architecture is a great choice.
Microkernel Architecture Analysis:
Agility: Since plugins can be developed independently and registered to the core system, microkernel architecture has high agility.
Ease of deployment: Depending on how the core system is implemented, deployment can be done without restarting the whole system.
Testability: If plugins are developed independently, which it should, testing can be done independently and in isolation. Plugins can also be mocked by the core system.
Performance: It depends how many plugins you are running, but performance can be tuned.
Scalability: If the complete system is deployed as a single unit, it would be hard to scale this system.
Ease of development: It is not easy to develop this architecture. Implementing the core system and registry can be difficult, moreover plugin contracts and data exchange models adds to the problem.
Microservices Architecture Pattern
Even though Microservices are fairly new, it did capture lot of attentions rather quickly as an alternative to monolithic applications and service oriented architecture. One of the core concepts is separately deployable units which enables high scalability, ease of deployment and delivery. The most important concept is service components. Service components include business logic and processes. Designing service components for desired granularity is essential and challenging. Service components are decoupled, distributed, independent from each other and accessible with a known protocol.
Microservices developed due to the problems in monolithic and service oriented applications. Monolithic applications usually contain layers that are tightly coupled which make deployments and delivery difficult. For example, if the application breaks every time there is a change, this is a big problem and arises from coupling. Microservices separates application into multiple deployable units hence enhances development, deployment and testability easier. While Service Oriented Architecture is very powerful, enables heterogeneous connectivity and loose coupling, it comes with a high cost. It is complex, expensive, difficult to understand and implement and usually overkill for most applications. Microservices simplifies this complexity.
It is totally normal to have redundant code across service components. You can violate DRY principle while developing Microservices. With the benefit of separate deployable units, deployment becomes much easier. Some of the challenges are contracts between service components and availability of service components.
Microservices Architecture Analysis:
Agility: Since service components can be developed independently and decoupled, microservices architecture has high agility. Separately deployable units can respond to changes rather quickly.
Ease of deployment: Microservices has advantages over other architectural patterns because services components are separately deployable units.
Testability: Testing can be done independently for service components. Testability is high.
Performance: Depends on the service components and distributed nature of this particular pattern.
Scalability: Scalability is high because of separately deployable units.
Ease of development: Separate service components can be implemented independently.