Back Arrow
From the blog

Formalizing API Workflow in .NET Microservices

Let's talk about how to organize the interaction of microservices in a large, long-lived product, both synchronously and asynchronously.

Artyom Chernenko

Lead .NET Developer

We work with IT products in the fields of logistics and e-commerce. Most of these projects are architecturally large, including many services essential for the proper operation of entire systems.

Let's talk about how to organize the interaction of microservices in a large, long-lived product, both synchronously and asynchronously.

The microservice approach involves creating a microservice for each feature within a large product. For example, a microservice that handles a specific function in logistics processes:

  • warehousing (collection/placement/movement of commodity units)
  • sorting of cargo spaces
  • labeling of cargo spaces
  • consolidation of cargo spaces
  • other

Each microservice has its codebase, database, and API for interacting with other services. This allows us to write them in different programming languages and use different technologies. 

All new microservices are written on new versions of frameworks, while all outdated ones are gradually migrated. The goal is to provide the most efficient and standardized approach to ensuring interoperability between microservices.  Creating a new microservice and integrating it into the overall system should be as quick and painless as possible for both its developer and the developers who use this microservice.

Synchronous interaction between microservices

Synchronous interaction occurs when one system sends a message to another and waits for an acknowledgment or response before continuing. This type of interaction is common when the requesting system needs information to proceed with its actions. To organize such interaction, various protocols like (g)RPC, SOAP, and the architectural style REST are widely used.

The diagram shows the approach to developing an API for a new microservice. First, we create an API specification in OpenAPI format and get it approved by the architecture department. Based on this specification, we create a contract library containing API interfaces and data structures. Then, we use this library to create a client's API that will interact with this API. Usually, we use the Refit library for this purpose.

This is what the OpenAPI specification looks like. IDE Rider has a plugin that allows you to edit it, and Swagger generates the specification description. All methods of this API, along with request and response structures, are described here. Once this specification is approved, we begin developing the contract library.

Sync contract library

The Sync contracts library is a NuGet package based on the specification. It contains:

  • Interfaces of each API controller
  • The client interface, which combines all controller interfaces
  • Request/Response models used in controllers

For example, in one of the libraries we have two interfaces: ITaskController and IConsolidationCargoUnitsController. They define all the necessary methods, which will later be implemented by both the corresponding controllers and clients. 

Since we use the Refit library to generate clients, we also define the types of requests and their routes using attributes like [Get(...)], [Post(...)], etc. It's important to note that these are Refit attributes, not ASP.NET attributes. Accordingly, our contract library may not depend on ASP.NET at all.

Next, we describe the client interface, which does not contain its own methods but simply inherits all controller interfaces and is marked it with a special attribute that implements versioning. The implementation of this interface (using the Refit library) will be the client that external systems use to interact with it.

This is what the library of contracts ultimately looks like: one client interface, several controller interfaces, and data models. This is all completely based on the specification. 

Once the development of the contract library is complete, we publish a NuGet package. Once the package is published, we can start implementing the API itself, as well as the client for this API. Since the specification has already been approved and the contract library has been published, API and client development can be done in parallel.

API development

This is what the API controller class looks like, where we implement the controller interface with the necessary business logic. It’s worth noting that here ASP.NET attributes are used, which is one of the disadvantages of this approach - you have to duplicate routes both in the contract library and in the controllers themselves. 

For simple cases, where the routes have no restrictions (for example, {id}/{sortingCenterId}), they can be made into constants and reused. But when restrictions are included in the routes (for example, {id:int}/{sortingCenterId}), such routes have to be duplicated, as the semantics inherent in ASP.NET are not supported by Refit (and vice versa).

Client development

The client for the API implements using the Refit library. We wrote the following extension method to register an API client. We pass the client interface from the contract library, along with configuration arguments, to the method below. As a result, a dynamically generated Refit class containing HTTP client calls is registered in the DI container for this interface.

Accordingly, all we need to do is register the client interface from the contract library in one line. Next, we implement this interface using DI into the necessary classes and address external services. Also, this approach makes it easy to test code that has dependencies on the client API.

Asynchronous interaction between microservices

Asynchronous communication occurs when one system sends a message to another and continues its work without waiting for an acknowledgment or response. The response can be received later through messages or callback functions. This type of interaction is common when the requesting system does not require information to continue its actions.

In our case, asynchronous communication of microservices is implemented through Kafka (message broker). We are writing the Async API specification, which is a similar standard to OpenAPI but is used for describing an asynchronous interaction protocol.

The principle is similar to synchronous one. We describe the specification, approve it, create a library of asynchronous contracts, and then write producers that will publish messages to the queue and consumers that will read and process them.

The specification is slightly different—  we describe not methods, but message types and channels. In each channel, we describe the types of messages that are sent to the corresponding channels.

Async contract library

The Async contracts library is a NuGet package based on the specification. It contains message models that are sent to or read from the queue.

In our case, all messages are events. An event is a wrapper for a data object with the necessary parameters defined within the event. 

For example, the “Delivery created” event, in addition to information about the event itself (identifier, type, date, and time), will contain information about the delivery. The process looks like this: we created a library of contracts, added all the events provided for in the specification, and created a producer that will generate events.

