Orchestration and choreography in .NET microservices

Orchestration takes a centralized approach, and choreography a decentralized approach, to coordinating the interactions of microservices. Understand the differences.

shutterstock 1869308242 team putting together a chain of gears teamwork coordination collaboration
Studio Romantic / Shutterstock

As more businesses adopt microservice architectures for their applications, more developers have had to grapple with the concepts of orchestration and choreography. Although these terms are sometimes used interchangeably, there are key differences between these two architectural patterns.

Orchestration is a centralized approach to making all control decisions about interactions between services. Here a central orchestrator service coordinates all of the other services that execute a business transaction or workflow. By contrast, a choreography is a decentralized approach to coordinating this workflow, where each service determines its own behavior based on the messages it receives from other services.

This article will cover the core concepts of orchestration and choreography in microservices architectures and discuss how you might use each (or both) in your microservices-based applications. We’ll also simulate microservices orchestration and choreography in code examples provided below.

What is microservices architecture?

Microservices refer to a style of software architecture where a large application can be built as a conglomeration of small, autonomous services. Each microservice has a specific purpose and is deployable independently.

A microservices architecture makes it easy to scale individual services as needed. It also allows for more speed and flexibility when making changes to the application because only the affected service needs to be redeployed.

Two main approaches to managing communication between microservices are orchestration and choreography. Let’s understand the differences.

What is microservices orchestration?

Microservices orchestration refers to a centralized approach, where a central component, known as the orchestrator, is responsible for managing and coordinating the interactions between microservices. Kubernetes and Docker Swarm are examples of orchestration tools.

Orchestration helps to ensure consistency and reliability in the interactions between microservices by defining the sequence of steps that need to be followed. It also helps to manage failures and errors by providing a centralized point of control and by ensuring that each microservice communicates only with the orchestrator.

Orchestration is useful in scenarios where there is a need for a centralized authority to control the interactions between microservices, and when there is a need for coordination and management of complex processes that involve multiple microservices.

Examples of orchestration include using an API gateway to manage and secure communications between microservices, or using a message broker to coordinate the flow of messages between microservices.

Microservices orchestration example in C#

Workflow Core is a lightweight workflow engine for .NET Core applications. We can use it to implement orchestration. Below is an example of a basic microservices orchestration application in C# using the Workflow Core library.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using WorkflowCore.Interface;
using WorkflowCore.Models;
namespace MicroservicesOrchestrationExample
{
    class Program
    {
        public static void Main(string[] args)
        {
            IServiceProvider serviceProvider = ConfigureServices();
            var host = serviceProvider.GetService<IWorkflowHost>();
            host.RegisterWorkflow<SampleWorkflow>();       
            host.Start();           
            host.StartWorkflow("Sample");           
            Console.ReadLine();
            host.Stop();
        }
        private static IServiceProvider ConfigureServices()
        {
            IServiceCollection services = new ServiceCollection();
            services.AddLogging();
            services.AddWorkflow();
            services.AddTransient<LastStep>();
            var serviceProvider = services.BuildServiceProvider();
            return serviceProvider;
        }
    }
    public class SampleWorkflow : IWorkflow
    {
        public void Build(IWorkflowBuilder<object> builder)
        {
            builder
                .StartWith<FirstStep>()
                .Then<LastStep>();
        }
        public string Id => "Sample";
        public int Version => 1;
    }
    public class FirstStep : StepBody
    {
        public override ExecutionResult Run(IStepExecutionContext context)
        {
            Console.WriteLine("Microservice A");
            return ExecutionResult.Next();
        }
    }
    public class LastStep : StepBody
    {
        public override ExecutionResult Run(IStepExecutionContext context)
        {
            Console.WriteLine("Microservice B");
            return ExecutionResult.Next();
        }
    }
}

In this example, we’ve defined a workflow called SampleWorkflow that consists of two steps, FirstStep and LastStep. The Workflow Core library takes care of executing these steps in the correct order. The Main method starts the workflow host and waits for the user to press any key to terminate the program.

What is microservices choreography?

Choreography refers to the decentralized approach to coordinating interactions between microservices, where each service communicates directly with other services, without relying on a central coordinator. This pattern puts the burden of coordinating interactions on the microservices themselves.

In choreography, each microservice is responsible for maintaining its own state and exchanging messages or events with other microservices to coordinate the workflow. This approach has the benefit of removing the tight coupling between an orchestrator and the microservices that execute the workflow. The drawback is that increases the point-to-point communication between the microservices.

