Hydrology XData Flow

Request flow through Delphi 12.x + TMS/XData + UniDAC + Aurelius stack.

High-Level Architecture

1. XData Service Contract

The interface defines the model and service name that form the route prefix:

[ServiceContract]
[ServiceName('HydrologyService')]
[Model('HydrologyModel')]
IHydrologyService = INTERFACE(IInvokable)
   FUNCTION ImAlive: STRING;
   FUNCTION GetStationDataBySelection(DashboardNumber, CardNumber, 
            Period, PeriodUnit: integer): TList<TStationDTO>;
END;
Routing: /<base>/HydrologyService/<MethodName>

2. Service Registration

// in Services.body.pas
RegisterServiceType(THydrologyService);

3. TRootService: Tenant Resolution

TRootService.DoSetup is invoked per request:

  1. Derives tenant from URL
  2. Ensures tenant's TTenantDatabaseModule exists
  3. Connects DataManager to tenant's TUniConnection
  4. Registers stored procedures

4. Business Layer

The request lands with a fully prepared IDataManager:

FUNCTION TStations.GetStationDataBySelection(...): TList<TStationDTO>;
BEGIN
   DataManager.LoadProc('GetStationDataBySelectionProc');
   // Execute and map to DTOs
END;

5. Stored Procedure Registry

PROCEDURE TQueriesModule.RegisterStationProcedures;
BEGIN
   DataManager.ProcedureList.Add('GetStationDataBySelectionProc', 
                                  GetStationDataBySelectionProc);
END;
Naming matters: The key must match exactly what the business object uses in LoadProc().

6. Full Request Lifecycle

  1. Client POSTs to /hydrology/HydrologyService/GetStationDataBySelection
  2. Sparkle receives on TIndySparkleHTTPServer
  3. Routing matches hydrology base to XData module
  4. XData resolves service and method
  5. TRootService.DoSetup wires tenant DataManager
  6. Business object uses LoadProc(), executes
  7. DTOs returned as JSON

7. "Unknown Path" Failures

LayerWhat must be trueWhat breaks
HTTP → ModuleXData module mounted at /hydrologyPlugin reload leaves module detached
XData → ServiceService registeredDifferent module instance
VerbPOST requestsGET retry fails
Base pathClient uses correct pathPath changes after reload

8. Plugin Mount Code

In Plugin.XDataServer.pas:

FUNCTION TXDataServerModule.SetupXDataServer: Boolean;
BEGIN
   XDataServer.BaseURL   := '/empire/' + PluginInstance.NameSpace;
   XDataServer.ModelName := PluginInstance.ModelName;
   HttpServerModule      := XDataServer.CreateModule;
   IF Assigned(HttpServerModule) THEN
      Appserver.AddModule(HttpServerModule);
END;

If AddModule is skipped or temporarily undone during reload, requests will fail with "Unknown path".