Introduction
Writing distributed applications, especially deployed across a network, tends to be a challenge. This is not only due to the trickiness of the network programming but more so because of your code and business logic getting messed up with communication details. That makes it probably not flexible but hard to reuse.
Meanwhile, most programmers already know how to make their code flexible, reusable, and testable. Yes, reducing code coupling, often achieved by introducing an additional level of indirection, is the definite way to go. Then why don't we apply the same technique to overall application architecture? Simply, decoupling communication details from application logic will help us to build a flexibly distributable, fully testable application consisting of reusable modules.
In this article, we will get through a few simple examples of x2net application and see how distribution works in an x2 way.
Background
x2
x2 is a set of concepts and specifications that facilitates the development of highly flexible cross-platform, cross-language distributed systems. Before further going on, it is recommended to look at its README.md and concepts.md.
x2net
x2net is the reference port of x2 written in C# targeting universal .NET environments.
Original
In order to focus on the structural aspect, we begin with an extremely simple application:
C#
- public class Hello
- {
- public static void Main()
- {
- while (true)
- {
- var input = Console.ReadLine();
- if (input == "bye")
- {
- break;
- }
- var greeting = String.Format("Hello, {0}!", input);
- Console.WriteLine(greeting);
- }
- }
- }
Defining Events
An x2 application is composed of logic cases (or flows) that communicate only with one another. So defining a shared event hierarchy is the key activity at design time. In this simple example, we can grab the key feature that makes up a greeting sentence out of the name input. We define a request/response event pair for this feature as follows,
XML
- <?xml version="1.0" encoding="utf-8"?>
- <x2 namespace="hello">
- <definitions>
- <!-- Hello request event. -->
- <event name="HelloReq" id="1">
- <!-- Input name. -->
- <property name="Name" type="string"/>
- </event>
- <!-- Hello response event. -->
- <event name="HelloResp" id="2">
- <!-- Resultant greeting sentence. -->
- <property name="Greeting" type="string"/>
- </event>
- </definitions>
- </x2>
Running x2net.xpiler on this XML definition file will yield a corresponding C# source file which we can include into our project.
Preparing Logic Module
Once we define events, we can write the application logic cases to handle those events. Here, we write a simple case which creates the greeting sentence.
C#
- using x2net;
- public class HelloCase : Case
- {
- protected override void Setup()
- {
- // Bind the HelloReq event handler.
- Bind(new HelloReq(), OnHelloReq);
- }
- void OnHelloReq(HelloReq req)
- {
- // Create a new HelloResp event.
- new HelloResp {
- // Set its Greeting property as a generated sentence.
- Greeting = String.Format("Hello, {0}!", req.Name)
- }
- .InResponseOf(req) // Copy the req._Handle builtin property.
- .Post(); // And post it to the hub.
- }
- }
Please note that logic cases react to their interesting events by posting another event in return. They know nothing about the communication details: where request events come from or where response events are headed. Consequently, these logic cases may be freely located, without any change, at any point of the entire distributed application. And they can also be easily tested in isolation.
First x2net Application
Having relevant events and cases, now we are ready to set up our first x2net application with these constructs.
C#
- using x2net;
- public class HelloStandalone
- {
- class LocalCase : Case
- {
- protected override void Setup()
- {
- // To print the content of HelloResp to the console output.
- Bind(new HelloResp(), (e) => {
- Console.WriteLine(e.Greeting);
- });
- }
- }
- public static void Main()
- {
- Hub.Instance
- .Attach(new SingleThreadFlow()
- .Add(new HelloCase())
- .Add(new LocalCase()));
- using (new Hub.Flows().Startup())
- {
- while (true)
- {
- var input = Console.ReadLine();
- if (input == "bye")
- {
- break;
- }
- new HelloReq { Name = input }.Post();
- }
- }
- }
- }
Zen WebnetPosted Jul 4, 2018, 5:07 AM
Great Writes. Thank you for sharing.
Bhavesh JadavPosted Apr 18, 2018, 5:01 AM
Great explanation, thanks for share it.