Tutorial: Web Service Federation
In this tutorial, you will learn how to easily implement web service federation with CorrLang.
The Prospect
Let us assume the following example scenario:
There is a hypothetical retails company running three systems:
- One for storing the purchases of customers, called
Sales
. - One for storing the invoices that were send to customers, called
Invoices
. - One for storing the employee data.
These systems have grown historically, were developed in different points in time by differnt teams. However, they sometimes share the same information, e.g. customers naturally appear in both Sales
and Invoices
(they are called “clients” there), and it is, of course, possible that an “employee” is also a “customer.
Now, the retail company is looking for a report of the data in all the systems, where the shared information is already aligned.
In this tutorial, I will demonstrate how CorrLang can help with this.
Preliminaries: Getting the Demo Code
To follow along with the example, it is advised to check out the demo code:
https://github.com/webminz/usecase_SalesInvicesHR.git
Follow the README instructions in that repository to set up the three Node.js endpoints.
Exploring the scenario
Assuming, the three endpoints are up and running and accessible at
- http://localhost:4011
- http://localhost:4012
- http://localhost:4013
The first step when applying CorrLang is always the creation of a so-called _CorrSpec. Thus, it is time to open you favourite text editor and create a new empty file called spec.corr
(actually the name nor the ending of this file does not really matter).
First, you will have to specify the endpoints we are building our correspondence on:
endpoint Sales {
type SERVER
at http://localhost:4011
technology GRAPH_QL
}
endpoint Invoices {
type SERVER
at http://localhost:4012
technology GRAPH_QL
}
endpoint HR {
type SERVER
at http://localhost:4013
technology GRAPH_QL
}
These definitions should be readable pretty intuitively. In layer articles, we will explain the meaning of these keywords in greater detail.
The core of every CorrSpec is the correspondence
. The following line defines a correspondence between the three endpoints and give it the name “Backoffice”.
correspondence Backoffice (Sales, Invoices, HR) {
}
Initially, it is empty, which means that we do not specify any relationships yet.
Finally, in order to actually run CorrLang, we need to define a goal.
goal Draw {
correspondence Backoffice
action SCHEMA
technology IMAGE
target FILE {
at file:./vizz.png
}
}
This goal will create the schema of the resulting “Backoffice” system in the form of a picture (i.e. as a PNG image).
Hence, it is time to run corrlang for the first time! Open a terminal, navigate to the folder where spec.corr
is stored, and run the following command (assuming that you had put the corrlang.sh/corrlang.bat on your $PATH during the installation).
corrlang spec.corr g:Draw
When CorrLang terminates, you should see a new file vizz.png
in the working directory. Open that file in you favourite picture viewer and try to intepret the result considering the fact the the correspondence
is empty.
Well, an empty correspondence
means that there are no further constraints on how the systems should be aligned. Thus, the systems are simply put in _parallel composition”.
You can even experience this as a running system! Try adding the following goal to spec.corr
.
goal Federation {
correspondence Backoffice
action FEDERATION
technology GRAPH_QL
target SERVER {
contextPath graphql/
port 8081
}
}
and run CorrLang with
corrlang spec.corr g:Federation
Try now opening the endpoint (http://localhost:8081/graphql) in a GraphQL client. Can you see what happens when two systems are using the same name (i.e. name clashes)?
Aligning your first data type
Clearly, the three schemas share a common entity: They are called Customer
in Sales
, Client
in Invoices
, and Employee
in HR
. Hence, we want to unify them under the same concept, and we call it Partner
.
To do this, let us add our first identification-commonalitu:
correspondence Backoffice (Sales, Invoices, HR) {
identify (Sales.Customer, Invoices.Client, HR.Employee) as Partner;
}
Now, try calling the Draw
goal and see what happens.
It turns out, that these three types have several Attributes that are shared as well. Moreover, the Address
type is shared among Sales
and Invoices
. Hence, we are extending our correspondence as follows:
correspondence Backoffice (Sales, Invoices, HR) {
identify (Sales.Customer,Invoices.Client,HR.Employee) as Partner
with {
identify (Sales.Customer.id,Invoices.Client.id,HR.Employee.id) as id;
identify (Sales.Customer.name,Invoices.Client.name) as name;
identify (Sales.Customer.email,HR.Employee.email) as email;
identify (Sales.Customer.address,Invoices.Client.address) as address;
};
identify (Sales.Address,Invoices.Address) as Address
with {
identify (Sales.Address.street,Invoices.Address.street) as street;
identify (Sales.Address.city,Invoices.Address.city) as city;
identify (Sales.Address.postalCode,Invoices.Address.postalCode) as postalCode;
identify (Sales.Address.country,Invoices.Address.country) as country;
identify (Sales.Address.state,Invoices.Address.state) as state;
};
}
Run the Draw
goal again and look at the result.
Now, that these data types are so nicely aligned, it would be nice to access the respective data all at once. To do this, we have to align respective query operations as well. Add the following line into the top level of the correspondence.
identify (Sales.Query.customers,Invoices.Query.clients,HR.Query.employees) as Query.partners;
OK! It is time to see that in action. Run the Federation
goal again! and query the Partner-data by sending the following query:
query {
partners {
fullName
purchases {
id
}
invoices {
id
}
worksAt {
name
}
}
}
Look carefully at the result and try to interpret it
Aligning data
(coming soon)
Bonus: Video
The following video demonstrates some of the highlights in the description above.