Hello friends,
Is anyone already using Harbour/FiveWin this way — with SQL moved behind a microservice?From what I can see, this approach seems to simplify the client side significantly by removing SQL client DLLs, network/TLS handling, and tight database coupling from the UI process.
--- A short note on performance (before architecture)When the UI, the service, and the SQL database run on the same machine, local IPC (socket / JSON / HTTP over localhost) is very fast.
Based on practical measurements and typical setups, the service layer usually adds:
- 1–2 ms per request for JSON encode/decode and local IPC
A simple SQL query itself typically takes:
- 1–10 ms (often more, depending on indexes and data size)
In other words:
The service layer usually adds less than 10–20% of the total request time.
The SQL query itself remains the dominant cost.
In practice, the performance impact is often negligible,
while the gain in stability and isolation is very noticeable.
In the past, embedding SQL directly into the Harbour application was often unavoidable:
- no permanently running web servers
- no lightweight local services
- no clean separation between UI, business logic, and data access
In short, the client process itself effectively had to be the server.
--- What has changedToday the situation looks very different:
- services and web servers run continuously
- local IPC is cheap and reliable
- SQL servers already run in their own processes
- expectations for stability and fault isolation are much higher
This opens a new option:
--- The idea in one sentenceUse SQL without embedding SQL clients into the UI process.
The client talks JSON to a service.
The service talks SQL to the database.
As a result:
- the client loads no SQL DLLs
- the client does not know table schemas
- SQL connectivity is no longer part of the UI runtime
Code: Select all | Expand
USE kunden
GO TOP
DO WHILE !EOF()
? name, ort
SKIP
ENDDOCode: Select all | Expand
hRes := ServiceCall( "kunden_list", { "limit" => 10 } )
FOR EACH hRow IN hRes["rows"]
? hRow["name"], hRow["ort"]
NEXTSame usage pattern.
Different responsibility.
Code: Select all | Expand
FUNCTION ServiceCall( cAction, hData )
LOCAL hReq := { ;
"action" => cAction, ;
"data" => hData ;
}
RETURN hb_jsonDecode( SendToService( hb_jsonEncode( hReq ) ) )Code: Select all | Expand
FUNCTION SendToService( cJson )
LOCAL oSock := hb_socketOpen()
hb_socketConnect( oSock, "127.0.0.1", 9001 )
hb_socketSend( oSock, cJson + hb_eol() )
LOCAL cRes := hb_socketRecv( oSock, 8192 )
hb_socketClose( oSock )
RETURN cResThis is the entire client-side SQL interface.
--- What runs where Client (Harbour / FiveWin)- UI
- business orchestration
- JSON in / JSON out
- SQL execution
- prepared statements
- transactions
- error handling
- SQLite / PostgreSQL / MySQL
SQL lives behind the service boundary.
--- What disappears from the client processWith a service-based approach, the following no longer need to be loaded into the UI process:
Database client DLLs- libmysql.dll / libmysqlclient.dll
- MariaDB client libraries
- libpq.dll (PostgreSQL)
- ODBC drivers (odbc32.dll and vendor-specific drivers)
- libssl*.dll
- libcrypto*.dll
- parts of schannel.dll / bcrypt.dll (DB-related)
- additional vcruntime*.dll required by DB clients
- extra C/C++ runtime layers pulled in by SQL libraries
- zlib1.dll
- character set / ICU libraries (depending on the DB client)
What remains in the client:
- Harbour runtime
- FiveWin UI
- minimal socket/HTTP code
- JSON handling
- no SQL DLLs in the client
- no OpenSSL, ODBC, or DB runtimes in the UI process
- fewer ABI and runtime conflicts
- crashes are isolated by process boundaries
This does not add complexity —
it moves complexity out of the wrong place.
- Harbour works naturally with hashes and JSON
- DBF-style access maps well to service calls
- SQLite feels similar to DBF (single file, easy backup)
- migration can be done step by step
For example:
Code: Select all | Expand
USE kunden → ServiceCall( "kunden_list" )The classic all-in-one client approach solved SQL at a time when the client effectively had to be the server and service-based isolation was hard or unavailable.
That constraint is largely gone today.
What remains is an opportunity to separate concerns more clearly:
- UI and orchestration in the client
- SQL and connectivity in a service
The all-in-one approach already solves SQL —
but today we finally have the option to solve it somewhere else.
That option simply did not exist when these integrations were originally designed.
--- Question to the communityIs anyone already using Harbour/FiveWin this way in production?
If so, I’d be very interested in hearing about real-world experiences — both positive and negative.
Best regards,
Otto