W poprzednim wpisie omówiłem, czym jest integracja oraz przedstawiłem jeden z realnych scenariuszy, który możemy napotkać podczas wdrażania ESB w przedsiębiorstwie. W ramach tego wpisu omówimy krok po kroku na przykładzie Mule ESB realizację tego, co było omówione w poprzednim wpisie. Celem tego wpisu jest przedstawienie i omówienie realizacji usług na Mule w oparciu o trójwarstwową architekturę, którą omówiliśmy w poprzednim wpisie.
sap-channel
Pierwsza aplikacja kanałowa sap-channel, jak wcześniej wspomniałem, jest to stary interfejs oparty o SOAP, ponieważ takie było wymaganie projektu --- zachowanie SOAP API dla systemu SAP.
To co istotne dla nas to model danych zdefiniowany w pliku WSDL:
1<wsdl:types> 2<xsd:schema targetNamespace="http://Example.org" elementFormDefault="qualified" > 3<xsd:element name="GetAccount"> 4<xsd:complexType> 5 <xsd:sequence> 6 <xsd:element minOccurs="0" name="account_id" type="xsd:string" /> 7 </xsd:sequence> 8</xsd:complexType> 9</xsd:element> 10<xsd:element name="GetAccountResponse"> 11<xsd:complexType> 12 <xsd:sequence> 13 <xsd:element minOccurs="0" name="account_id" type="xsd:string" /> 14 <xsd:element minOccurs="0" name="account_name" type="xsd:string" /> 15 <xsd:element minOccurs="0" name="account_phone" type="xsd:string" /> 16 </xsd:sequence> 17</xsd:complexType> 18</xsd:element> 19</xsd:schema> 20</wsdl:types>
Model ten różni się od naszego kanonicznego konwencją nazewniczą oraz tym, że jest oparty o XML. Zgodnie z diagramem, istotnym punktem w implementacji naszej warstwy kanałowej będzie nie tylko wywołanie usługi na warstwie business ale także poprawny mapping odpowiedzi.
Powyższy screen prezentuje realizację w Mule ESB przepływu dla sap-channel. Poniżej omówimy najważniejsze jego części:
Api-main to przepływ odpowiedzialny za publikację interfejsu SOAP opartego o plik WSDL. To co istotne, to komponent SOAP Router odpowiedzialny za przekierowywanie w zależności od wywołanej operacji na właściwy przepływ z implementacją. W naszym przypadku jest to operacja GetAccount zdefiniowana poniżej:
1<wsdl:message name="IAccount_GetAccount_InputMessage"> 2 <wsdl:part name="parameters" element="tns:GetAccount" /> 3 </wsdl:message> 4 <wsdl:message name="IAccount_GetAccount_OutputMessage"> 5 <wsdl:part name="parameters" element="tns:GetAccountResponse" /> 6 </wsdl:message> 7 8 <wsdl:portType name="IAccount"> 9 <wsdl:operation name="GetAccount"> 10 <wsdl:input wsaw:Action="http://Example.org/IAccount/GetAccount" message="tns:IAccount_GetAccount_InputMessage" /> 11 <wsdl:output wsaw:Action="http://Example.org/IAccount/GetAccountResponse" message="tns:IAccount_GetAccount_OutputMessage" /> 12 </wsdl:operation> 13 </wsdl:portType> 14 15 <wsdl:binding name="DefaultBinding_IAccount" type="tns:IAccount"> 16 <soap:binding transport="http://schemas.xmlsoap.org/soap/http" /> 17 <wsdl:operation name="GetAccount"> 18 <soap:operation soapAction="http://Example.org/IAccount/GetAccount" style="document" /> 19 <wsdl:input> 20 <soap:body use="literal" /> 21 </wsdl:input> 22 <wsdl:output> 23 <soap:body use="literal" /> 24 </wsdl:output> 25 </wsdl:operation> 26 27 </wsdl:binding> 28 <wsdl:service name="AccountService"> 29 <wsdl:port name="IAccount" binding="tns:DefaultBinding_IAccount"> 30 <soap:address location="http://Example.org/IAccount" /> 31 </wsdl:port> 32 </wsdl:service>
W samej uproszczonej implementacji mamy 3 kroki:
set account id --- odpowiada za przypisanie do payload-u komunikatu wartości account_id. Krok realizowany za pomocą Data Weave.
/account --- wywołanie REST-owej usługi w ramach aplikacji business-account.
Response-mapping --- transformacja odpowiedzi z business-account do XML zgodnego z WSDL.
webapp-channel
Druga aplikacja webapp będzie korzystać z naszego nowego API REST opartego o kanoniczny model w JSON-ie.
Usługi zostały zdefiniowane w ramach pliku RAML i opublikowane w ramach aplikacji webapp-channel. Poniżej jej uproszczona definicja a także realizacja w ramach Mule ESB.
1#%RAML 1.0 2title: Webapp API 3 4types: 5 Account: 6 type: object 7 properties: 8 name: string 9 phone: string 10 id: string 11 12/account/{id}: 13 get: 14 responses: 15 200: 16 body: 17 application/json: 18 type: Account
Analogicznie jak w przypadku aplikacji sap-channel mamy HTTP input odpowiedzialny za wystawienie usługi GET /account/{id} oraz APIkit za właściwy routing na przepływ realizujący implementację. W naszym scenariuszu zakładamy, że webapp operuje na modelu kanonicznym w JSON, gdzie nie ma potrzeby wykonywania mappingu pomiędzy modelami stąd implementacja polegająca na wywołaniu operacji GET /account{id} z aplikacji business-account bez mapowania zwrotnego.
business-account
Aplikacja business-account zgodnie z założeniami ma wystawiać kanoniczny interfejs oraz realizować jedynie orkiestrację usług.
Aplikacja odbiera HTTP GET, wykonuje wywołanie usługi pobrania danych o koncie z systemu salesforce za pomocą aplikacji salesforce-adapter. Następnie w ramach kroku “check status” sprawdzamy czy dane o koncie zostały zwrócone. Jeśli tak, zwracamy odpowiedź, w przeciwnym razie pobieramy dane o koncie z systemu bazodanowego.
salesforce-adapter
Pierwsza z dwóch aplikacji odpowiedzialnych za integrację z systemami domenowymi. Zadaniem tej aplikacji jest odebranie operacji HTTP GET z warstwy business, zbudowanie requestu wyjściowego do systemu Salesforce oraz przetworzenie odpowiedzi do modelu kanonicznego w JSON.
Pierwszym ważnym krokiem w przepływie jest wykonanie operacji SELECT w systemie Salesforce. Realizujemy to za pomocą wbudowanego konektora. Dzięki wbudowanemu mechanizmowi DataSense pobieramy metadane obiektów z Salesforce na których będziemy pracować. Pobranie tych danych daje nam możliwość budowania zapytań za pomocą narzędzia Query Builder oraz możliwość łatwego mapowania w kroku “map to json” ponieważ mamy zaimportowany model danych obiektu zwrotnego z Salesforce.
Następny krok to weryfikacja czy Salesforce zwrócił odpowiedź. W przypadku pobrania danych, dokonujemy mapowania z obiektu Account zwracanego przez Salesforce do odpowiedzi kanonicznej w JSON.
W przeciwnym razie, za pomocą komponentu Groovy możemy wygenerować wyjątek, który spowoduje nam zwrócenie HTTP 404 Resource not found.
throw new org.mule.module.apikit.exception.NotFoundException()
Wygenerowanie tego wyjątku spowoduje zwrócenie do business-account statusu 404 na bazie którego przepływ zdecyduje o wywołaniu dodatkowo aplikacji database-adapter.
database-adapter
Druga z dwóch aplikacji domenowych. Jej zadanie jest identyczne jak w salesforce-adapter. Aplikacja ma na bazie przekazanego id pobrać z bazy danych informacje o koncie oraz wygenerować odpowiedź w modelu kanonicznym w JSON.
Podobnie jak w przypadku Salesforce, tak tu mamy możliwość skorzystać z wbudowanego konektora, który wykorzystuje DataSeanse w celu pobrania modelu danych.
Omówiliśmy jeden z bardzo typowych scenariuszy integracyjnych, z którymi można spotkać się na projekcie wdrożenia ESB. W modelowym wdrożeniu interfejs usług publikowanych na ESB byłby przedstawiony systemom klienckim (konsumentom API) podczas etapu analizy i cała późniejsza realizacja byłaby skupiona na implementacji katalogu usług oraz integracji Mule z systemami domenowymi. Takie projekty się zdarzają, sam miałem przyjemność brać udział w dwóch takich, jednak większość decyzji o wdrożeniu warstwy integracyjnej zapada w realiach, w których systemy w przedsiębiorstwie istnieją od wielu lat i ESB ma za zadanie pewne rzeczy poprawić, inne uporządkować, wprowadzić pewną przewidywalność w projektowaniu przyszłych zmian i funkcjonalności. W takich warunkach nie jesteśmy w stanie zrobić modelowego wdrożenia, omawianego często na konferencjach, ale możemy za pomocą ESB znacznie uporządkować rzeczywistość, z jaką zmagają się osoby odpowiedzialne za rozwój IT w przedsiębiorstwie.