Skip to content

Commit 2196a7f

Browse files
committed
...
1 parent 73fffff commit 2196a7f

6 files changed

Lines changed: 219 additions & 620 deletions

File tree

guides/integration/assets/calesi-overview.drawio.svg

Lines changed: 4 additions & 0 deletions
Loading
1.3 MB
Loading

guides/integration/assets/overview.drawio.svg

Lines changed: 0 additions & 509 deletions
This file was deleted.

guides/integration/calesi.md

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ Integrating remote services - from other applications, third-party services, or
2222

2323

2424

25-
## Introduction & Overview
25+
## Preliminaries
2626

27-
### As If They’re Local
27+
### Teaser
2828

2929
With CAP, Service integration is greatly simplified. Consumption of remote services from other applications, third-party services, or platform services is as easy as calling them _as if they're local_:
3030

@@ -72,15 +72,24 @@ Remote CAP services can be consumed using the same high-level, uniform APIs as f
7272
>
7373
> Note that in the exercise above, the consumer side didn't even have any information about the service provider, except for the URL endpoint and protocols served, which it got from the service binding. In particular no API/service definitions at all – neither in *CDS*, *OData*, nor *OpenAPI*.
7474
75-
The remainder of this guide goes beyond such simple scenarios
76-
and covers all the aspects of CAP-level service integration in detail.
7775

7876

7977

8078

79+
### Overview
80+
81+
While the above teaser nicely demonstrates the simplicity of CAP-level service integration, CAP can facilitate real-life integration scenarios even more, if we've captured APIs in CDS models. The remainder of this guide walks us through the steps to provide and share such APIs, import them to consuming apps as CDS models and use these in there as if they were local. The graphic below shows the flow of essential steps involved:
82+
83+
![Workflow diagram showing five numbered steps of CAP-level service integration. Service Provider box on left contains step 1 Service Definition in blue and Domain Models in gray. Packaged API box in center shows step 2 Service Interface in light gray. Service Consumer box on right displays step 4 Consumption Views in light blue and step 5 Own Models in blue. Arrows connect the components left to right. Below, numbered list describes: 1 Expose Service Interfaces as usual, 2 Export APIs using cds export and npm publish, 3 Import APIs using cds import or npm add, 4 Add Consumption Views defining what to consume, 5 Use with own models as if they're local.
84+
](assets/calesi-overview.drawio.svg)
85+
86+
87+
####
88+
89+
8190
### The XTravels Sample
8291

