Lecture
Service-oriented architecture (SOA) — a modular approach to software development based on the use of distributed, loosely coupled[en] replaceable components equipped with standardized interfaces for interacting over standardized protocols.
SOA and DDD are two concepts that work great together. DDD is a way to develop a unit of deployment (a single application). SOA is a way to glue together several units of deployment.
SOA:
an architectural style promoting the concept of business-oriented enterprise services as the fundamental unit of designing, building, and composing enterprise business solutions
DDD is:
a way of thinking and a set of priorities aimed at accelerating software projects that deal with complex domains
DDD is intended to build domain logic within the shared services of SOA
"A service is a visible resource that performs a repeatable task and is described by an external instruction."
The Open Group Architecture Forum (TOGAF) offers two definitions of architecture depending on the context:
DDD vs SOA: what are the differences and what do they have in common?
Microservices are components, SOA is an architecture, so we cannot compare them. Microservices Architecture is primarily a concept at the application scale. Some of the recommendations may work at the enterprise level, but I think it depends on the scale and shape of the enterprise.
Service-oriented architecture is a concept that is more commonly used in enterprise architectures. This architecture is mainly used for better integration between numerous applications within an enterprise. Having said that, we can obviously apply some principles of microservices architecture within SOA to guide us in designing components in more modern ways. We just have to be aware that some of the principles of the two architectures are opposites, as I mentioned above.

Software systems developed in accordance with service-oriented architecture are usually implemented as a set of web services that interact over the SOAP protocol, but other implementations also exist (for example, based on Jini, CORBA, or REST).
Component interfaces in service-oriented architecture encapsulate implementation details (the operating system, platform, programming language) from the rest of the components, thereby enabling the combination and reuse of components to build complex distributed software systems, ensuring independence from the platforms and development tools used, and facilitating the scalability and manageability of the systems being built.
It became widespread in the late 1990s and early 2000s. Since the mid-2010s, microservices architecture has gained popularity — a variant of service-oriented architecture aimed at using modules that are as minimally coupled as possible.
The key ideas behind the concept of a service:
Taken together, these characteristics show that SOA deals not only with "technologies" but also with the needs and requirements of the business.
There are no industry standards regarding the exact composition of service-oriented architecture, although many industry sources have published their own principles. Some of these include the following:
Standard service contract
Services adhere to a standard communication agreement, as defined collectively by one or more service description documents within a given set of services.
Service reference autonomy (an aspect of loose coupling)
The relationship between services is minimized to the level where they are aware only of each other's existence.
Service location transparency (an aspect of loose coupling)
Services can be called from anywhere in the network, wherever they are located.
Service longevity
Services must be long-lived. Wherever possible, services should avoid forcing consumers to change unless they require new functionality. If you call a service today, you should be able to call the same service tomorrow.
Service abstraction
Services act as black boxes, meaning their internal logic is hidden from consumers.
Service autonomy
Services are independent and control the functionality they encapsulate both during development and at runtime.
Service statelessness
Services are stateless, meaning they either return the requested value or throw an exception, which minimizes resource usage.
Service granularity
The principle of ensuring an adequate size and scope of services. The functionality provided by a service to a consumer must be relevant.
Service normalization
Services are decomposed or consolidated (normalized) to minimize redundancy. In some cases this is not possible. These are the cases where performance optimization, access, and aggregation are required. [15]
Service composability
Services can be used to create other services.
Service discoverability
Services are supplemented with communicative metadata by which they can be effectively discovered and interpreted.
Service reusability
Logic is divided into distinct services to promote code reuse.
Service encapsulation
Many services that were not originally planned within SOA can be encapsulated or become part of SOA.
The architecture is not tied to any particular technology. It can be implemented using a wide range of technologies, including technologies such as REST, RPC, DCOM, CORBA, or web services. SOA can be implemented using one of these protocols and, for example, may additionally use a file system mechanism to exchange data.
The main thing that distinguishes SOA is the use of independent services with clearly defined interfaces, which can be called in some standard way to perform their tasks, provided that the services know nothing in advance about the application that will call them, and the application does not know how the services perform their task.

