API-led connectivity transforma integrarea enterprise printr-o abordare pe straturi de API-uri reutilizabile. Acest ghid acopera pattern-uri arhitecturale, strategii de implementare si governance pentru o adoptare API-led de succes.
Straturile arhitecturii API-Led
Cele trei straturi ale arhitecturii:
┌─────────────────────────────────────────────────────────────────┐
│ EXPERIENCE LAYER │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Mobile │ │ Web │ │ Partner │ │ IoT │ │
│ │ API │ │ API │ │ API │ │ API │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
├───────┼─────────────┼─────────────┼─────────────┼──────────────┤
│ │ PROCESS LAYER │ │ │
│ ┌────▼─────────────▼─────────────▼─────────────▼────┐ │
│ │ Order Process API │ │
│ └────┬─────────────┬─────────────┬─────────────┬────┘ │
│ │ │ │ │ │
│ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ │
│ │Customer │ │Inventory│ │ Payment │ │Shipping │ │
│ │ Process │ │ Process │ │ Process │ │ Process │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
├───────┼─────────────┼─────────────┼─────────────┼──────────────┤
│ │ SYSTEM LAYER │ │ │
│ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ │
│ │Salesforce│ │ SAP │ │ Stripe │ │ FedEx │ │
│ │ sAPI │ │ sAPI │ │ sAPI │ │ sAPI │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
└───────┼─────────────┼─────────────┼─────────────┼──────────────┘
│ │ │ │
[Salesforce] [SAP ERP] [Stripe] [FedEx API]
Implementarea System API
System API-urile expun sisteme backend cu interfete standardizate:
Salesforce System API
<!-- salesforce-sapi.xml -->
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
xmlns:http="http://www.mulesoft.org/schema/mule/http"
xmlns:salesforce="http://www.mulesoft.org/schema/mule/salesforce"
xmlns:apikit="http://www.mulesoft.org/schema/mule/mule-apikit">
<!-- API Router -->
<flow name="salesforce-sapi-main">
<http:listener config-ref="HTTPS_Listener_Config" path="/api/*">
<http:response statusCode="#[vars.httpStatus default 200]">
<http:headers>#[vars.responseHeaders default {}]</http:headers>
</http:response>
</http:listener>
<!-- Client ID enforcement -->
<apikit:router config-ref="salesforce-sapi-config"/>
</flow>
<!-- GET /customers -->
<flow name="get:\customers:salesforce-sapi-config">
<ee:transform doc:name="Build Query">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output text/plain
var searchTerm = attributes.queryParams.search default ""
var limit = attributes.queryParams.limit default 100
var offset = attributes.queryParams.offset default 0
---
"SELECT Id, Name, Email, Phone, Industry, BillingCity, BillingCountry,
CreatedDate, LastModifiedDate
FROM Account
WHERE IsDeleted = false" ++
(if (searchTerm != "") " AND Name LIKE '%" ++ searchTerm ++ "%'" else "") ++
" ORDER BY Name
LIMIT " ++ (limit as String) ++
" OFFSET " ++ (offset as String)]]></ee:set-payload>
</ee:message>
</ee:transform>
<salesforce:query config-ref="Salesforce_Config" doc:name="Query Accounts"/>
<ee:transform doc:name="Transform to Canonical">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
metadata: {
totalRecords: sizeOf(payload),
offset: attributes.queryParams.offset default 0,
limit: attributes.queryParams.limit default 100,
source: "salesforce"
},
customers: payload map {
id: $.Id,
externalId: $.Id,
name: $.Name,
email: $.Email,
phone: $.Phone,
industry: $.Industry,
address: {
city: $.BillingCity,
country: $.BillingCountry
},
createdAt: $.CreatedDate,
updatedAt: $.LastModifiedDate
}
}]]></ee:set-payload>
</ee:message>
</ee:transform>
</flow>
<!-- GET /customers/{customerId} -->
<flow name="get:\customers\(customerId):salesforce-sapi-config">
<salesforce:query-single config-ref="Salesforce_Config" doc:name="Get Account">
<salesforce:salesforce-query><![CDATA[
SELECT Id, Name, Email, Phone, Industry, Website, Description,
BillingStreet, BillingCity, BillingState, BillingPostalCode, BillingCountry,
ShippingStreet, ShippingCity, ShippingState, ShippingPostalCode, ShippingCountry,
NumberOfEmployees, AnnualRevenue, CreatedDate, LastModifiedDate
FROM Account
WHERE Id = ':customerId'
]]></salesforce:salesforce-query>
<salesforce:parameters><![CDATA[#[{
customerId: attributes.uriParams.customerId
}]]]></salesforce:parameters>
</salesforce:query-single>
<choice doc:name="Check Result">
<when expression="#[payload != null]">
<ee:transform doc:name="Transform Response">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
id: payload.Id,
externalId: payload.Id,
name: payload.Name,
email: payload.Email,
phone: payload.Phone,
website: payload.Website,
industry: payload.Industry,
description: payload.Description,
employees: payload.NumberOfEmployees,
revenue: payload.AnnualRevenue,
billingAddress: {
street: payload.BillingStreet,
city: payload.BillingCity,
state: payload.BillingState,
postalCode: payload.BillingPostalCode,
country: payload.BillingCountry
},
shippingAddress: {
street: payload.ShippingStreet,
city: payload.ShippingCity,
state: payload.ShippingState,
postalCode: payload.ShippingPostalCode,
country: payload.ShippingCountry
},
createdAt: payload.CreatedDate,
updatedAt: payload.LastModifiedDate,
_links: {
self: "/customers/" ++ payload.Id,
contacts: "/customers/" ++ payload.Id ++ "/contacts",
orders: "/customers/" ++ payload.Id ++ "/orders"
}
}]]></ee:set-payload>
</ee:message>
</ee:transform>
</when>
<otherwise>
<raise-error type="APP:NOT_FOUND" description="Customer not found"/>
</otherwise>
</choice>
</flow>
<!-- POST /customers -->
<flow name="post:\customers:application\json:salesforce-sapi-config">
<ee:transform doc:name="Map to Salesforce">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/java
---
[{
Name: payload.name,
Email: payload.email,
Phone: payload.phone,
Website: payload.website,
Industry: payload.industry,
Description: payload.description,
NumberOfEmployees: payload.employees,
BillingStreet: payload.billingAddress.street,
BillingCity: payload.billingAddress.city,
BillingState: payload.billingAddress.state,
BillingPostalCode: payload.billingAddress.postalCode,
BillingCountry: payload.billingAddress.country
}]]]></ee:set-payload>
</ee:message>
</ee:transform>
<salesforce:create config-ref="Salesforce_Config" type="Account" doc:name="Create Account"/>
<ee:transform doc:name="Build Response">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
id: payload[0].id,
success: payload[0].success,
message: if (payload[0].success) "Customer created successfully"
else payload[0].errors[0].message
}]]></ee:set-payload>
</ee:message>
<ee:variables>
<ee:set-variable variableName="httpStatus">
#[if (payload[0].success) 201 else 400]
</ee:set-variable>
</ee:variables>
</ee:transform>
</flow>
</mule>SAP System API
<!-- sap-sapi.xml -->
<flow name="get:\products:sap-sapi-config">
<ee:transform doc:name="Build RFC Parameters">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/java
---
{
I_MATERIAL_TYPE: attributes.queryParams.type default "*",
I_PLANT: attributes.queryParams.plant default "1000",
I_MAX_ROWS: attributes.queryParams.limit default 100
}]]></ee:set-payload>
</ee:message>
</ee:transform>
<!-- Call SAP RFC/BAPI -->
<sap:execute-synchronous-remote-function-call
config-ref="SAP_Config"
functionName="BAPI_MATERIAL_GETLIST"
doc:name="Get Materials">
<sap:content>#[payload]</sap:content>
</sap:execute-synchronous-remote-function-call>
<ee:transform doc:name="Transform to Canonical">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
var materials = payload.MATNRLIST default []
var returnMessages = payload.RETURN default []
---
{
metadata: {
totalRecords: sizeOf(materials),
source: "sap",
plant: attributes.queryParams.plant default "1000"
},
products: materials map {
id: $.MATERIAL,
sku: $.MATERIAL,
name: $.MATL_DESC,
type: $.MATL_TYPE,
group: $.MATL_GROUP,
unit: $.BASE_UOM,
status: $.MATL_STATUS default "active",
plant: $.PLANT
},
(warnings: returnMessages filter ($.TYPE == "W") map $.MESSAGE)
if (sizeOf(returnMessages filter ($.TYPE == "W")) > 0)
}]]></ee:set-payload>
</ee:message>
</ee:transform>
</flow>
<flow name="get:\inventory\{productId}:sap-sapi-config">
<sap:execute-synchronous-remote-function-call
config-ref="SAP_Config"
functionName="BAPI_MATERIAL_STOCK_REQ_LIST"
doc:name="Get Stock">
<sap:content><![CDATA[#[{
MATERIAL: attributes.uriParams.productId,
PLANT: attributes.queryParams.plant default "1000"
}]]]></sap:content>
</sap:execute-synchronous-remote-function-call>
<ee:transform doc:name="Transform Stock Response">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
var stockData = payload.STOCK_REQ_LIST default []
---
{
productId: attributes.uriParams.productId,
plant: attributes.queryParams.plant default "1000",
inventory: {
available: sum(stockData.AVAIL_QTY default [0]),
reserved: sum(stockData.RES_QTY default [0]),
blocked: sum(stockData.BLOCK_QTY default [0]),
inTransit: sum(stockData.TRANSIT_QTY default [0])
},
locations: stockData map {
storageLocation: $.STGE_LOC,
available: $.AVAIL_QTY,
reserved: $.RES_QTY,
unit: $.BASE_UOM
},
lastUpdated: now()
}]]></ee:set-payload>
</ee:message>
</ee:transform>
</flow>Implementarea Process API
Process API-urile orchestreaza procese de business pe mai multe System API-uri:
<!-- order-process-api.xml -->
<flow name="post:\orders:application\json:order-papi-config">
<set-variable variableName="orderRequest" value="#[payload]" doc:name="Store Order Request"/>
<set-variable variableName="orderId" value="#[uuid()]" doc:name="Generate Order ID"/>
<!-- Pasul 1: Valideaza clientul -->
<flow-ref name="validate-customer" doc:name="Validate Customer"/>
<!-- Pasul 2: Verifica stocul -->
<flow-ref name="check-inventory" doc:name="Check Inventory"/>
<!-- Pasul 3: Calculeaza pretul -->
<flow-ref name="calculate-pricing" doc:name="Calculate Pricing"/>
<!-- Pasul 4: Proceseaza plata -->
<flow-ref name="process-payment" doc:name="Process Payment"/>
<!-- Pasul 5: Creeaza inregistrarile comenzii -->
<flow-ref name="create-order-records" doc:name="Create Order Records"/>
<!-- Pasul 6: Rezerva stocul -->
<flow-ref name="reserve-inventory" doc:name="Reserve Inventory"/>
<!-- Pasul 7: Initiaza fulfillment-ul -->
<flow-ref name="initiate-fulfillment" doc:name="Initiate Fulfillment"/>
<!-- Construieste raspunsul -->
<ee:transform doc:name="Build Order Response">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
orderId: vars.orderId,
status: "CONFIRMED",
customer: vars.customerDetails,
items: vars.pricedItems,
totals: vars.orderTotals,
payment: {
transactionId: vars.paymentResult.transactionId,
status: vars.paymentResult.status
},
fulfillment: {
estimatedDelivery: vars.fulfillmentDetails.estimatedDelivery,
trackingAvailable: vars.fulfillmentDetails.trackingAvailable
},
createdAt: now()
}]]></ee:set-payload>
</ee:message>
</ee:transform>
<!-- Error Handler cu compensare -->
<error-handler>
<on-error-propagate type="ANY">
<flow-ref name="compensate-order-failure" doc:name="Compensation"/>
</on-error-propagate>
</error-handler>
</flow>
<sub-flow name="validate-customer">
<http:request
method="GET"
config-ref="Salesforce_SAPI_Config"
path="/customers/{customerId}"
doc:name="Get Customer">
<http:uri-params><![CDATA[#[{
customerId: vars.orderRequest.customerId
}]]]></http:uri-params>
</http:request>
<choice doc:name="Check Customer Status">
<when expression="#[payload.status == 'SUSPENDED']">
<raise-error type="APP:CUSTOMER_SUSPENDED" description="Customer account is suspended"/>
</when>
</choice>
<set-variable variableName="customerDetails" value="#[payload]" doc:name="Store Customer"/>
</sub-flow>
<sub-flow name="check-inventory">
<scatter-gather doc:name="Check All Items">
<route>
<foreach collection="#[vars.orderRequest.items]" doc:name="For Each Item">
<http:request
method="GET"
config-ref="SAP_SAPI_Config"
path="/inventory/{productId}"
doc:name="Check Stock">
<http:uri-params><![CDATA[#[{
productId: payload.productId
}]]]></http:uri-params>
</http:request>
<choice doc:name="Validate Stock">
<when expression="#[payload.inventory.available < vars.rootMessage.payload.quantity]">
<raise-error type="APP:INSUFFICIENT_STOCK"
description='Insufficient stock for $(payload.productId)'/>
</when>
</choice>
</foreach>
</route>
</scatter-gather>
<set-variable variableName="inventoryChecked" value="#[true]" doc:name="Mark Inventory Checked"/>
</sub-flow>
<sub-flow name="process-payment">
<http:request
method="POST"
config-ref="Stripe_SAPI_Config"
path="/payments"
doc:name="Create Payment">
<http:body><![CDATA[#[{
amount: vars.orderTotals.grandTotal,
currency: vars.orderRequest.currency default "USD",
customerId: vars.customerDetails.stripeCustomerId,
paymentMethod: vars.orderRequest.payment.methodId,
description: "Order " ++ vars.orderId,
metadata: {
orderId: vars.orderId,
customerId: vars.orderRequest.customerId
}
}]]]></http:body>
</http:request>
<choice doc:name="Check Payment Result">
<when expression="#[payload.status != 'succeeded']">
<raise-error type="APP:PAYMENT_FAILED" description="#[payload.failureMessage]"/>
</when>
</choice>
<set-variable variableName="paymentResult" value="#[payload]" doc:name="Store Payment Result"/>
</sub-flow>
<sub-flow name="compensate-order-failure">
<!-- Inverseaza plata daca a fost procesata -->
<choice doc:name="Check Payment to Reverse">
<when expression="#[vars.paymentResult != null]">
<http:request
method="POST"
config-ref="Stripe_SAPI_Config"
path="/payments/{paymentId}/refund"
doc:name="Refund Payment">
<http:uri-params><![CDATA[#[{
paymentId: vars.paymentResult.transactionId
}]]]></http:uri-params>
</http:request>
<logger level="INFO" message="Payment reversed: #[vars.paymentResult.transactionId]"/>
</when>
</choice>
<!-- Elibereaza stocul daca a fost rezervat -->
<choice doc:name="Check Inventory to Release">
<when expression="#[vars.inventoryReserved == true]">
<foreach collection="#[vars.orderRequest.items]" doc:name="Release Each Item">
<http:request
method="DELETE"
config-ref="SAP_SAPI_Config"
path="/inventory/reservations/{reservationId}"
doc:name="Release Reservation"/>
</foreach>
<logger level="INFO" message="Inventory reservations released"/>
</when>
</choice>
<!-- Construieste raspunsul de eroare -->
<ee:transform doc:name="Build Error Response">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
orderId: vars.orderId,
status: "FAILED",
error: {
code: error.errorType.identifier,
message: error.description,
compensationApplied: true
}
}]]></ee:set-payload>
</ee:message>
</ee:transform>
</sub-flow>Implementarea Experience API
Experience API-urile adapteaza datele pentru consumatori specifici:
<!-- mobile-experience-api.xml -->
<flow name="get:\dashboard:mobile-eapi-config">
<!-- Apeluri paralele pentru datele dashboard-ului -->
<scatter-gather doc:name="Gather Dashboard Data">
<!-- Comenzi recente -->
<route>
<http:request
method="GET"
config-ref="Order_PAPI_Config"
path="/orders"
doc:name="Get Recent Orders">
<http:query-params><![CDATA[#[{
customerId: vars.userId,
limit: 5,
status: "RECENT"
}]]]></http:query-params>
</http:request>
<set-variable variableName="recentOrders" value="#[payload]"/>
</route>
<!-- Sumar cont -->
<route>
<http:request
method="GET"
config-ref="Customer_PAPI_Config"
path="/customers/{customerId}/summary"
doc:name="Get Account Summary">
<http:uri-params><![CDATA[#[{
customerId: vars.userId
}]]]></http:uri-params>
</http:request>
<set-variable variableName="accountSummary" value="#[payload]"/>
</route>
<!-- Notificari -->
<route>
<http:request
method="GET"
config-ref="Notification_PAPI_Config"
path="/notifications"
doc:name="Get Notifications">
<http:query-params><![CDATA[#[{
userId: vars.userId,
unreadOnly: true,
limit: 10
}]]]></http:query-params>
</http:request>
<set-variable variableName="notifications" value="#[payload]"/>
</route>
<!-- Recomandari -->
<route>
<http:request
method="GET"
config-ref="Recommendation_PAPI_Config"
path="/recommendations"
doc:name="Get Recommendations">
<http:query-params><![CDATA[#[{
userId: vars.userId,
limit: 6
}]]]></http:query-params>
</http:request>
<set-variable variableName="recommendations" value="#[payload]"/>
</route>
</scatter-gather>
<!-- Construieste raspunsul optimizat pentru mobil -->
<ee:transform doc:name="Build Mobile Dashboard">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
// Helper pentru formatarea preturilor pe mobil
fun formatPrice(amount, currency) =
currency ++ " " ++ (amount as String {format: "#,##0.00"})
---
{
user: {
name: vars.accountSummary.name,
avatar: vars.accountSummary.avatarUrl,
memberSince: vars.accountSummary.memberSince as Date as String {format: "MMM yyyy"}
},
// Carduri de comenzi compacte pentru mobil
recentOrders: vars.recentOrders.orders[0 to 2] map {
id: $.orderId,
date: $.createdAt as Date as String {format: "MMM d"},
total: formatPrice($.totals.grandTotal, $.currency),
status: $.status,
statusColor: $.status match {
case "DELIVERED" -> "#4CAF50"
case "SHIPPED" -> "#2196F3"
case "PROCESSING" -> "#FF9800"
else -> "#9E9E9E"
},
itemCount: sizeOf($.items),
thumbnail: $.items[0].imageUrl
},
// Badge-uri de notificari
notifications: {
unreadCount: sizeOf(vars.notifications.items),
items: vars.notifications.items[0 to 4] map {
id: $.id,
title: $.title,
preview: if (sizeOf($.message) > 50)
$.message[0 to 47] ++ "..."
else
$.message,
type: $.type,
icon: $.type match {
case "ORDER" -> "shopping_cart"
case "PROMO" -> "local_offer"
case "ALERT" -> "warning"
else -> "notifications"
},
timestamp: $.createdAt
}
},
// Carduri de produse optimizate pentru grid-ul mobil
recommendations: vars.recommendations.products map {
id: $.productId,
name: if (sizeOf($.name) > 25) $.name[0 to 22] ++ "..." else $.name,
price: formatPrice($.price, "USD"),
originalPrice: if ($.onSale) formatPrice($.originalPrice, "USD") else null,
discount: if ($.onSale) round((1 - $.price / $.originalPrice) * 100) ++ "%" else null,
image: $.images[0].thumbnailUrl,
rating: $.rating,
reviewCount: $.reviewCount
},
// Butoane de actiuni rapide
quickActions: [
{id: "reorder", label: "Reorder", icon: "replay"},
{id: "track", label: "Track Order", icon: "local_shipping"},
{id: "support", label: "Support", icon: "help_outline"},
{id: "deals", label: "Deals", icon: "local_offer"}
],
_meta: {
generatedAt: now(),
cacheControl: "max-age=300"
}
}]]></ee:set-payload>
</ee:message>
</ee:transform>
</flow>API Governance
Stabilirea governance-ului pe toate straturile API:
# api-governance-policy.yaml
governance:
naming_conventions:
system_api:
pattern: "{system-name}-sapi"
examples: ["salesforce-sapi", "sap-sapi", "stripe-sapi"]
process_api:
pattern: "{business-process}-papi"
examples: ["order-papi", "customer-papi", "fulfillment-papi"]
experience_api:
pattern: "{channel}-{domain}-eapi"
examples: ["mobile-commerce-eapi", "web-account-eapi", "partner-orders-eapi"]
versioning:
strategy: "URI path versioning"
format: "/v{major}"
examples:
- "/v1/customers"
- "/v2/customers"
deprecation:
notice_period_days: 90
sunset_header: true
security:
authentication:
system_api: "Client Credentials OAuth 2.0"
process_api: "Client Credentials OAuth 2.0"
experience_api: "Authorization Code OAuth 2.0 or JWT"
policies:
required:
- "Client ID enforcement"
- "Rate limiting"
- "Spike control"
recommended:
- "IP allowlist (system APIs)"
- "JWT validation (experience APIs)"
documentation:
required_elements:
- "API description and purpose"
- "Authentication requirements"
- "Request/response examples"
- "Error codes and handling"
- "Rate limits and quotas"
format: "OAS 3.0"
hosting: "Anypoint Exchange"
sla:
system_api:
availability: "99.5%"
latency_p95: "500ms"
process_api:
availability: "99.5%"
latency_p95: "2000ms"
experience_api:
availability: "99.9%"
latency_p95: "1000ms"Concluzie
API-led connectivity ofera o abordare scalabila si reutilizabila pentru integrarea enterprise. System API-urile abstractizeaza complexitatea backend-ului, Process API-urile orchestreaza logica de business, iar Experience API-urile optimizeaza datele pentru nevoile consumatorilor. Un governance solid asigura consistenta si calitate pe intregul ecosistem de API-uri. Aceasta arhitectura pe straturi permite agilitate mentinand in acelasi timp standardele enterprise.
Sistemul tau AI e conform cu EU AI Act? Evaluare gratuita de risc - afla in 2 minute →