83-
In this guide we'll use the _XTravels_ sample application as our running example. It's a modernized adaptation of the renowned [ABAP Flight reference sample](https://help.sap.com/docs/abap-cloud/abap-rap/abap-flight-reference-scenario), reimplemented using CAP and split into two microservices:
92+
In this guide we use the _XTravels_ sample application as our running example. It's a modernized adaptation of the [ABAP Flight reference sample](https://help.sap.com/docs/abap-cloud/abap-rap/abap-flight-reference-scenario), reimplemented using CAP and split into two microservices:
8493

8594
- The [*@capire/xflights*](https://github.com/capire/xflights) service provides flight-related master data, such as *Flights*, *Airports*, *Airlines*, and *Supplements* (like extra luggage, meals, etc.). It exposes this data via a CAP service API.
8695

@@ -96,17 +105,13 @@ The resulting entity-relationship model looks like that:
96105

97106
From a service integration perspective, this sample mainly shows a data federation scenario, where the application consumes data from different upstream systems (XFlights and S/4HANA) – most frequently in a readonly fashion – to display it together with the application's local data.
98107

99-
### Workflow Overview
100-
101-
The graphic below shows the flow of essential steps for service integration, which the following sections walk you through in detail:
102108

103-
![Workflow diagram showing five numbered steps of CAP-level service integration. Service Provider box on left contains step 1 Service Definition in blue and Domain Models in gray. Packaged API box in center shows step 2 Service Interface in light gray. Service Consumer box on right displays step 4 Consumption Views in light blue and step 5 Own Models in blue. Arrows connect the components left to right. Below, numbered list describes: 1 Expose Service Interfaces as usual, 2 Export APIs using cds export and npm publish, 3 Import APIs using cds import or npm add, 4 Add Consumption Views defining what to consume, 5 Use with own models as if they're local.
104-
](assets/overview.drawio.svg)
105109

110+
#### Getting Started
106111

107-
#### Getting Started...
112+
[`cap/samples`]: #getting-started
108113

109-
Let's dive into the details of CAP-level service integration, using the XTravels sample as our running example. Clone both repositories as follows to follow along:
114+
So, let's dive into the details of CAP-level service integration, using the XTravels sample as our running example. Clone both repositories as follows to follow along:
110115

111116
```sh
112117
mkdir -p cap/samples
@@ -116,9 +121,10 @@ git clone https://github.com/capire/xtravels
116121
```
117122

118123

124+
119125
## Providing CAP-level APIs
120126

121-
In case of CAP service providers, as for [*@capire/xflights*](https://github.com/capire/xflights) in our [sample scenario](#the-xtravels-sample), you define [CAP services](../services/index) for all inbound interfaces, which includes (private) interfaces to your application's UIs, as well as public APIs to any other remote consumers.
127+
In case of CAP service providers, as for [*@capire/xflights*](https://github.com/capire/xflights), you define [CAP services](../services/index) for all inbound interfaces, which includes (private) interfaces to your application's UIs, as well as public APIs to any other remote consumers.
122128

123129

124130
### Defining Service APIs
@@ -447,7 +453,7 @@ npm add @capire/xflights-data
447453
This makes the exported models with all accompanying artifacts available in the target project's `node_modules` folder. In addition, it adds a respective package dependency to the consuming application's *package.json* like this:
448454

449455
::: code-group
450-
```json [package.json]
456+
```json [xtravels/package.json]
451457
{...
452458
"dependencies": { ...
453459
"@capire/xflights-data": "0.1.12"
@@ -512,7 +518,7 @@ Instead of importing the same APIs repeatedly in each project, you can import th
512518
For the _XTravels_ sample, we did so with the [`@capire/s4`](https://github.com/capire/s4) sample package, which we created as follows.
513519
514520
515-
1. We started a new CAP project – get the outcome from Github to follow along:
521+
1. We started a new CAP project – clone the repository into the [`cap/samples`] folder we created in the beginning and open it in VS Code to follow along:
516522
517523
```shell
518524
git clone https://github.com/capire/s4.git
@@ -773,7 +779,7 @@ There are similar references to `Flights` entity from xflights in other parts of
773779
774780
### Mocked Out of the Box
775781
776-
With mashed up models, you can run applications in _'airplane mode'_ without upstream services running. CAP mocks imported services automatically _in-process_ with mock data in the same _in-memory_ database as your own data.
782+
With mashed up models in place, we can run applications in _'airplane mode'_ without upstream services running. CAP mocks imported services automatically _in-process_ with mock data in the same _in-memory_ database as our own data.
777783
778784
1. Start the xtravels application locally using `cds watch` as usual, and note the output about the integrated services being mocked automatically:
779785
@@ -801,7 +807,9 @@ With mashed up models, you can run applications in _'airplane mode'_ without ups
801807
>
802808
> The mocked-out-of-the-box capabilities of CAP, with remoted services mocked in-process and a shared in-memory database, allows us to greatly speed up development and time to market. For real remote operations there is additional investment required, of course. But the agnostic nature of CAP-level Service Integration also allows you to spawn two working tracks running in parallel: One team to focus on domain and functionality, and another one to work on the integration logic under the hood.
803809
804-
We'll learn more about mocking and inner loop development in the [next chapter](./inner-loops).
810+
Learn more about mocking and inner loop development in the [*Inner Loop Development*](./inner-loops) guide.
811+
812+
805813
806814
807815
#### Integration Logic Required
@@ -968,7 +976,7 @@ await s4.read`A_BusinessPartner`.limit (3) // shorthand // [!code focus]
968976
```
969977
:::
970978
971-
Read the same data via the `s4.capire.s4.Customers` consumption view:
979+
Read the same data via the `sap.capire.s4.Customers` consumption view:
972980
```js
973981
const { Customers } = cds.entities ('sap.capire.s4')
974982
await s4.read (Customers) .limit (3) // [!code focus]
@@ -1000,7 +1008,7 @@ Watch the log output in the second terminal to see the translated OData requests
10001008
```
10011009
:::
10021010
1003-
CRUD some data into remote `A_BusinessPartner` entity, still via the `s4.capire.s4.Customers` consumption view:
1011+
CRUD some data into remote `A_BusinessPartner` entity, still via the `sap.capire.s4.Customers` consumption view:
10041012
```js
10051013
await s4.insert ({ ID: '123', Name: 'Sherlock' }) .into (Customers)
10061014
await s4.create (Customers, { ID: '456', Name: 'Holmes' })

guides/integration/data-federation.md

Lines changed: 53 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,35 @@
11
# CAP-level Data Federation
22

3-
CAP applications can integrate and federate data from multiple external data sources, enabling seamless access and manipulation of distributed data. This guide provides an overview of the core concepts and techniques for implementing data federation in CAP applications, including service-level replication, HANA virtual tables, synonyms, and data products.
3+
CAP applications can integrate and federate data from multiple external data sources, enabling close access to distributed data. This guide provides an overview of the core concepts and techniques for implementing data federation in CAP applications, and how CAP helps solving this generically, and thus serving data federation out of the box.
44
{.abstract}
55

6+
[[toc]]
67

7-
## Introduction & Motivation
88

9-
Displaying external data in lists commonly requires fast access to that data. Relying on live calls to remote services per row is clearly not an option, as that would lead to poor performance, excessive load on server, and a nightmare regarding resilience. Instead, we somehow need to ensure that all required data is available locally, so that it can be accessed fast and reliably by UIs, using good old SQL JOINs.
9+
## Preliminaries
1010

11-
For example, we saw the need for that already in the [CAP-level Service Integration](calesi.md#integration-logic-required) guide, where the `Customer` field in the travel requests list is populated from the remote S/4 Business Partner service, but missing when running the services separately:
11+
### Prerequisites
1212

13-
1. First run these commands **in two separate terminals**:
13+
You should be familiar with the content in the [_CAP-level Service Integration_](calesi.md) guide, as we build upon that foundation here. In particular, you should have read and understood these sections:
1414

15-
```shell :line-numbers=1
16-
cds mock apis/capire/s4.cds
17-
```
18-
```shell :line-numbers=2
19-
cds mock apis/capire/xflights.cds
20-
```
15+
- [*Overview* of *CAP-level Service Integration*](calesi.md#overview)
16+
- [_Providing & Exporting APIs_](calesi.md#providing-cap-level-apis)
17+
- [_Importing APIs_](calesi.md#importing-apis)
18+
- [_Consumption Views_](calesi.md#consumption-views)
19+
20+
In addition, you should have read the introduction to [the _XTravels_ sample](calesi.md#the-xtravels-sample) application, which we continue to use as our running example.
2121

22-
2. Start the xtravels server as usual **in a third terminal**, and note that it now _connects_ to the other services instead of mocking them:
2322

24-
```shell :line-numbers=3
25-
cds watch
26-
```
27-
```zsh
28-
[cds] - connect to sap.capire.s4.business-partner > odata {
29-
url: 'http://localhost:54476/odata/v4/s4-business-partner'
30-
}
31-
```
32-
```zsh
33-
[cds] - connect to sap.capire.flights.data > hcql {
34-
url: 'http://localhost:54475/hcql/data'
35-
}
36-
```
3723

38-
2. Open the Fiori UI in the browser again -> data from the S/4 service is missing now, as we have not yet implemented the required custom code for the actual data integration, the same applies to the flight data from _xflights_:
24+
### Motivation
25+
26+
There are many scenarios where data from remote services needs to be in close access locally. For example when we display lists of local data joined with remote data, as we introduce in the [*CAP-level Service Integration*](calesi.md#integration-logic-required) guide:
3927

4028
![XTravels Fiori list view showing a table of travel requests, with the Customer column empty.](assets/xtravels-list-.png)
4129

4230
![XTravels Fiori details view showing a travel requests, with the flights data missing](assets/xtravels-bookings-.png)
4331

44-
In addition, when we again look into the log output, we see some bulk requests like shown below, which indicates that the Fiori client is desparately trying to fetch the missing customer data. If we'd scroll the list in the UI this would repeat like crazy.
32+
When we run that and look into the log output of the xtravels app server, we see some bulk requests as shown below, which indicates that the Fiori client is desparately trying to fetch the missing customer data. If we'd scroll the list in the UI this would repeat like crazy.
4533

4634
<span style="font-size:63%">
4735

@@ -80,37 +68,62 @@ In addition, when we again look into the log output, we see some bulk requests l
8068
```
8169
</span>
8270

71+
Relying on live calls to remote services per row is clearly not an option. Instead, we'd rather ensure that data required in close access is really available locally, so it can be joined with own data using SQL JOINs. This is what _data federation_ is all about.
72+
8373

84-
## Get The XTravels Sample
8574

86-
In the [`@capire/xtravels`](https://github.com/capire/xtravels) app we accomplished that with a simple, yet quite effective data replication solution, which automatically replicates data as documented below.
8775

88-
Clone the project from GitHub and open it in VS Code to follow along:
76+
### The XTravels Sample
8977

90-
```shell
91-
git clone https://github.com/capire/xtravels.git cap/samples/xtravels
92-
code cap/samples/xtravels
78+
We'll use the same [XTravels sample](calesi.md#the-xtravels-sample) and setup as in the [_CAP-level Service Integration_](calesi.md) guide. If you haven't done so already, clone the required repositories to follow along:
79+
80+
```sh
81+
mkdir -p cap/samples
82+
cd cap/samples
83+
git clone https://github.com/capire/xtravels
84+
git clone https://github.com/capire/xflights
85+
git clone https://github.com/capire/s4
9386
```
9487

88+
[@capire/xtravels]: https://github.com/capire/xtravels
89+
[@capire/xflights]: https://github.com/capire/xflights
90+
[@capire/s4]: https://github.com/capire/s4
91+
92+
93+
9594

9695
## Federated Consumption Views
9796

98-
Tag [consumption views](calesi#consumption-views) with the `@federated` annotation, to express your intent to have that data federated, i.e. in close access locally. For example, we did so in out consumption view for S/4 Business Partners:
97+
Tag [consumption views](calesi#consumption-views) with the `@federated` annotation, to express your intent to have that data federated, i.e. in close access locally. For example, we did so in our consumption view for entities imported from XFlights as well as for the S/4 Business Partners entity:
9998

10099
::: code-group
101-
```cds :line-numbers=4 [apis/capire/s4.cds]
102-
@federated entity Customers as projection on S4.A_BusinessPartner { ... }
100+
101+
```cds :line-numbers [apis/capire/xflights.cds]
102+
@federated entity Flights as projection on x.Flights { ... }
103+
@federated entity Supplements as projection on x.Supplements { ... }
103104
```
105+
106+
:::
107+
::: code-group
108+
109+
```cds :line-numbers [apis/capire/s4.cds]
110+
@federated entity Customers as projection on S4.A_BusinessPartner {
111+
BusinessPartner as ID,
112+
PersonFullName as Name,
113+
LastChangeDate as modifiedAt,
114+
} where BusinessPartnerCategory == 1; // 1 = Person
115+
```
116+
104117
:::
105118

106119
> [!tip] Stay Intentional -> <i>What, not how!</i> -> Minimal Assumptions
107120
>
108121
> By tagging entities with `@federated` we stay _intentional_ about **_what_** we want to achieve, and avoid any premature assumptions about **_how_** things are actually implemented. => This allows CAP runtimes – or your own _generic_ solutions, as in this case – to choose the best possible implementation strategies for the given environment and use case, which may differ between development, testing, and production environments, or might need to evolve over time.
109122
110123

111-
## Generic Implementation
124+
## Service-level Replication
112125

113-
Here's the complete code, as found in [`srv/data-federation.js`](https://github.com/capire/xtravels/blob/main/srv/data-federation.js):
126+
Next we implement a generic solution for data federation, which automates the basic hard-coded approach for data federation presented [before](calesi#data-federation). Here's the complete code, as found in [`srv/data-federation.js`](https://github.com/capire/xtravels/blob/main/srv/data-federation.js):
114127

115128
::: code-group
116129
```js:line-numbers [srv/data-federation.js]
@@ -135,7 +148,7 @@ PROD || cds.on ('loaded', csn => {
135148
PROD || cds.once ('served', () => Promise.all (feed.map (async each => {
136149
const srv = await cds.connect.to (each.remote)
137150
srv._once ??=! srv.on ('replicate', replicate)
138-
await srv.schedule ('replicate', each) .every ('3 seconds')
151+
await srv.schedule ('replicate', each) .every ('10 minutes')
139152
})))
140153
141154
// Event handler for replicating single entities
@@ -158,9 +171,7 @@ const is_remote = srv => cds.requires[srv]?.credentials?.url
158171
Let's have a closer look at this code, which handles these main tasks:
159172

160173
1. **Prepare Persistence** – When the model is `loaded`, before it's deployed to the database, we collect all to be `@federated` entities, check whether their respective services are remote, and if so, turn them into tables for local replicas (line 11).
161-
162-
2. **Setup Replication** – Later when all services are `served`, we connect to each remote one (line 20), register a handler for replication (line 21), and schedule it to be invoked every three seconds (line 22).
163-
174+
2. **Setup Replication** – Later when all services are `served`, we connect to each remote one (line 20), register a handler for replication (line 21), and schedule it to be invoked repeatedly (line 22).
164175
3. **Replicate Data** – Finally, the `replicate` handler implements a simple polling-based data federation strategy, based on `modifiedAt` timestamps (lines 28-32), with the actual call to remote happening on line 29.
165176

166177
> [!tip] CAP-level Querying -> agnostic to databases & protocols

0 commit comments

Comments
 (0)