Elements of service-oriented architecture, from: Dirk Krafzig, Karl Banke, and Dirk Slama. Enterprise SOA. Prentice Hall, 2005
SOA can also be viewed as an architectural style for information systems that makes it possible to build applications composed of loosely coupled, interacting services. These services interact based on some strictly defined platform-independent and language-independent interface (for example, WSDL). The interface definition hides the language-dependent implementation of the service.
Thus, systems based on SOA can be independent of development technologies and platforms (such as Java, .NET, etc.). For example, services written in C# running on the .Net platform and services written in Java running on the Java EE platform can be called with equal success by a common composite application. Applications running on some platforms can call services running on other platforms, which facilitates component reuse.
SOA can support the integration and consolidation of operations within complex systems; however, SOA does not define or provide methodologies or frameworks for documenting services.
High-level languages such as BPEL, or specifications such as WS-CDL and WS-Coordination, extend the concept of a service by providing an orchestration method for combining fine-grained services into larger business services, which in turn can be incorporated into technological processes and business processes implemented as composite applications or portals.
The use of Service Component Architecture (SCA) to implement SOA is an area of ongoing research.

Figure 1. Reference model of SOA foundation
SOA has been conflated with web services; [30] however, web services are only one option for implementing the patterns that make up the SOA style. In the absence of native or binary forms of remote procedure call (RPC), applications may run slower and require more computing power, which increases costs. Most implementations incur this overhead, but SOA can be implemented using technologies (for example, Java Business Integration (JBI), Windows Communication Foundation (WCF), and Data Distribution Service (DDS)) that do not depend on remote procedure calls or translation via XML or JSON. At the same time, emerging open-source XML parsing technologies (such as VTD-XML) and various XML-compatible binary formats promise to significantly improve SOA performance. [31] [32] [33]
Stateful services require that both the consumer and the provider share the same consumer-specific context, which is either included in or referenced by the messages exchanged between the provider and the consumer. This constraint has the drawback that it can reduce the overall scalability of the service provider if the provider needs to retain a shared context for each consumer. It also increases the coupling between the service provider and the consumer and makes it harder to switch between service providers. [34] Ultimately, some critics believe that SOA services are still too constrained by the applications they represent. [35]
The main problem faced by service-oriented architecture is metadata management. SOA-based environments include many services that interact with each other to perform tasks. Because a design can involve multiple services working together, an application can generate millions of messages. Additional services may belong to different organizations or even competing firms, which creates an enormous problem of trust. Thus, SOA governance comes into the picture. [36]
Another serious problem faced by SOA is the lack of a unified testing environment. There are no tools that provide the functionality needed to test these services in a service-oriented architecture. The main reasons for the difficulties are: [37]
Service-oriented architecture can be implemented using web services or microservices. [21] This is done so that functional building blocks are accessible over standard Internet protocols that are independent of platforms and programming languages. These services can represent either new applications or simply wrappers around existing legacy systems to make them available for use over the network. [22]
Developers typically build SOA using web service standards. One example is the SOAP protocol, which gained wide industry acceptance after the version 1.2 recommendation from the W3C consortium [23] (World Wide Web Consortium) in 2003. These standards (also called web service specifications) also provide greater interoperability and some protection against lock-in to proprietary vendor software. However, one can also implement SOA using any other service technology, such as Jini, CORBA, REST, or gRPC.
Architectures can operate independently of specific technologies and therefore can be implemented using a wide range of technologies, including:
Service-oriented architecture (SOA) was conceived in the late 1980s. It originates in the ideas set out in CORBA, DCOM, DCE, and other documents. Much has been written about SOA, and there are several of its implementations. But in essence, SOA can be boiled down to a few ideas, and the architecture does not dictate how they are implemented:
SOA is a set of architectural principles independent of technologies and products, just like polymorphism or encapsulation.
Let's consider the following patterns related to SOA:
In the 1980s, the active use of corporate networks and client-server architecture began. A need arose for a standard way for applications to interact when they were built using different technologies, ran on different computers, and under different operating systems. CORBA was developed for this purpose. It is one of the standards for distributed computing, which emerged in the 1980s and flourished by 1991.
The CORBA standard was implemented by several vendors. It provides:
Today CORBA is still used for heterogeneous computing. For example, it is still part of Java EE, although starting with Java 9 it will be shipped as a separate module.
I want to note that I do not consider CORBA a pattern of SOA (although I place both CORBA and SOA patterns in the field of distributed computing). I discuss it here because I consider the shortcomings of CORBA to be one of the reasons SOA emerged.
First, we need to obtain an Object Request Broker (ORB) that conforms to the CORBA specification. It is provided by a vendor and uses language mappers to generate «stubs» and «skeletons» in the languages of the client code. Using this ORB and the interface definitions that use IDL (an analog of WSDL), you can generate remotely callable stub classes in the client based on real classes. And on the server, you can generate skeleton classes that handle incoming requests and call the real target objects.

