It is going to be the software-defined, digitization-enabled, cloud-hosted, context-aware, service-oriented, event-driven, and people-centric era. It is a well-known and widely accepted truth that reactive and cognitive software plays a very vital role in shaping up the projected and pronounced era of knowledge-filled and insight-driven services and applications. That is, we need highly responsive, reliable, scalable, adaptive, and secure software suites and libraries to fulfill the identified goals for the forthcoming era of knowledge. There are competent information and communication technologies (ICTs), tools, techniques, and tips emerging and evolving fast to artistically enable the realization of such kinds of advanced and astute software modules.
The quickly-enlarging domain of patterns has been there for several decades. The complexity of software engineering is also increasing in an uninhibited fashion. Software experts, evangelists, and exponents have articulated and accentuated the deft and decisive leverage of software patterns in order to mitigate the rising complexity of software engineering. Therefore, software patterns are widely being recognized as one prime and paramount method for building resilient and versatile software packages and programs. Professionals and professors have been steady in unearthing newer patterns. As a result, a bevy of path-breaking and promising architectural, design, deployment, delivery, and integration patterns are quickly emerging and evolving to speed up and streamline the increasingly complicated processes of designing, developing, debugging, deploying, and delivering robust and rewarding software applications.
This chapter aims to explain the prominent software patterns, particularly the following widely deliberated and detailed architecture patterns:
- Object-oriented architecture (OOA)
- Component-based assembly (CBD) architecture
- Domain-driven design architecture
- Client/server architecture
- Multi-tier distributed computing architecture
- Layered/tiered architecture
- Event-driven architecture (EDA)
- Service-oriented architecture (SOA)
- Microservices architecture (MSA)
- Space-based architecture (SBA)
- Special-purpose architectures
There are newer requirements such as smarter homes, hotels, hospitals, and so on, and the crucial role and responsibility of information and communication technologies (ICT) fulfilling the varying business and people needs are growing steadily. There are a variety of noteworthy trends and transitions happening in the enigmatic ICT space these days. The prominent ones include the following:
- IT industrialization through cloud computing
- IT compartmentalization through virtualization and containerization
- IT consumerization through handhelds, wearables, mobiles, nomadic devices, and so on
- The extreme and deeper connectivity amongst all kinds of physical, electrical, mechanical, and electronic systems through the leverage of the Internet of Things (IoT) technologies, cyber-physical systems (CPSs), and so on
- Cognitive IT to empower our everyday systems to be cognitive of their actions and reactions
With everything getting connected with one another, the amount of data getting generated, collected, cleansed, and crunched goes up exponentially. There are integrated platforms for big, fast, streaming, and IoT data analytics to extricate useful information and actionable insights out of data heaps. The database paradigm is going through a slew of changes and challenges.
The middleware domain is upping the ante as there are heterogeneous and multiple systems and services to be integrated and made to work together. I can go on and on. In short, both the IT and business landscapes are changing day by day. Also, businesses expect their IT service providers to be innovative, disruptive, and transformative in order to achieve more with less. As the IT budgets are being pruned by businesses across the world, the IT domain has to pick the latest advancements in order to be right and relevant to their stakeholders.
As usual, the field of software engineering is also progressing steadily with a dazzling array of noteworthy progressions. There are agile programming models for enabling the business agility. In the recent past, there have been DevOps methods germinating for guaranteeing IT agility. There are newer software infrastructure and platform solutions coming up fast in order to meet various requirements from different stakeholders. Professionals and professors are working overtime to ensure process excellence and infrastructure optimization. Strategically sound architectural paradigms and styles are being assimilated. Further on, the automation level will pick up and reach greater heights with the adoption of artificial intelligence (AI) methods.
As we all know, the most powerful domain of software engineering has been accomplishing grandiose things for the business acceleration, augmentation, and automation. With the arrival and articulation of the IoT and CPS paradigms, the software field is steadily and sagaciously veering towards the much-expected people empowerment. Accordingly, there is a greater demand for software applications and services to intelligently empower not only business operations and offerings, but also to contribute to everyday decisions, deals, and deeds of individuals, innovators, and institutions. The currently available programming models, frameworks, and tools are helping us out in producing applications that fulfill functional requirements. Hereafter, the crucial challenge ahead for software professionals and practitioners is to bring forth software libraries and suites that comply with all kinds of non-functional requirements (NFRs) / Quality of Service (QoS) attributes. That is, we ought to construct applications that innately ensure various abilities, such as reliability, scalability, availability, modifiability, sustainability, security, and so on. Software patterns come in handy here. Precisely speaking, the penetration, participation, and pervasiveness of software patterns are consistently on the increase.
As we all know, patterns are a kind of simplified and smarter solution for a repetitive concern and recurring challenge in any field of importance. In the field of software engineering, there are primarily many designs, integration, and architecture patterns. These patterns come in handy in speedily surmounting some of the routine and fresh issues being encountered by software architects, developers, and integrators in their everyday assignments and engagements. Design patterns are very popular and are used for expertly designing enterprise-class software systems whereas architectural patterns are used for skilfully deciding the optimized and organized architecture for a system under development. The changes and complexities are substantially taken care of through the deft leverage of various architectural patterns. The architectural patterns enable taking a series of decisions regarding the choice of technologies and tools. The various system components, their distinct capabilities, and how they connect and collaborate with one another are the key ingredients in architecting and designing next-generation software systems. The architectural patterns render their yeoman services here. With system architectures increasingly becoming complicated, the role and responsibility of architectural patterns are consistently on the increase. Similarly, as we tend towards the distributed and decentralized era, the integration patterns are very significant as they bring a lot of praiseworthy simplifications and delegations.
With the flourishing of the DevOps concept, there are additional patterns emerging in IT agility. Patterns also assiduously accelerate the process of building newer and powerful software packages. Besides the development-centric patterns, there are deployment and delivery-specific patterns also. In the ensuing DevOps and NoOps days, the deployment patterns are going to be highly beneficial in automating the time-consuming and tedious tasks. Similarly, there are delivery-enablement patterns. Patterns are fast-evolving and stabilizing tellingly with more usage and continuous refinements. Patterns are capable of overcoming the initial hiccups for newer technologies, too. Patterns will be a key ingredient for IT to survive and shine in the market-driven, knowledge-driven and cut-throat competitive environment. Precisely speaking, patterns are a crucial enabler in building sophisticated software by minimizing the workload of software developers. The risks being associated with constructing enterprise-scale, high-quality, and microservices-based applications are being substantially moderated by meticulously using the proven software patterns.
There is a bevy of noteworthy transformations happening in the IT space, especially in software engineering. The complexity of recent software solutions is continuously going up due to the continued evolution of the business expectations. With complex software, not only does the software development activity become very difficult, but also the software maintenance and enhancement tasks become tedious and time-consuming. Software patterns come as a soothing factor for software architects, developers, and operators.
Software systems are becoming extremely complicated and sophisticated in order to meet up the newer demands of the business. The field of software architecture helps to smoothen and straighten the path towards producing well-defined and designed software suites. Software architecture is primarily tuned for moderating the rising software complexities and changes. Hence, there are purported efforts to bring forth software architecture patterns to arrive at easy-to-implement and sustainable software architectures. This section begins with some basics about architecture and goes on to elaborate on some of the widely used software architectural patterns.
Architecture is essential for systems that are increasingly becoming complex due to the continuous addition of fresh modules. Architectures generally are decided by three crucial aspects: the participating components, the distinct capabilities of each of those components, and, finally, the connectivity between those components. Establishing software architectures is not an easy job. A variety of factors need to be taken into consideration while deciding the architecture. A number of architectural decisions need to be meticulously considered in order to strengthen the final architecture. Not only functional requirements but also non-functional requirements too need to be inscribed in the architecture. Typically, the architecture pattern is for designing a generic architecture for a system, such as a software solution.
Several different formats are used in the literature for describing patterns, and no single format has achieved widespread acceptance. The following elements described will be found in most patterns, even if different headings are used to describe them. In the Opengroup.org site, the following terminologies are used:
- Name: A meaningful and memorable way to refer to the pattern, typically a single word or short phrase.
- Problem: This is a concise description of the problem at hand. It has to throw some light on the intended goals and objectives to be reached within the context.
- Context: The context typically illustrates the preconditions under which the pattern can be applied. That is, it is a description of the initial state before the pattern is applied.
- Forces: This is for describing the relevant forces and constraints and how they interact/conflict with each other. It inscribes the intended goals and objectives. The description should clarify the intricacies of the problem and make explicit the kinds of trade-offs that must be considered. The notion of forces more or less equates to the QoS attributes (availability, scalability, security, sustainability, composability, maneuverability, resiliency, reliability, reusability, and so on) that architects seek to obtain and optimize besides the concerns they seek to address in designing architectures.
- Solution: This is all about clearly explaining how to achieve the intended goals and objectives. The description should identify both the solution's static structure and its dynamic behavior.
- Resulting context: This indicates the post-conditions after the pattern is applied. Implementing the solution normally requires trade-offs among competing forces. This element describes which forces have been resolved and how, and which remain unresolved. It may also indicate other patterns that may be applicable in the new context.
- Examples: This is about incorporating a few sample applications of the pattern for illustrating each of the elements (a specific problem, the context, the set of forces, how the pattern gets applied, and the resulting context).
- Rationale: It is necessary to give a convincing explanation/justification of the pattern as a whole or of the individual components within it. The rationale has to indicate how the pattern actually works and how it resolves the forces to achieve the desired goals and objectives.
- Related patterns: There are other similar patterns and hence the relationships between this pattern and others have to be clearly articulated. These may be predecessor patterns, whose resulting contexts correspond to the initial context of this one. Or, these may be successor patterns, whose initial contexts correspond to the resulting context of this one. There may also be alternative patterns, which describe a different solution to the same problem, but under different forces. Finally these may be co-dependent patterns, which may/must be applied along with this pattern.
- Known uses: This has to detail the known applications of the pattern within existing systems. This is for verifying that the pattern does indeed describe a proven solution to a recurring problem. The known uses can also serve as value-added examples.
Patterns may also begin with an abstract providing an overview of the pattern and indicating the types of problems it addresses. The abstract may also identify the target audience and what assumptions are made of the reader.
Several newer types of patterns are emerging in order to cater to different demands. This section throws some light on these.
An architecture pattern expresses a fundamental structural organization or schema for complex systems. It provides a set of predefined subsystems, specifies their unique responsibilities, and includes the decision-enabling rules and guidelines for organizing the relationships between them. The architecture pattern for a software system illustrates the macro-level structure for the whole software solution. An architectural pattern is a set of principles and a coarse-grained pattern that provides an abstract framework for a family of systems. An architectural pattern improves partitioning and promotes design reuse by providing solutions to frequently recurring problems. Precisely speaking, an architectural pattern comprises a set of principles that shape an application.
A design pattern provides a scheme for refining the subsystems or components of a system, or the relationships between them. It describes a commonly recurring structure of communicating components that solves a general design problem within a particular context. The design pattern for a software system prescribes the ways and means of building the software components. The design pattern articulates how the various components within the system collaborate with one another in order to fulfil the desired functionality.
There are other patterns, too. The dawn of the big data era mandates for distributed computing. The monolithic and massive nature of enterprise-scale applications demands microservices-centric applications. Here, application services need to be found and integrated in order to give an integrated result and view. Thus, there are integration-enabled patterns. Similarly, there are patterns for simplifying software deployment and delivery. Other complex actions are being addressed through the smart leverage of simple as well as composite patterns. In the next section, we will discuss the various dimensions of IT with the intention of conveying the tremendous impacts of software patterns for next-generation IT.
This section is allocated for describing the prominent and dominant software architecture patterns.
There are several weaknesses associated with monolithic applications:
- Scalability: Monolithic applications are designed to run on a single and powerful system within a process. Increasing the application's speed or capacity requires fork lifting onto newer and faster hardware, which takes significant planning and consideration.
- Reliability and availability: Any kind of faults or bugs within a monolithic application can take the entire application offline. Additionally, updating the application typically requires downtime in order to restart services.
- Agility: Monolithic code bases become increasingly complex as features are being continuously added, and release cycles are usually measured in periods of 6-12 months or more.
As already mentioned, legacy applications are monolithic in nature and massive in size. Refactoring and remedying them to be web, cloud, and service-enabled is bound to consume a lot of time, money, and talent. As enterprises are consistently pruning the IT budget and still expecting more with less from IT teams, the time for leveraging various architectural patterns individually or collectively to prepare and put modernized applications has arrived. The following sections detail the various promising and potential architecture patterns.
Objects are the fundamental and foundational building blocks for all kinds of software applications. The structure and behavior of any software application can be represented through the use of multiple and interoperable objects. Objects elegantly encapsulate the various properties and the tasks in an optimized and organized manner. Objects connect, communicate, and collaborate through well-defined interfaces. Therefore, the object-oriented architectural style has become the dominant one for producing object-oriented software applications. Ultimately, a software system is viewed as a dynamic collection of cooperating objects, instead of a set of routines or procedural instructions.
We know that there are proven object-oriented programming methods and enabling languages, such as C++, Java, and so on. The properties of inheritance, polymorphism, encapsulation, and composition being provided by OOA come in handy in producing highly modular (highly cohesive and loosely coupled), usable and reusable software applications.
The object-oriented style is suitable if we want to encapsulate logic and data together in reusable components. Also, the complex business logic that requires abstraction and dynamic behavior can effectively use this OOA.
Monolithic and massive applications can be partitioned into multiple interactive and smaller components. When components are found, bound, and composed, we get the full-fledged software applications. Components emerge as the building-block for designing and developing enterprise-scale applications. Thus, the aspects of decomposition of complicated applications and the composition of components to arrive at competent applications receive a lot of traction. Components expose well-defined interfaces for other components to find and communicate. This setup provides a higher level of abstraction than the object-oriented design principles. CBA does not focus on issues such as communication protocols and shared states. Components are reusable, replaceable, substitutable, extensible, independent, and so on. Design patterns such as the dependency injection (DI) pattern or the service locator pattern can be used to manage dependencies between components and promote loose coupling and reuse. Such patterns are often used to build composite applications that combine and reuse components across multiple applications.
Aspect-oriented programming (AOP) aspects are another popular application building block. By deft maneuvering of this unit of development, different applications can be built and deployed. The AOP style aims to increase modularity by allowing the separation of cross-cutting concerns. AOP includes programming methods and tools that support the modularization of concerns at the level of the source code. Aspect-oriented programming entails breaking down program logic into distinct parts (concerns, the cohesive areas of functionality). All programming paradigms intrinsically support some level of grouping and encapsulation of concerns into independent entities by providing abstractions (for example, functions, procedures, modules, classes, methods, and so on). These abstractions can be used for implementing, abstracting, and composing various concerns. Some concerns anyway cut across multiple abstractions in a program and defy these forms of implementation. These concerns are called cross-cutting concerns or horizontal concerns.
Logging exemplifies a cross-cutting concern because a logging strategy necessarily affects every logged part of the system. Logging thereby cross-cuts all logged classes and methods. In short, aspects are being represented as cross-cutting concerns and they are injected on a need basis. Through the separation of concerns, the source code complexity comes down sharply and the coding efficiency is bound to escalate.
Agent-oriented software engineering (AOSE) is a programming paradigm where the construction of the software is centered on the concept of software agents. In contrast to the proven object-oriented programming, which has objects (providing methods with variable parameters) at its core, agent-oriented programming has externally specified agents with interfaces and messaging capabilities at its core. They can be thought of as abstractions of objects. Exchanged messages are interpreted by receiving agents, in a way specific to its class of agents.
A software agent is a persistent, goal-oriented computer program that reacts to its environment and runs without continuous direct supervision to perform some function for an end user or another program. A software agent is the computer analog of an autonomous robot. There are a set of specific applications and industry verticals that require the unique services of software agents. Thus, we have software objects, components, aspects, and agents as the popular software construct for building a bevy of differently abled applications.
Domain-driven design is an object-oriented approach to designing software based on the business domain, its elements and behaviors, and the relationships between them. It aims to enable software systems that are a correct realization of the underlying business domain by defining a domain model expressed in the language of business domain experts. The domain model can be viewed as a framework from which solutions can then be readied and rationalized.
Architects have to have a good understanding of the business domain to model. The development team has too often worked with business domain experts to model the domain in a precise and perfect manner. In this, the team agrees to only use a single language that is focused on the business domain, by excluding any technical jargon. As the core of the software is the domain model, which is a direct projection of this shared language, it allows the team to quickly find gaps in the software by analyzing the language around it. The DDD process holds the goal not only of implementing the language being used, but also improving and refining the language of the domain. This, in turn, benefits the software being built.
DDD is good if we have a complex domain and we wish to improve communication and understanding within the development team. DDD can also be an ideal approach if we have large and complex enterprise data scenarios that are difficult to manage using the existing techniques.
This pattern segregates the system into two main applications, where the client makes requests to the server. In many cases, the server is a database with application logic represented as stored procedures. This pattern helps to design distributed systems that involve a client system and a server system and a connecting network. The simplest form of client/server architecture involves a server application that is accessed directly by multiple clients. This is referred to as a two-tier architecture application. Web and application servers play the server role in order to receive client requests, process them, and send the responses back to the clients. The following figure is the pictorial representation of the client/server pattern:
The peer-to-peer (P2P) applications pattern allows the client and server to swap their roles in order to distribute and synchronize files and information across multiple clients. Every participating system can play the client as well as the server role. They are just peers working towards the fulfillment of business functionality. It extends the client/server style through multiple responses to requests, shared data, resource discovery, and resilience to the removal of peers.
The main benefits of the client/server architecture pattern are:
- Higher security: All data gets stored on the server, which generally offers a greater control of security than client machines.
- Centralized data access: Because data is stored only on the server, access and updates to the data are far easier to administer than in other architectural styles.
- Ease of maintenance: The server system can be a single machine or a cluster of multiple machines. The server application and the database can be made to run on a single machine or replicated across multiple machines to ensure easy scalability and high availability. The multiple machines eventually form a cluster through appropriate networking. Lately, the enterprise-grade server application is made up of multiple subsystems and each subsystem/microservice can be run on the separate server machine in the cluster. Another trend is each subsystem and its instances are also being hosted and run on multiple machines. This sort of single or multiple server machines being leveraged for executing server applications and databases ensures that a client remains unaware and unaffected by a server repair, upgrade, or relocation.
However, the traditional two-tier client/server architecture pattern has numerous disadvantages. Firstly, the tendency of keeping both application and data in a server can negatively impact system extensibility and scalability. The server can be a single point of failure. The reliability is the main worry here. To address these issues, the client-server architecture has evolved into the more general three-tier (or N-tier) architecture. This multi-tier architecture not only surmounts the issues just mentioned, but also brings forth a set of new benefits.
The two-tier architecture is neither flexible nor extensible. Hence, multi-tier distributed computing architecture has attracted a lot of attention. The application components can be deployed in multiple machines (these can be co-located and geographically distributed). Application components can be integrated through messages or remote procedure calls (RPCs), remote method invocations (RMIs), common object request broker architecture (CORBA), enterprise Java beans (EJBs), and so on. The distributed deployment of application services ensures high availability, scalability, manageability, and so on. Web, cloud, mobile, and other customer-facing applications are deployed using this architecture.
Thus, based on the business requirements and the application complexity, IT teams can choose the simple two-tier client/server architecture or the advanced N-tier distributed architecture to deploy their applications. These patterns are for simplifying the deployment and delivery of software applications to their subscribers and users.
This pattern is an improvement over the client/server architecture pattern. This is the most commonly used architectural pattern. Typically, an enterprise software application comprises three or more layers: presentation / user interface layer, business logic layer, and data persistence layer. Additional layers for enabling integration with third-party applications/services can be readily inscribed in this layered architecture. There are primarily database management systems at the backend, the middle tier involves an application and web server, and the presentation layer is primarily user interface applications (thick clients) or web browsers (thin clients). With the fast proliferation of mobile devices, mobile browsers are also being attached to the presentation layer. Such tiered segregation comes in handy in managing and maintaining each layer accordingly. The power of plug-in and play gets realized with this approach. Additional layers can be fit in as needed. There are model view controller (MVC) pattern-compliant frameworks hugely simplifying enterprise-grade and web-scale applications. MVC is a web application architecture pattern. The main advantage of the layered architecture is the separation of concerns. That is, each layer can focus solely on its role and responsibility. The layered and tiered pattern makes the application:
- Easy to assign specific and separate roles
- Easy to update and enhance layers separately
This architecture pattern is good for developing web-scale, production-grade, and cloud-hosted applications quickly and in a risk-free fashion. The current and legacy-tiered applications can be easily modified at each layer with newer technologies and tools. This pattern remarkably moderates and minimizes the development, operational, and management complexities of software applications. The partitioning of different components participating in the system can be replaced and substituted by other right components. When there are business and technology changes, this layered architecture comes in handy in embedding newer things in order to meet varying business requirements.
As illustrated in the following figure, there can be multiple layers fulfilling various needs. Some layers can be termed as open in order to be bypassed during some specific requests. In the figure, the services layer is marked as open. That is, requests are allowed to bypass this opened layer and go directly to the layer under it. The business layer is now allowed to go directly to the persistence layer. Thus, the layered approach is highly open and flexible.
In short, the layered or tiered approach is bound to moderate the rising complexity of software applications. Also, bypassing certain layers, the flexibility is being incorporated easily. Additional layers can be embedded as needed in order to bring highly synchronized applications.
Generally, server applications respond to clients requests. That is, the request and reply method is the main one for interactions between clients and servers as per the famous client-server architectural style. This is kind of pulling information from servers. The communication is also synchronous. In this case, both clients and servers have to be available online in order to initiate and accomplish the tasks. Further on, when service requests are being processed and performed by server machines, the requesting services/clients have to wait to receive the intended response from servers. That means clients cannot do any other work while waiting to receive servers' responses.
The world is eventually becoming event-driven. That is, applications have to be sensitive and responsive proactively, pre-emptively, and precisely. Whenever there is an event happening, applications have to receive the event information and plunge into the necessary activities immediately. The request and reply notion paves the way for the fire and forgets tenet. The communication becomes asynchronous. There is no need for the participating applications to be available online all the time.
An event is a noteworthy thing that happens inside or outside of any business. An event may signify a problem, an opportunity, a deviation, state change, or a threshold break-in. Every event occurrence has an event header and an event body. The event header contains elements describing the event occurrence details, such as specification ID, event type, name, creator, timestamp, and so on. The event body concisely yet unambiguously describes what happened. The event body has to have all the right and relevant information so that any interested party can use that information to take necessary action in time. If the event is not fully described, then the interested party has to go back to the source system to extract the value-adding information.
EDA is typically based on an asynchronous message-driven communication model to propagate information throughout an enterprise. It supports a more natural alignment with an organization's operational model by describing business activities as series of events. EDA does not bind functionally disparate systems and teams into the same centralized management model. EDA ultimately leads to highly decoupled systems. The common issues being introduced by system dependencies are getting eliminated through the adoption of the proven and potential EDA.
We have seen various forms of events used in different areas. There are business and technical events. Systems update their status and condition emitting events to be captured and subjected to a variety of investigations in order to precisely understand the prevailing situations. The submission of web forms and clicking on some hypertexts generate events to be captured. Incremental database synchronization mechanisms, RFID readings, email messages, short message service (SMS), instant messaging, and so on are events not to be taken lightly. There can be coarse-grained and fine-grained events. Typically, a coarse-grained event is composed of multiple fine-grained events. That is, a coarse-grained event gets abstracted into business concepts and activities. For example, a new customer registration has occurred on the external website, an order has completed the checkout process, a loan application is approved in underwriting, a market trade transaction is completed, a fulfillment request is submitted to a supplier, and so on. On the other hand, fine-grained events such as infrastructure faults, application exceptions, system capacity changes, and change deployments are still important. But their scope is local and limited.
There are event processing engines, message-oriented middleware (MoM) solutions such as message queues and brokers to collect and stock event data and messages. Millions of events can be collected, parsed, and delivered through multiple topics through these MoM solutions. As event sources/producers publish notifications, event receivers can choose to listen to or filter out specific events and make proactive decisions in real-time on what to do next.
EDA style is built on the fundamental aspects of event notifications to facilitate immediate information dissemination and reactive business process execution. In an EDA environment, information can be propagated to all the services and applications in real-time. The EDA pattern enables highly reactive enterprise applications. Real-time analytics is the new normal with the surging popularity of the EDA pattern.
Anuradha Wickramarachchi in his blog writes that this is the most common distributed asynchronous architecture. This architecture is capable of producing highly scalable systems. The architecture consists of single-purpose event processing components that listen to events and process them asynchronously. There are two main topologies in the event-driven architecture:
- Mediator topology: The mediator topology has a single event queue and a mediator which directs each of the events to relevant event processors. Usually, events are fed into the event processors passing through an event channel to filter or pre-process events. The implementation of the event queue could be in the form of a simple message queue or through a message passing interface leveraging a large distributed system, which intrinsically involves complex messaging protocols. The following diagram demonstrates the architectural implementation of the mediator topology:
- Broker topology: This topology involves no event queue. Event processors are responsible for obtaining events, processing and publishing another event indicating the end. As the name of the topology implies, event processors act as brokers to chain events. Once an event is processed by a processor, another event is published so that another processor can proceed.
As the diagram indicates, some event processors just process and leave no trace and some tend to publish new events. The steps of certain tasks are chained in the manner of callbacks. That is, when one task ends, the callback is triggered, and all the tasks remain asynchronous in nature.
The EDA pattern lacks the atomicity of transactions since there is no execution sequence of the events. This is because event processors are being implemented to be highly distributed, decoupled, and asynchronous. The results are also expected to be provided at a future time mostly through callbacks. Testing of the systems with event-driven architecture is not easy due to the asynchronous nature of the processing. Finally, since the tasks are asynchronous and non-blocking, the executions happen in parallel, guaranteeing higher performance. This setup outweighs the cost of queueing mechanisms.
Business enterprises are being bombarded with a large number of simple as well as complex events every day, and the enterprise and cloud IT teams have to have the appropriate event capture and processing engines in place to take corrective actions and to give a pertinent answer in real-time. The well-known examples include all kinds of real-time and real-world IT systems, such as trade settlement systems, flight reservation systems, real-time vehicle location data for transportation and logistics companies, streaming stock data for financial services companies, and so on. Companies empower these systems to comfortably handle large volumes of complex data in real time.
We have been fiddling with object-oriented, component-based, aspect-oriented, and agent-based software development processes. However, with the arrival of service paradigms, software packages and libraries are being developed as a collection of services. That is, software systems and their subsystems are increasingly expressed and exposed as services. Services are capable of running independently of the underlying technology. Also, services can be implemented using any programming and script languages.
Services are self-defined, autonomous, and interoperable, publicly discoverable, assessable, accessible, reusable, and compostable. Services interact with one another through messaging. There are service providers/developers and consumers/clients. There are service discovery services that innately leverage both private and public service registries and repositories. Client services can find their serving services dynamically through service discovery services.
Every service has two parts: the interface and the implementation. The interface is the single point of contact for requesting services. Interfaces give the required separation between services. All kinds of deficiencies and differences of service implementation get hidden by the service interface. To make the service interface easy to use by other services, it is a good idea to use a schema definition that defines the structure of the messages. When a service is used by multiple other services, formalizing the service with a contract is paramount. A contract bounds the service with schemas, a clear message exchange pattern, and policies. Policies define the QoS attributes, such as scalability, sustainability, security and so on. SOA differs from the client/server architecture in the sense that services are universally available and stateless, while client/server architecture requires tight coupling among the participants.
Precisely speaking, SOA enables application functionality to be provided as a set of services, and the creation of personal as well as professional applications that make use of software services.
Services can integrate disparate and distributed applications and data sources. The Enterprise service bus (ESB) is the service middleware enabling service-based integration of multiple assets and resources. The ESB facilitates service interconnectivity, routing, remediation, enrichment, governance, and so on. The ESB is the integration middleware for any service environment, where the message is the basic unit of interaction between services. An ESB is lightweight compared with previous middleware solutions, such as the EAI hub. The ESB is lightweight because it obviates the need of using custom-made connectors, drivers, and adapters for integrating processes/applications, data sources, and UIs.
Let us consider a sample scenario. Application A is only capable of exporting files to a particular directory and application B would like to get some information out of an exported file in a SOAP message over HTTP. The ESB can implement a message flow that is triggered by a SOAP request message from application B and read the requested information of the exported file of application A with a file adapter.
The ESB gathers the requested information and transforms it into a SOAP message corresponding to an agreed upon XML schema. Then the ESB sends the SOAP message back to application B over HTTP.
The message flow is an important ingredient of any ESB solution. A message flow is a definition that describes where the message originates from, how it arrives at the ESB, and then how it lands at the target service/application. Matching is another prominent functionality provided by the ESB. This function prescribes which message flow must be executed when a message arrives in the ESB.
There are other key functionalities, routing, translation, and transformation of the message format. The routing is all about routing messages from one service to another service. Routing is often used by a message flow module to describe what service will be called for a particular incoming message. The second core functionality is the protocol translation. There are many application and message transmission protocols. An ESB can translate the requester protocol into the provider-compatible protocol. Suppose the requester supports the HTTP protocol and the provider/receiver supports the FTP protocol. Then, this functionality of ESB translates the HTTP protocol to the FTP protocol to enable different and distributed applications to find, bind, and interact. The following figure is the macro-level SOA:
The last core function of the ESB is the message/data format transformation. When a requestor sends a message in SOAP format, the provider can be called by the ESB with an EDIFACT message format. The technology behind such message-format transformations can be the proven XML stylesheet language transformation (XSLT).
In conclusion, heterogeneous applications are deployed in an enterprise and cloud IT environments to automate business operations, offerings, and outputs. Legacy applications are service-enabled by attaching one or more interfaces. By putting the ESB in the center, service-enabled applications are easily getting integrated to connect, communicate, collaborate, corroborate and correlate to produce the desired results. In short, SOA is for service-enablement and service-based integration of monolithic and massive applications. The complexity of enterprise process/application integration gets moderated through the smart leverage of the service paradigm. The ESB is the most optimal middleware solution for enabling disparate and distributed applications to talk with one another in a risk-free fashion.
Today, most of the SOA efforts are keen on implementing synchronous request-response interaction patterns to connect different and distributed processes. This approach works well for highly centralized environments and creates a kind of loose coupling for distributed software components at the IT infrastructure level. However, SOA leads to the tight coupling of application functions due to the synchronous communication. This being said, increasingly enterprise environments are tending towards being dynamic and real-time in their interactions, decision-enablement, and actuation. The SOA patterns may find it difficult in ensuring the overwhelmingly pronounced requirements of next-generation enterprise IT.
SOA is a good option if the requirement is just to send requests and receive responses synchronously. But SOA is not good enough to handle real-time events asynchronously. That is why the new pattern of event-driven SOA, which intrinsically combines the proven SOA's request-response and the EDA's event publish-subscribe paradigms, is acquiring a lot of attention and attraction these days. That is, in order to fulfil the newly incorporated requirements, there is a need for such a composite pattern. This is being touted as the new-generation SOA (alternatively touted as SOA 2.0). It is based on the asynchronous message-driven communication model to propagate information across all sorts of enterprise-grade applications throughout an enterprise. Services are activated by differently sourced events and the resulting event messages pass through the right services to accomplish the predestined business operation. Precisely speaking, the participating and contributing services are fully decoupled and joined through event messages. All kinds of dependencies get simply eliminated in this new model.
Applications are being designed, developed, and deployed in such a way to be extremely yet elegantly sensitive and responsive. With enterprise applications and big data mandating the distributed computing model, undoubtedly the event-driven SOA pattern is the way forward. The goals of dynamism, autonomy, and real-time interactions can be achieved through this new pattern. This new event-driven SOA pattern allows system architects and designers to process both event messages and service requests (RPC/RMI). This enables a closer affinity and association between business needs and the respective IT solutions. This invariably results in business agility, adaptivity, autonomy, and affordability.
The following diagram illustrates the traditional request-and-response SOA style. The SOA pattern generally prescribes the synchronous and pull-based approach:
The following diagram depicts the message-oriented, event-driven, asynchronous, and non-blocking process architecture:
In an asynchronous push-based messaging pattern, the EDA model builds on the pub/sub model to push a variety of real-time notifications and alerts out to the subscribed listeners in a fire-and-forget fashion. This neither blocks nor waits for a synchronous response. Also, this is a unidirectional and asynchronous pattern.
- Autonomous messages: Events are communicated in the form of autonomous/self-defined messages. That is, each message contains just enough details to represent a unit of work and this provides the decision-enablement capability for notification receivers. Event messages should not require any additional context. Also, they should not require any kind of dependencies on the in-memory session state of the connected applications. The event message is simply intended to communicate the business state transitions of each application, domain, or workgroup within an enterprise.
- Decoupled and distributed systems: As mentioned, the EDA pattern logically decouples connected systems. SOA guarantees loose and light coupling. That is, participating applications need not be available online all the time to accomplish the business tasks. The middleware (ESB) does take care in unobtrusively delivering the messages to the target application. The issue here is that the sender system has to know the relevant details of the target application towards service invocation to process completion.
In the synchronous SOA case, connected and dependent systems are often required to meet the various non-functional requirements/quality of service (QoS) attributes, such as scalability, availability, performance, and so on. But in the case of asynchronous EDA, the transaction load of one system does not need to influence or depend on the service levels of downstream systems. This decoupling-enabled autonomy allows application architects to be a bit carefree in designing their respective physical architectures and capacity planning. Decoupled systems can be deployed independently and are horizontally scalable, as there are no dependencies among the participating modules.
- Receiver-driven flow control: The EDA pattern shifts much of the responsibility of control-flow away from the event source (or sender system) and distributes/delegates it to event receivers. The EDA-centric connected systems have more autonomy in deciding whether to propagate the events further or not. The knowledge used to support these decisions is distributed into discrete steps or stages throughout the architecture and is encapsulated where the ownerships reside. The following diagram is the grandiose mix of both the SOA and EDA patterns:
A monolithic application puts all of its functionality into a single process. For scaling, it is mandatory to replicate the whole application. However, the partitioning of an application into a collection of dynamic application services facilitates the choice and the replication of one or more application components/services for scaling. Thus, the technique of divide and conquer is still doing a great job for the increasingly complicated world of software engineering. This section illustrates the other benefits of SOA and EDA patterns combined.
- Effective data integration: In the synchronous request-driven architecture, the focus is on reusing remotely held functions and implementing process-oriented integration. That means the data integration, which is the important aspect of integrated environments, is innately not supported in the SOA environments. But in the case of EDA, the data integration is intrinsically accomplished as the event data/message is the base unit of communication and collaboration.
- Timeliness and trustworthiness: Events are propagated across all the participating applications in real-time for real-time data capture, processing, decision making, and actuation. The timely exchange of event data/messages enables operational systems to have the most accurate and recent view of the business state/situation. The decisions being arrived based on the precise and perfect data are going to be correct and informed.
- Improved scalability and sustainability: It is a fact that asynchronous systems tend to be more scalable when compared with synchronous systems. Individual processes block less and have less dependency on remote/distributed processes. Furthermore, the intermediaries (message queues and brokers) can be made more stateless, thus reducing the overall complexity of distributed systems. Less dependency ultimately results in them being highly scalable, reliable/dependable, resilient, responsive, and manageable. Any kind of replacement, substitution, and advancements can be easily performed in decoupled systems.
Thus, the beneficial combination of SOA and EDA patterns are capable of producing real-time, adaptive, and extensible enterprises. This hybrid pattern is all set to result in innumerable innovations, disruptions, and transformations.
We have discussed the unique contributions of the SOA pattern towards establishing and sustaining service-enabled environments and enterprises. The service-oriented architectural pattern has evolved over decades to express and expose any legacy, monolithic, and massive application as a dynamic collection of interdependent services. Services are blessed with interfaces and implementations. Interfaces are the contacting and contracting mechanism for any service-enabled application. There are standards, languages, models, frameworks, platforms, patterns, and a bevy of toolkits to shepherd the service paradigm towards its logical conclusion. Though SOA has done a lot of things for the IT enterprise, there are some issues, drawbacks, and limitations.
The popular SOA style majorly relies on a shared data model with multiple hierarchies. The sharing of databases in SOA tends to create a kind of tight data coupling between services and other system components. This tight coupling comes in the way of bringing forth desired changes in the database. That is, if a few RESTful services are tightly coupled with a backend database and if there is any change mandated and enacted on the database schema, then there is a need for a retesting of services to verify and validate how the services work on the altered schema. This dependency is a bit troublesome for the increasingly automated and dynamic world.
The other challenge is that the services in the SOA style are typically coarse-grained and hence the aspect of reusability is quite a tough affair. Most of the application components of legacy applications are fitted with service-oriented interfaces for enabling discovery, integration, and interactions. At the infrastructure level, there is no dependency problem because these service-enabled application components can literally run anywhere. There is no restriction for co-location. SOA uses a kind of tiered organizational architecture that contains a centralized messaging middleware for service invocation, intermediation, and coordination. But the prickling issue here is that application components need to know the corresponding details of one another in order to initiate and bring the required collaboration, corroboration, and correlation to closure. There are other challenges being associated with the highly matured and stabilized SOA paradigm.
Let us move over to the MSA pattern, which is growing by leaps and bounds. Microservices architecture is the new architectural pattern for defining, designing, developing, deploying, and delivering distributed and enterprise-grade software applications. This fast-emerging and evolving pattern is being positioned as the one for easily and quickly achieving the non-functional requirements such as scalability, availability, and reliability for any software application.
The MSA pattern is for producing fine-grained, loosely coupled, horizontally scalable, independently deployable, interoperable, publicly discoverable, network-accessible, easily manageable, and composable services, which is not only the optimized unit of software construction, but also allows enabling quicker software deployment and delivery. In the past, software companies assembled large teams of engineers to build applications which, over a period of time, became monolithic and unwieldy. Legacy applications are close, bulky, tough to maintain, inflexible, and not modern. We need applications that are adaptive, dynamic, open, easy to modify and enhance, and so on. Having understood the distinct contributions of the MSA pattern, today corporates across the globe are keenly strategizing and planning to embrace this new pattern with clarity and confidence. As a result, enterprise-class cloud, mobile, and embedded applications are being built using the powerful and pioneering MSA pattern.
The growing ecosystem of tools, engines, platforms, and other software infrastructure solutions speeds up the process of producing microservices-centric applications. With the tools-assisted orchestration, microservices are being deftly orchestrated to bring forth versatile applications for business automation, acceleration, and augmentation.
Microservices are built upon a concept known as a bounded context, which leads to a self-contained association between a single service and its data. There is no technology or vendor lock-in as far as the MSA-inspired applications are concerned. Every microservice is being empowered with its own data source, which can be a filesystem, SQL, NoSQL, NewSQL, in-memory cache, and so on. There are API gateway solutions to streamline the end-to-end lifecycle management of microservices. Microservices rely solely on inter-service communication. Each microservice calls another microservice as required to complete its function. Furthermore, called microservices may call other services as needed in a process known as service chaining. Microservices use a non-coordinating API layer over the services composing an application. As Docker containers are emerging as the most appropriate runtime for microservices, the MSA pattern is seeing a lot of traction these days.
Each microservice is being designed as an atomic and self-sufficient piece of software. Implementing an application will often require composing multiple calls to these single responsibility and distributed endpoints. Although synchronous request-response calls are required when the requester expects an immediate response, the integration patterns based on eventing and asynchronous messaging provide maximum scalability and resiliency. The microservices approach is well-aligned to a typical big data deployment.
We can gain the required modularity, extensive parallelism, and cost-effective scaling by deploying services across many commodity hardware servers. Microservices modularity facilitates independent updates/deployments and helps to avoid single points of failure, which can help prevent large-scale outages.
As mentioned, the microservice architectural style is an approach for developing an application as a suite of discrete yet self-sufficient services built around specific business capabilities. Microservices-centric applications are being enabled to be event-driven. There are a few interesting architectural patterns quickly emerging and evolving. In this section, we will look at the event stream pattern.
Like polyglot and decentralized persistence, the decentralized polyglot messaging method should be a key to achieving the intended success in microservices architectures. This allows different groups of services to be developed in their own cadence. Furthermore, it minimizes the need for highly coordinated and risky big-bang releases. The microservices approach gives more flexibility to developers for choosing the optimal messaging middleware solution. Every use case will have its own specific needs mandating different messaging technologies such as Apache Kafka, RabbitMQ, or even event-driven NoSQL data grids, such as Apache Geode / Pivotal GemFire.
In the MSA approach, a common architecture pattern is event sourcing using an append-only event stream such as Kafka or MapR Streams, which implements Kafka. With MapR Streams, events are grouped into logical collections of events called topics. Topics are partitioned for parallel processing. We can think of a partitioned topic as a queue. Events are delivered in the order they are received. Unlike a queue, events are persisted, even after they are delivered they remain on the partition, available to other consumers. Older messages are automatically deleted based on the stream's time-to-live setting; if the setting is zero, then they will never be deleted. Messages are not deleted from topics when read, and topics can have multiple different consumers. This allows processing of the same messages by different consumers for different purposes.
Pipelining is also possible where a consumer enriches an event and publishes it to another topic:
With the faster proliferation, penetration, and participation of the microservices architecture, there will be fresh patterns to address its growing and prevailing issues and limitations. Furthermore, existing patterns can be seamlessly combined to come out with bigger and better patterns for enabling the realization of microservices-centric applications. We have detailed the architecture and design patterns in Chapter 9, Microservices Architecture Patterns.
Typically, enterprise applications are being blessed with a backend database management system. These enterprise-scale applications function well as long as the database is able to keep up with the load. But when usage peaks and the database can't keep up with the constant challenge of writing a log of the transactions, the application is bound to fail. In any high-volume application with an extremely large concurrent user load, the database will usually be the final limiting factor in how many transactions we can process concurrently. While various caching technologies and database scaling products help to address these issues, it is still a pipe dream that scaling out a normal application for extreme loads is a very difficult proposition.
The space-based architecture is designed with the aim of empowering software systems to work even under the heavy load of users. This is being achieved by splitting up both the processing and the storage between multiple servers. The data is spread out across many nodes. The space-based architecture pattern is widely used to address and solve scalability and concurrency issues. Customer-facing applications are quite unpredictable and this specialized architecture is competent and cognitive enough to support a large number of users.
High scalability is achieved by removing the central database constraint and using replicated in-memory data grids instead. Application data is kept in-memory and replicated among all the active processing units. Processing units can be dynamically started up and shut down as the user load increases and decreases, thereby addressing variable scalability. Because there is no central database, the database bottleneck is removed, providing near-infinite scalability within the application. Most applications that fit into this pattern are standard websites that receive a request from a browser and perform some sort of action. A bidding auction site is a good example of this. The site continually receives bids from internet users through a browser request. The application would receive a bid for a particular item, record that bid with a timestamp, update the latest bid information for the item, and send the information back to the browser.
Precisely speaking, the SBA style is primarily for ensuring the goals of higher concurrency. Next-generation applications have to be scalable, available, and dependable. The SBA pattern is a great enabler.
As we all know, it is becoming a software-defined world. Everything is being stuffed with software to exhibit adaptive behavior. Thus, the buzzwords such as software-defined networking, storage, compute, security, and environment are acquiring significance these days. Also, the concept of software-defined everything is becoming prominent and paramount. The role and responsibility of software, therefore, is increasing. Along with the faster proliferation, penetration, and participation, the software product is also becoming a complicated task. Software experts and exponents are recommending the combination of multiple architecture patterns that we have discussed previously in order to soften and speed up the realization of next-generation software solutions and services. We have described how SOA and EDA team up to put a stimulating foundation for producing dynamic and adaptive applications. Similarly, other architectural patterns can be synchronized in order to bring forth composite patterns in order to produce competent and versatile applications.
A context-aware event-driven service-oriented architecture (ALFONSO GARCÍA DE PRADO and his team).
Context awareness has become a fundamental requirement for realizing people-centric IT applications. People leave their homes expecting the lights, which they left on, to be turned off automatically. The mobile phone can warn people if there is traffic congestion on the way to the office. Windscreen wipers automatically turn on if it is raining. If there is any possibility of a fire at home, hotel, or hospital, then sensors have to integrate with one another to do data fusion in time to facilitate any fire, flame, and fall detection. Our current IT services and systems are not typically context-aware. Every common, casual, and cheap item has to be self-, surroundings, and situation-aware in order to be unique in their decisions, deals, and deeds. Software packages, libraries, and suites have to be event-driven in order to be sensitive and responsive. They have to be designed and deployed in such a way that they are receptive to any kind of noteworthy events and to answer accordingly. We have discussed service-oriented and event-driven architectures. They need to be blended with the new-generation technologies, such as the IoT, in order to be right and relevant for the increasingly connected world.
The connected things, sensors, actuators, robots, drones, beacons, machines, equipment, instruments, wares, utensils, and other devices are to empower software services to be context-aware. There are IoT data analytics platforms, the growing array of different and distributed event sources, the faster maturity and stability of event processors, streaming analytics engines, scores of connectors, drivers and adapters, knowledge visualization dashboards, and other enabling frameworks, patterns, processes, practices, and products aimed towards producing context-aware applications across industry verticals.
The authors have devised with a high-level context-aware event-driven service-oriented architecture:
With the dawning of the game-changing IoT era, application and data architectures are set to be synchronized to create versatile architectures for various use cases.
With the setting up of IoT environments across our personal, professional, and social environments to fulfill the dreams of connected and smarter environments, the amount of multi-structured data getting generated, collected, cleansed, and crunched is growing exponentially. With the arrival of a dazzling array of specific and generic, disappearing, disposable yet indispensable, slim and sleek, handy and trendy, embedded yet networked devices, the projected big data era has set in. This has opened up fresh possibilities and opportunities for businesses as well as IT service/solution providers.
With our manufacturing floors, retail stores, warehouses, airports, railway stations, and bus bays, multi-specialty clinics, shopping complexes, malls and hypermarkets, auditoriums and stadiums, entertainment centers and cinema theaters, food joints, and so on, slowly yet steadily tending towards being smarter through the application of pioneering edge technologies, the long-awaited context-aware computing is becoming a reality. That is, software applications have to be context-aware to be adaptive, accommodating, and adjustable.
Considering the trends and transitions happening in the IT space, a few researchers (David Corral-Plaza and his team) have envisioned a holistic event-driven service-oriented architecture (ED-SOA) and it has the following important factors:
- Data producers should gather data from several sources (databases, IoT sensors, social networks, and so on) and send them to the data collector.
- The data collector follows the necessary transformations so that the information received can be used in the following phases of their solution. It is an intermediate layer that performs a process of homogenization since information will most probably be received in different formats and structures in most scenarios.
- Data processing should provide the complex event processing (CEP), context-awareness, and prediction module.
- Data consumers, which can be databases, end users, or additional endpoints, pave the way for the collaborative architecture. Such data consumers communicate with the previous module through a REST interface:
We are heading towards the world of real-time applications and enterprises. Real-time data capture, processing, knowledge discovery and dissemination, decision making, and actuation turn out to be the new normal. IT systems are being enabled to be real-time. The preceding architecture spells out the ways and means of achieving real-time predictions.
We have detailed the prominent and dominant software architecture patterns and how they are distinctly foundational and fundamental for producing and running any kind of enterprise-class and production-grade software applications. To accommodate the evolution and revolutions and to surmount the complications due to constant changes happening in the business and technology spaces, the smart leverage of a variety of software patterns is being recommended as the way forward. Increasingly, to design and develop sophisticated and smarter applications, various architectural patterns are being meticulously chosen and cognitively clubbed together to produce composite patterns.
Besides this, there are several application and domain-specific architectures being formed, verified, and validated by worldwide researchers and presented as research contributions. Thus, the domain of architectural patterns is consistently on the growth in order to support and sustain the software engineering field. The forthcoming chapters will go deep and dig further to bring forth a lot of useful and usable details on design patterns for existing and fresh technologies. We have covered the emerging and evolving technologies such as Docker-enabled containerization, microservices architecture (MSA), big data analytics, reactive programming, high-performance computing (HPC), and so on.
To learn more, you can refer to the following reading resources:
- .NET microservices architecture for containerized .NET applications: https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/
- SOA patterns: http://www.soapatterns.org/
- Using events in highly distributed architectures: https://msdn.microsoft.com/en-us/library/dd129913.aspx
- All about microservices and the design patterns: http://microservices.io/index.html
- Event-driven architecture pattern: https://towardsdatascience.com/event-driven-architecture-pattern-b54fc50276cd
- Common software architectural patterns in a nutshell: https://towardsdatascience.com/10-common-software-architectural-patterns-in-a-nutshell-a0b47a1e9013