Choreography helps to achieve greater scalability and flexibility in microservices architecture, as each microservice can evolve and change independently, without affecting the overall system. And because choreography eliminates the dependence on a central orchestrator, it can increase the reliability and resiliency of the overall system.

Examples of choreography include ecommerce websites that make use of different services to provide product information, payment processing, and delivery tracking, but do not use a centralized service to control and coordinate these functions.

Microservices choreography example in C#

There are different ways to implement microservices choreography. One approach is to use a centralized message broker, such as RabbitMQ or ActiveMQ, to which all services can connect. The message broker acts as a mediator between the services, routing messages as needed.

Another approach is to use a pub/sub model, where each service publishes its events on a central topic, and any interested parties can subscribe to that topic. This allows for loose coupling between services, as they don’t need to know about each other in advance. But as with the message broker approach, there is still a kind of central dependency.

Below is an example of a basic microservices choreography application in C# using the MassTransit library.

using MassTransit;
namespace HelloWorldChoreography
{
    class Program
    {
        static void Main(string[] args)
        {
            var busControl = Bus.Factory.CreateUsingInMemory(configure =>
            {
                configure.ReceiveEndpoint("sample-queue", endpoint =>
                {
                    endpoint.Consumer<SampleConsumer>();
                });
            });
            busControl.Start();
            Console.WriteLine("Press any key to exit");
            Console.ReadLine();
            busControl.Stop();
        }
    }
    public class SampleConsumer : IConsumer<SampleMessage>
    {
        public Task Consume(ConsumeContext<SampleMessage> context)
        {
            Console.WriteLine("Message received...");
            return Task.FromResult(0);
        }
    }
    public class SampleMessage { }
}

In this example, the consumer class, SampleConsumer, listens to the sample-queue and handles incoming messages of type SampleMessage. When a message is received, the consumer will display the text “Message received…” at the console window. When the program starts, the Main method triggers the MassTransit bus and waits for the user to press any key to terminate the program.

Orchestration vs. choreography

Orchestration, as the name suggests, is all about having centralized control over the various services in a microservices architecture. This can be seen as an advantage, as it gives you more control over how the various services interact with each other. However, this also means that any changes to the overall system will require changes to the orchestrator itself, which can be complex and time-consuming.

Choreography, on the other hand, takes a more decentralized approach. Each service is responsible for its own interactions with other services, meaning that there is no need for a centralized orchestrator. This can make development and deployment simpler and faster, as there are no dependencies on a central system. However, choreography can also make debugging and troubleshooting more difficult, as it can be harder to understand how the various services are interacting.

Which should you use?

Orchestration and choreography are key elements of a successful microservices architecture that can significantly enhance performance, scalability, and reliability. Through orchestration, microservices can communicate with each other more efficiently, enabling them to scale quickly and easily to accommodate larger workloads.

Orchestration defines a precise sequence of steps that each microservice must follow, which is beneficial for identifying and addressing complex service interdependencies. Additionally, orchestration allows business logic to be managed and monitored in one place.

Choreography, on the other hand, is the practice of orchestrating microservices together without a central coordination point. This allows each service to operate independently while still being part of the larger architecture.

Orchestration can be used to manage both simple and complex deployments. It can automate the provisioning of resources, the scaling of services, and the application of updates and patches. Orchestration can also provide insight into the performance of individual services and identify issues that need to be addressed.

Choreography is a good choice if your application requires frequent updates or new releases. It can be used to define how services interact with each other, but it cannot automate the management of those interactions. Choreography is best suited for managing smaller deployments or for testing purposes.

A hybrid approach

The choice between orchestration and choreography depends upon the particular needs and requirements of the architecture. In general, orchestration is used when there is a need for a centralized authority to control the interactions between microservices, while choreography is used when there is a need for a more decentralized and autonomous interaction between services.

Alternatively, one might choose a hybrid approach that integrates orchestration and choreography. A hybrid approach could protect your application from orchestrator failure. But again, this choice would depend on your application’s requirements and your organization’s goals.

Orchestration and choreography play a vital role in managing complex systems, allowing for faster development cycles, greater scalability, and reduced complexity. By implementing either or both approaches carefully and thoughtfully, you can create a robust microservices architecture that is scalable, secure, and easily adaptable to changing requirements.

Copyright © 2023 IDG Communications, Inc.

InfoWorld Technology of the Year Awards 2023. Now open for entries!