The caller invokes a local procedure implemented by the stub.
Although today a use can still be found for CORBA, we know that it was necessary to reduce the number of remote calls in order to improve system performance. A reliable communication channel and a simpler messaging specification were also required.
And to solve these problems, web services began to appear in the late 1990s.
[Web] services can be published, discovered, and used in a standard way regardless of technologies.
— Microsoft 2004, Understanding Service-Oriented Architecture

Thanks to microservices, within the SOA paradigm we moved from remote method calls on objects (CORBA) to message passing between services.
But it is important to understand that within SOA, web services are not simply general-purpose APIs that merely provide CRUD access to a database over HTTP. In some cases this implementation can be useful, but for the integrity of your data it is necessary that users understand the model underlying the implementation and follow the business rules. SOA implies that web services are bounded contexts of business sub-domains and separates the implementation from the tasks solved by the web services.
From a technology standpoint, SOA is not just a service architecture, but a set of policies, practices, and frameworks through which we provide and obtain the services we need.
— Microsoft 2004, Understanding Service-Oriented Architecture
We have several applications that communicate asynchronously with each other using platform-independent messages. A message queue improves scalability and strengthens the isolation of applications. They do not need to know where the other applications are, how many there are, or even what they are. However, all these applications must use a single messaging language, i.e., a predefined text format for representing data.
A message queue uses a software message broker (RabbitMQ, Beanstalkd, Kafka, etc.) as an infrastructure component. To implement communication between applications, the queue can be configured in various ways:
Request/Response

