Context
The AICore service is currently implemented as a plain CqnService registered via CdsRuntimeConfiguration SPI, with an AICoreApplicationServiceHandler ("delegation handler") that intercepts CRUD events on all application services to automatically forward queries targeting projections on AICore.* entities.
This should be migrated to the standard RemoteService pattern used by CAP Java for services backed by external systems (see cds-services-impl RemoteServiceImpl / AbstractCdsDefinedService).
Problems with the Current Approach
Delegation Handler (AICoreApplicationServiceHandler)
The delegation handler at cds-feature-ai-core/.../handler/AICoreApplicationServiceHandler.java introduces significant issues:
- Wildcard registration —
@ServiceName(value = "*", type = ApplicationService.class) intercepts every single CRUD event for every entity across the entire application
- Runtime check overhead — Every request must resolve whether the entity is a projection on
AICore.* before deciding to act or return
- Incomplete coverage — Only handles READ/CREATE/UPDATE/DELETE; actions (like
stop()) are not forwarded
- Handler ordering conflicts —
@HandlerOrder(OrderConstants.On.FEATURE) competes with other feature-level handlers (e.g., change-tracking, audit-log)
- Fragile CQN rewriting — The
entityModifier pattern rewrites only the root segment; nested associations, expands, and complex projections (multi-level, renamed elements) can break silently
- Non-obvious "magic" — Developers don't see explicit forwarding in their code, making debugging difficult when things go wrong
- Draft interaction — If someone creates a draft-enabled projection on AICore entities, the delegation handler doesn't account for active/draft entity pairs
Service Type Mismatch
The AICoreService extends CqnService directly but functionally acts as a remote service (it proxies REST API calls to SAP AI Core). By not using RemoteService, it misses:
- Built-in projection resolution (
AbstractCdsDefinedService.runProjected() uses ProjectionResolver to automatically resolve queries through projection layers)
- Entity validation (ensures entities belong to the service before dispatching)
- Standard service lifecycle integration (Before/On/After dispatching with proper ordering)
- Outbox compatibility (
outboxService.outboxed(remoteService))
- Messaging integration (auto-register inbound handlers for declared events)
- DI discoverability — Cannot be injected as
@Autowired RemoteService with standard patterns
Proposed Changes
1. Make AICoreService extend RemoteService
public interface AICoreService extends RemoteService {
String DEFAULT_NAME = "AICore";
// Keep programmatic helpers on the same interface
String resourceGroup();
String deploymentId(String resourceGroupId, ModelDeploymentSpec spec);
ApiClient inferenceClient(String resourceGroupId, String deploymentId);
}
2. Implementation extends RemoteServiceImpl (or equivalent base)
The implementation gains automatic projection resolution from AbstractCdsDefinedService:
- Queries like
Select.from("AICore.deployments") dispatched directly work unchanged
- Queries through projections are resolved automatically via
ProjectionResolver before handler dispatch
3. Delete AICoreApplicationServiceHandler (delegation handler)
Users who want to expose AICore entities via their own ApplicationService must write an explicit forwarding handler:
// User's own handler — explicit, debuggable, customizable
@Component
@ServiceName("MyAdminService")
public class AIForwardHandler implements EventHandler {
@Autowired @Qualifier("AICore")
AICoreService aiCore;
@On(entity = "MyAdminService.Deployments")
public void forwardDeploymentRead(CdsReadEventContext ctx) {
ctx.setResult(aiCore.run(ctx.getCqn()));
}
}
This is the standard CAP pattern for forwarding to remote services (see ForwardHandler in cds-services/integration-tests/remote/). It is explicit, type-safe, and allows custom logic (filtering, authorization, mapping).
4. Register the service as a RemoteService
Update AICoreServiceConfiguration.services() to register the service such that the CAP runtime recognizes it as a RemoteService in the ServiceCatalog.
5. Update entity handlers registration
DeploymentHandler, ResourceGroupHandler, ConfigurationHandler, and ActionHandler should be registered against the AICore service specifically (either via @ServiceName("AICore") or @ServiceName(value = "*", type = RemoteService.class) with AICore-type filtering), rather than being wired through the service impl directly.
Usage
Before (implicit delegation — just works but fragile)
// app/srv/admin-service.cds
service AdminService {
entity Deployments as projection on AICore.deployments;
}
No Java code needed — the delegation handler magically forwarded queries.
After (explicit forwarding — standard CAP pattern)
// app/srv/admin-service.cds
service AdminService {
entity Deployments as projection on AICore.deployments;
}
@Component
@ServiceName("AdminService")
public class AdminServiceHandler implements EventHandler {
@Autowired @Qualifier("AICore")
AICoreService aiCore;
@On(entity = "AdminService.Deployments")
public Result onReadDeployments(CdsReadEventContext ctx) {
return aiCore.run(ctx.getCqn());
}
}
Benefits
- Standard CAP architecture — same pattern as S/4HANA, SuccessFactors, or any remote OData consumption
- No wildcard handler intercepting all application services
- Explicit control — consumers decide what to expose and how
- Actions/functions can be forwarded selectively
- Works with outbox, messaging, standard DI
- Projection resolution handled by the framework, not custom code
- Easier to test (mock the RemoteService interface)
Related Issues
Context
The
AICoreservice is currently implemented as a plainCqnServiceregistered viaCdsRuntimeConfigurationSPI, with anAICoreApplicationServiceHandler("delegation handler") that intercepts CRUD events on all application services to automatically forward queries targeting projections onAICore.*entities.This should be migrated to the standard
RemoteServicepattern used by CAP Java for services backed by external systems (seecds-services-implRemoteServiceImpl/AbstractCdsDefinedService).Problems with the Current Approach
Delegation Handler (
AICoreApplicationServiceHandler)The delegation handler at
cds-feature-ai-core/.../handler/AICoreApplicationServiceHandler.javaintroduces significant issues:@ServiceName(value = "*", type = ApplicationService.class)intercepts every single CRUD event for every entity across the entire applicationAICore.*before deciding to act or returnstop()) are not forwarded@HandlerOrder(OrderConstants.On.FEATURE)competes with other feature-level handlers (e.g., change-tracking, audit-log)entityModifierpattern rewrites only the root segment; nested associations, expands, and complex projections (multi-level, renamed elements) can break silentlyService Type Mismatch
The
AICoreServiceextendsCqnServicedirectly but functionally acts as a remote service (it proxies REST API calls to SAP AI Core). By not usingRemoteService, it misses:AbstractCdsDefinedService.runProjected()usesProjectionResolverto automatically resolve queries through projection layers)outboxService.outboxed(remoteService))@Autowired RemoteServicewith standard patternsProposed Changes
1. Make
AICoreServiceextendRemoteService2. Implementation extends
RemoteServiceImpl(or equivalent base)The implementation gains automatic projection resolution from
AbstractCdsDefinedService:Select.from("AICore.deployments")dispatched directly work unchangedProjectionResolverbefore handler dispatch3. Delete
AICoreApplicationServiceHandler(delegation handler)Users who want to expose AICore entities via their own ApplicationService must write an explicit forwarding handler:
This is the standard CAP pattern for forwarding to remote services (see
ForwardHandlerincds-services/integration-tests/remote/). It is explicit, type-safe, and allows custom logic (filtering, authorization, mapping).4. Register the service as a RemoteService
Update
AICoreServiceConfiguration.services()to register the service such that the CAP runtime recognizes it as aRemoteServicein theServiceCatalog.5. Update entity handlers registration
DeploymentHandler,ResourceGroupHandler,ConfigurationHandler, andActionHandlershould be registered against the AICore service specifically (either via@ServiceName("AICore")or@ServiceName(value = "*", type = RemoteService.class)with AICore-type filtering), rather than being wired through the service impl directly.Usage
Before (implicit delegation — just works but fragile)
No Java code needed — the delegation handler magically forwarded queries.
After (explicit forwarding — standard CAP pattern)
Benefits
Related Issues