## Summary Remove implementation-detail methods from the `AICoreService` interface. Consolidate duplicated logic between `AICoreServiceImpl` and `MockAICoreServiceImpl` into a shared utility. ## Problem The `AICoreService` interface currently exposes internal state and implementation concerns: ```java Retry getRetry(); // Cross-module: KEEP String getDefaultResourceGroup(); // Internal only String getResourceGroupPrefix(); // Internal only Map<String, String> getTenantResourceGroupCache(); // Internal only Map<String, String> getResourceGroupDeploymentCache(); // Internal only void clearTenantCache(String tenantId); // Internal only String resolveResourceGroupFromKeys(Map<String, Object> keys); // Internal only ``` In `cds-services`, service interfaces (`AuditLogService`, `MessagingService`, `PersistenceService`, etc.) **never** expose mutable caches, configuration values, or resolution utilities. These are implementation details accessed via the concrete type. Additionally, `resolveResourceGroupFromKeys()` is **identically duplicated** between `AICoreServiceImpl` and `MockAICoreServiceImpl`. ## Plan ### Step 1: Slim the Interface Keep only domain-level API on `AICoreService`: ```java public interface AICoreService extends CqnService { String DEFAULT_NAME = "AICore"; String RESOURCE_GROUPS = "AICore.resourceGroups"; String DEPLOYMENTS = "AICore.deployments"; String CONFIGURATIONS = "AICore.configurations"; String resourceGroupForTenant(String tenantId); String deploymentId(String resourceGroupId, ModelDeploymentSpec spec); ApiClient inferenceClient(String resourceGroupId, String deploymentId); boolean isMultiTenancyEnabled(); Retry getRetry(); // Cross-module consumer: RecommendationConfiguration } ``` ### Step 2: Keep Methods on Implementations Methods removed from the interface remain as `public` methods on `AICoreServiceImpl` / `MockAICoreServiceImpl` (remove `@Override`). Handlers already receive the concrete type. ### Step 3: Extract Shared Logic Create `AICoreUtils`: ```java final class AICoreUtils { static String resolveResourceGroupFromKeys(Map<String, Object> keys, String fallback) { if (keys.containsKey("resourceGroup_resourceGroupId")) return (String) keys.get("resourceGroup_resourceGroupId"); Object rgObj = keys.get("resourceGroup"); if (rgObj instanceof Map<?, ?> rgMap && rgMap.containsKey("resourceGroupId")) return (String) rgMap.get("resourceGroupId"); return fallback; } } ``` ### Step 4: Update Tests Tests currently access these methods through the `AICoreService` interface. Update to use `AICoreServiceImpl` directly (the tests already have access to the concrete type via injection/ServiceCatalog). ## Caller Analysis | Method | Cross-Module? | Same-Module Callers | |--------|:---:|---| | `getRetry()` | Yes (`RecommendationConfiguration`, sample) | — | | `getDefaultResourceGroup()` | No | `ResourceGroupHandler`, tests | | `getResourceGroupPrefix()` | No | tests | | `getTenantResourceGroupCache()` | No | `AICoreSetupHandler`, tests | | `getResourceGroupDeploymentCache()` | No | tests | | `clearTenantCache()` | No | `AICoreSetupHandler`, `MockAICoreSetupHandler`, tests | | `resolveResourceGroupFromKeys()` | No | `AbstractCrudHandler`, tests | ## Files - `AICoreService.java` (remove 6 methods) - `AICoreServiceImpl.java` (remove 6 `@Override`) - `MockAICoreServiceImpl.java` (remove 6 `@Override`) - **New:** `AICoreUtils.java` - ~12 test files (update to use concrete type) ## Risk Breaking change if external consumers call these methods through the interface. Mitigated by: this is an internal plugin, the methods serve no external use case, and the CDS model defines the real public contract.