All these patterns can be classified as either a pull (polling) approach or a push approach:
The Enterprise Service Bus was already using web services back in the 1990s, when they were just developing (perhaps some implementations initially used CORBA?).
The ESB arose at a time when companies had separate applications. For example, one for handling finances, another for personnel accounting, a third for warehouse management, and so on, and they needed to be linked and integrated with each other somehow. But all these applications were created without integration in mind, and there was no standard language for applications to interact (as there still isn't today). Therefore, application developers provided endpoints for sending and receiving data in a certain format. Client companies then integrated the applications, establishing communication channels between them and converting messages from one application's language into another.
A message queue can simplify the interaction of applications, but it is not able to solve the problem of different language formats. Nonetheless, an attempt was made to turn the message queue from a simple communication channel into an intermediary that delivers messages and converts them into the required formats/languages. The ESB became the next step in the natural evolution of a simple message queue.

This architecture uses a composite application, usually user-oriented, that communicates with web services to perform some operations. In turn, these web services can also communicate with other web services, subsequently returning some data to the application. But neither the application nor the backend services know anything about each other, including their location and communication protocols. They know only which service they want to communicate with and where the service bus is located.
The client (a service or a composite application) sends a request to the service bus, which converts the message into a format supported at the destination and forwards the request there. All interaction goes through the service bus, so if it goes down, all the other systems go down with it. That is, the ESB is a key intermediary, a very complex component of the system.
This is a very simplified description of the ESB architecture. Moreover, although the ESB is the main component of the architecture, other components may also be used in the system, such as Domain Brokers, Data Services, Process Orchestration Services, and Rules Engines. The same pattern can be used by a federated design: the system is divided into business domains with their own ESBs, and all the ESBs are connected to each other. Such a scheme has higher performance and no single point of failure: if some ESB goes down, only its business domain suffers.

The main responsibilities of the ESB:
As we have built structures for communication between different processes, we have seen many products and approaches that use very sophisticated communication mechanisms. A good example is enterprise service buses, which often include sophisticated facilities for message routing, choreography, transformation, and applying business rules.
— Martin Fowler 2014, Microservices
This architectural pattern has its positive sides. However, I consider it especially useful in cases where we do not «own» the web services and need an intermediary to translate messages between services, to orchestrate business processes that use several web services, and for other tasks.
I also recommend keeping in mind that ESB implementations are already quite advanced and in most cases allow the use of a user interface with drag & drop support for their configuration.
Microservices architecture is based on the concepts of SOA. Its purpose is the same as that of the ESB: to create a single shared enterprise application from several specialized business-domain applications.
The main difference between microservices and the bus is that the ESB was created in the context of integrating separate applications to produce a single enterprise distributed application. Whereas microservices architecture was created in the context of rapidly and constantly changing businesses that (mostly) build their own cloud applications from scratch.
That is, in the case of the ESB, we already had applications that we did not «own», and therefore we could not change them. But in the case of microservices, we fully control the applications (while third-party web services may also be used in the system).
The nature of building/designing microservices does not require deep integration. Microservices must correspond to a business concept, a bounded context. They must maintain their own state, be independent of other microservices, and therefore they need less integration. That is, low interdependence and high cohesion led to a remarkable side effect — a reduced need for integration.
[Microservices are] small autonomous services that work together and are designed around a business domain.
— Sam Newman 2015, Principles Of Microservices
The main disadvantage of the ESB architecture was a very complex centralized application on which all the other applications depended. But in microservices architecture, this application is almost entirely removed.
Some elements that permeate the entire microservices ecosystem still remain. But they have far fewer tasks compared to the ESB. For example, a message queue is still used for asynchronous communication between microservices, but it is merely a channel for passing messages, nothing more. Or one can recall the microservices ecosystem gateway, through which all external data exchange passes.
Sam Newman, the author of Building Microservices, identifies eight principles of microservices architecture. They are:

The community prefers a different approach: smart endpoints and dumb pipes. The microservices from which applications are assembled should depend on each other as little as possible while being very tightly cohesive — they contain their own domain logic and act more like filters in the classic Unix sense: they receive requests, apply logic, and generate responses. They are orchestrated using simple REST-like protocols rather than complex protocols such as WS-Choreography or BPEL or some centralized tool.
— Martin Fowler 2014, Microservices
microservices architecture is a special case of SOA:

In other words, microservices architecture is just a set of stricter rules and conventions for writing all those same SOA services
Here are the differences between SOA and microservices:
| SOA | Microservices |
|---|---|
| The SOA model has a single data storage layer that is used by all services in the application. | Microservices applications mostly allocate a database or other storage to the services that need it. |
| Communication between the various services in a SOA application uses simple and straightforward approaches. | Microservices use complex APIs. |
| Focused on maximizing the reuse of application services. | We pay more attention to decoupling. |
| A systematic change requires modifying the monolith. | A systematic change will help you create a new service. |
| DevOps and continuous delivery are becoming popular but have not yet become mainstream. | Strong emphasis on DevOps and continuous delivery |
| Monolithic in nature | Full stack in nature |
| Supports multiple messaging protocols. | Uses lightweight protocols such as HTTP, REST, or Thrift API. |
| It is intended for sharing resources between services. | It is intended to host services that can operate independently. |
| Often involves sharing components | As a rule, it does not involve sharing components |
| Assumes the sharing of a data store between services | Each service can have an independent data store. |
| Better for large-scale integration | Better for small and web applications. |
| Exchanges data through an ESB | Communicate through an API layer |
| Relies on resource sharing | Relies on a bounded context for communication. |
| Less flexibility in deployment | Fast and simple deployment. |
| The SOA technology stack is lower compared to Microservice. | The microservices technology stack can be very large. |
| Business units are dependent. | Business units are independent of one another. |
| A SOA application consisting of two or three services. | A Microservices application can have dozens of services. |
| SOA applications are built to perform many business tasks. | They are built to perform a single business task. |
| Deployment is a time-consuming process. | Deployment is uncomplicated and requires less time. |
| Business logic components are stored inside simple wire protocols (HTTP with XML JSON) in a single-service domain. The API is managed using SDKs / client packages. | Business logic can exist across all domains of the enterprise service bus, as separate layers between services. |
| Uses an enterprise service bus (ESB) for communication | It uses a less complex and simple messaging system. |
| The size of the software is larger than that of any ordinary software | The size of the software in microservices is small. |
| Multithreaded with a lot of overhead for handling I/O | Single-threaded, mainly used with Event Loop functions for non-blocking I/O handling. |
| A systematic change is required to modify the monolith. | In microservices, a systematic change consists of creating a new service. |
| Focus on maximizing the reusability of application services. | Emphasis on decoupling. |
| Unified governance and standards. | Relaxed governance, since it is more oriented toward the collaboration of people and freedom of choice. |
| The deployment process takes a lot of time. | Deployment is done easily and requires less time. |
| A less scalable architecture. |
A highly scalable architecture.
|

Ravioli architecture is the name usually given to an antipattern of microservices architecture. You get ravioli if there are too many microservices, they are too small, and they do not reflect domain concepts.
In recent decades, SOA has evolved considerably. Thanks to the inefficiency of earlier solutions and the development of technologies, today we have arrived at microservices architecture.
The evolution followed the classic path: complex problems were broken down into smaller ones that are easy to solve.
The problem of code complexity can be solved in the same way that we break a monolithic application into separate domain components (bounded contexts). But as teams and the codebase grow, the need for independent development, scaling, and deployment increases. SOA helps achieve such independence by strengthening the boundaries of contexts.

I repeat that it all comes down to loose interdependence and high cohesion, and the size of the components should be larger than before. It is necessary to pragmatically assess your needs: use SOA only when it is necessary, since it greatly increases complexity. And if you can actually do without SOA, then it is better to choose microservices of an appropriate size and number, no more and no less.
You have applied the «Database per Service» pattern. Each service has its own database. However, some business transactions span several services, so you need a mechanism for implementing transactions that span services. For example, imagine you are building an e-commerce store in which customers have a credit limit. The application must ensure that a new order does not exceed the customer's credit limit. Since orders and customers reside in different databases belonging to different services, the application cannot simply use a local ACID transaction.
How to implement transactions that span services?
The implementation of each business transaction that spans several services is a saga. A saga is a sequence of local transactions. Each local transaction updates the database and publishes a message or event to trigger the next local transaction in the saga. If a local transaction fails due to a violation of a business rule, then the saga executes a series of compensating transactions that undo the changes made by the preceding local transactions.

There are two ways to coordinate sagas:

An e-commerce application using this approach could create an order using a choreography-based saga that consists of the following steps:
Order Service receives the POST /orders request and creates an Order in the PENDING stateOrder Created eventCustomer Service event handler attempts to reserve creditOrderService event handler either approves or rejects the Order
An e-commerce application using this approach could create an order using an orchestration-based saga that consists of the following steps:
Order Service receives the POST /orders request and creates a Create Order saga OrchestratorOrder in the PENDING stateReserve Credit command to the Customer ServiceCustomer Service attempts to reserve creditOrderThis pattern has the following advantages:
This solution has the following disadvantages:
The following problems also need to be solved:
To be reliable, a service must atomically update its database and publish a message/event. It cannot use the traditional distributed transaction mechanism that spans the database and the message broker. Instead, it must use one of the patterns listed below.
A client that initiates a saga, which is an asynchronous flow, using a synchronous request (for example, HTTP POST /orders), must be able to determine its outcome. There are several options, each of which has its own trade-offs:
OrderApproved or OrderRejected event.orderID) after starting the saga, and the client periodically polls (for example GET /orders/{orderID}) to determine the outcomeorderID) after starting the saga, and then sends an event (for example, a web socket, webhook, etc.) to the client after the saga completes.
Comments