In this article, we will discuss the Service-Oriented Architecture in .NET. We will cover what it is, how to implement it, how it differs from Microservice Architecture, and how we can use cloud computing platforms as a jump pad to quickly stand up services and infrastructural features.

To download the source code for this article, you can visit our GitHub repository.

Let’s begin!

What Is Service-Oriented Architecture

Service-Oriented Architecture (SOA) defines an architectural pattern for structuring projects in which small, logical groupings of features exist as individual applications or services. We can deploy, scale, and iterate on each service independently without disrupting the entire system. Ideally, a service should manage its data storage, and, in most cases, other services should not alter that data.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

Let’s take a look at an example of running a movie theater business and how we might logically separate each component of the business into its service. We can have a front-end service that runs the theater website so that guests can find movie times and purchase movie tickets. Logically, we will need a ticketing service that will keep track of showtimes and how many tickets are available, even which specific seats are available. Next, we require a purchasing service that will complete sales transactions for movie tickets and even concessions transactions. Finally, we can see the need for an inventory service that will track concessions and alert administrators when it’s time to make new purchase orders for specific items.

Let’s take a look at a VS solution that includes all of these services:

Service oriented architecture solution

In this solution, we create a separate project for each service we discussed. The front-end services are Blazor projects, while the other services are .NET Core Web APIs. Note that these are not requirements. Services can be built with other technology stacks as long as they are able to communicate with each other. In this case, we use HTTP.

Service-Oriented Architecture Principles

Service-Oriented architecture prioritizes four principles in defining how we should implement solutions. These principles are Interoperability, Loose coupling, Abstraction, and Granularity (also called Single Responsibility). Let’s discuss each individually and how they direct us to develop our services.

Interoperability

First, “Interoperability” means that each service that needs to interact with other services can do so easily, efficiently, and most importantly without violating any of the other principles. We can implement communication by creating services that talk with each other via HTTP protocols, which is typically the communication option of choice for microservices.

In SOA we can have other distributed forms of communication like Advanced Messaging Queuing Protocol (AMQP), which is a messaging protocol that handles operations like message queueing, and routing in a point-to-point or publish-subscribe paradigm. Even a more centralized form of messaging like Enterprise Service Bus (ESB), in addition to the features AMQP has, can also handle operations like data model transformation, and message orchestration. Interoperable services also enable writing services in different languages, as long as they can still communicate.

Loose Coupling

Second, Loose Coupling is a principle that is concerned with keeping services independent of each other as much as possible, ideally completely standalone. As a result of following this principle, services should not share stores of data. Services should not be so tightly coupled that iterating on one service disrupts another. Additionally, services should be stateless which means that they do not require any information from past operations to continue to do their job.

Abstraction

Next, abstraction refers to the fact that clients of the service or service users should not need to know the specific logic a service is executing to be able to use it. Documentation and the implementation of a service’s interface support this. For example, if a service adheres to the interface expected by other services even after an update, there should be no disruption.

Let’s see how we implement Abstraction in the movie theater project:

"Abstraction" sample implementation in service oriented architecture

Here we adhere to abstraction by defining what the InventoryService can do without showing how the service performs these actions. Note that the InventoryService offers both a client class and also public contracts for requests and responses:

public interface IInventoryServiceClient
{
    Task<GetInventoryCountByIdResponse> GetInventoryItemById(GetInventoryCountByIdRequest request);
    Task<bool> AddItemToInventory(InventoryItem item);
    Task<bool> DeleteItemFromInventory(string itemId);
    Task<bool> ReduceItemInventoryCount(string itemId, int amountToReduce);
    Task<bool> IncreaseItemInventoryCount(string itemId, int amountToIncrease);
}

Looking at IInventoryServiceClient we can see that we can add and remove items from the inventory using AddItemToInventory() and DeleteItemFromInventory() methods respectively. Additionally, item counts can be increased and decreased in cases such as sales or returns. Lastly, we can query the inventory by item id using GetInventoryItemById().

Granularity

Lastly, Granularity means that services should be as small as possible encompassing a small set of related features. We also call this principle Single Responsibility. Distributing a single feature set among several services to create small code bases, however, would be an anti-pattern. Simply stated, a service should be responsible for one business function or feature set.

In our movie theater example, we can see this principle in action. We could have one monolithic API project to handle all of our needs. Instead, we opt for smaller services that have a single purpose. Even as far as to separate easily coupled services such as inventory and purchasing.

Service-Oriented Architecture Implementation

We can break down the implementation of SOA services into three components: an interface, a contract, and implementation.

A service defines its feature set through interfaces. Of course, we define both of these in code and documentation. A service should always adhere to its interface because it will be the basis of how other services interact with it.

A service contract defines how a service consumer and the service will interact. Let’s take a look at an example of a service contract:

public sealed class GetInventoryCountByIdRequest
{
    public string ItemId { get; set; } = "";
}

public sealed class GetInventoryCountByIdResponse
{
    public string Id { get; set; } = "";
    public string Name { get; set; } = "";
    public int Count { get; set; } = 0;
    public string UnitOfMeasurement { get; set; } = "";
}

Here, we define the contract a consumer will use to interact with our service. If a consumer submits a GetInventoryCountByIdRequest it can expect to receive a GetInventoryCountByIdResponse.

Lastly, a service implementation is the actual logic a service will perform to execute its interface.

Pros and Cons of Service-Oriented Architecture

Service-Oriented Architecture has many good aspects such as speed of development, efficient maintenance, and resilient, adaptable solutions. An SOA solution is also strong in terms of speed, although the speed of the application can be negatively impacted as the solution becomes larger and more intertwined. This is less of a concern with a Microservice Architecture which is a subset of SOA. We will discuss more about Microservices and how they compare to SOA in general in the following section.

While Service-Oriented Architecture is a great, clean pattern to follow for projects, it is not always the right choice. SOA’s scalability can be impacted as more services use the messaging bus due to its centralized nature. Additionally, because SOA allows for shared data storage when necessary, when a solution becomes more coupled around shared data we can start to see performance issues. This is a result of a solution becoming more tightly coupled.

Differences Between SOA and Microservices Architecture

While Microservice Architecture is a specific implementation of Service-Oriented Architecture, it exhibits some key differences. Microservice Architecture is more strict in how loosely coupled services should be. Proponents of Microservice Architecture argue that it implements SOA correctly.

One major difference between Microservice Architecture and SOA is that an ESB would not fit into the implementation of a project. This is because ESB would represent a centralized form of communication that services are coupled to. Additionally, a Microservice Architecture would favor data duplication where each service owns a copy of data. Data duplication is preferred over a shared data source. Microservices, being more loosely coupled, can scale more easily when needed.

Cloud Computing Role in Service-Oriented Architecture

Cloud computing platforms like Azure or AWS can be an amazing complement to setting up an SOA solution.

Primarily, these cloud platforms offer serverless hosting of a service. This is helpful because there is no need to consider which server type we might need or any maintenance required such as patching. We can change these serverless offerings to provide more computing power on demand.

Additionally, there are offerings for many other components a solution will need. Components such as a variety of data storage options, and messaging queues as we have discussed. Additionally, they offer service health monitoring, security, logging, and API load balancing.

Conclusion

In this article, we discussed Service-Oriented Architecture and its core principles. We took a detailed look at how we can implement this pattern in our solutions. Moreover, we learned that Microservice Architecture is a subset of SOA and we drew a comparison between the two. Additionally, we discussed when SOA is a good choice in pattern for a solution and when it falls short. Lastly, we examined the role cloud computing platforms like Azure or AWS can play in implementing Service-Oriented Architecture. Until next time!  

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!