The screenshot shows a piece of business logic code that completes the execution of the task. Starting from line 5, a transaction is opened, within which data is written to the microservice database. The next three lines are responsible for sending the message to the queue.

The screenshot above shows a consumer. This is the handler to which all messages arrive. It includes filtering and usually processes the business logic that should be triggered when a message is received.

When a new microservice appears, the approach has already been worked out, and all the processes for creating a library of contracts and working with Kafka are debugged. As a result, everything is efficient and there are no discrepancies in development.

Conclusion

Of course, the methods of organizing the interaction of microservices described in this article are not the only possible ones. For instance, gRPC is another powerful alternative worth exploring. We might talk about that in a future article, using an example from one of our other projects. Until then, happy coding!

It's easy to start working with us. Just fill the brief or call us.

Find out more
White Arrow
From the blog
Related articles

How personalisation works in Sitecore XM Cloud

Anna Bastron

In my previous article, I shared a comprehensive troubleshooting guide for Sitecore XM Cloud tracking and personalisation. This article visualises what happens behind the scenes when you enable personalisation and tracking in your Sitecore XM Cloud applications.

Sitecore

Server and client components in Next.js: when, how and why?

Sergei Pestov

All the text and examples in this article refer to Next.js 13.4 and newer versions, in which React Server Components have gained stable status and became the recommended approach for developing applications using Next.js.

Next.js

How to properly measure code speed in .NET

Anton Vorotyncev

Imagine you have a solution to a problem or a task, and now you need to evaluate the optimality of this solution from a performance perspective.

.NET

Hidden Aspects of TypeScript and How to Resolve Them

Andrey Stepanov

We suggest using a special editor to immediately check each example while reading the article. This editor is convenient because you can switch the TypeScript version in it.

TypeScript

Troubleshooting tracking and personalisation in Sitecore XM Cloud

Anna Gevel

One of the first things I tested in Sitecore XM Cloud was embedded tracking and personalisation capabilities. It has been really interesting to see what is available out-of-the-box, how much flexibility XM Cloud offers to marketing teams and what is required from developers to set it up.

Sitecore

Mastering advanced tracking with Kentico Xperience

Dmitry Bastron

We will take you on a journey through a real-life scenario of implementing advanced tracking and analytics using Kentico Xperience 13 DXP.

Kentico
Devtools

Why is Kentico of such significance to us?

Anastasia Medvedeva

Kentico stands as one of our principal development tools, we believe it would be fitting to address why we opt to work with Kentico and why we allocate substantial time to cultivating our experts in this DXP.

Kentico

Where to start learning Sitecore - An interview with Sitecore MVP Anna Gevel

Anna Gevel

As a software development company, we at Byteminds truly believe that learning and sharing knowledge is one of the best ways of growing technical expertise.

Sitecore

Sitecore replatforming and upgrades

Anastasia Medvedeva

Our expertise spans full-scale builds and support to upgrades and replatforming.

Sitecore

How we improved page load speed for Next.js ecommerce website by 50%

Sergei Pestov

How to stop declining of the performance indicators of your ecommerce website and perform optimising page load performance.

Next.js

Sitecore integration with Azure Active Directory B2C

Dmitry Bastron

We would like to share our experience of integrating Sitecore 9.3 with the Azure AD B2C (Azure Active Directory Business to Consumer) user management system.

Sitecore
Azure

Activity logging with Xperience by Kentico

Dmitry Bastron

We'll dive into practical implementation in your Xperience by Kentico project. We'll guide you through setting up a custom activity type and show you how to log visitor activities effectively.

Kentico

Interesting features of devtools for QA

Egor Yaroslavcev

Chrome DevTools serves as a developer console, offering an array of in-browser tools for constructing and debugging websites and applications.

Devtools
QA

Kentico replatforming and upgrades

Anastasia Medvedeva

Since 2015, we've been harnessing Kentico's capabilities well beyond its core CMS functions.

Kentico

Umbraco replatforming and upgrades

Anastasia Medvedeva

Our team boasts several developers experienced in working with Umbraco, specialising in development, upgrading, and replatforming from other CMS to Umbraco.

Umbraco

Sitecore Personalize: tips & tricks for decision models and programmable nodes

Anna Gevel

We've collected various findings around decision models and programmable nodes working with Sitecore Personalize.

Sitecore

Fixed Price, Time & Materials, and Retainer: How to Choose the Right Agreement for Your Project with Us

Andrey Stepanov

We will explain how these agreements differ from one another and what projects they are suitable for.

Customer success

Enterprise projects: what does a developer need to know?

Fedor Kiselev

Let's talk about what enterprise development is, what nuance enterprise projects may have, and which skills you need to acquire to successfully work within the .NET stack.

Development

Headless CMS. Identifying Ideal Use Cases and Speeding Up Time-to-Market

Andrey Stepanov

All you need to know about Headless CMS. We also share the knowledge about benefits of Headless CMS, its pros and cons.

Headless CMS

Dynamic URL routing with Kontent.ai

We'll consider the top-to-bottom approach for modeling content relationships, as it is more user-friendly for content editors working in the Kontent.ai admin interface.

Kontent Ai
This website uses cookies. View Privacy Policy.