Hello friends,
Today I’d like to briefly introduce my current project:
Microservice CRUD for DBF
This is a lean, modular microservice that reliably performs CRUD operations (Create, Read, Update, Delete) on classic DBF files – without direct file access by clients. Ideal as a bridge between legacy desktop applications and modern web UIs.
If you're interested, you’ll find all the details below – including example code and architecture principles.
Best regards,
Otto
🧩 Project Overview: Microservice Architecture for DBF Files
🎯 Goal
This project provides a lightweight, modular microservice that processes DBF files (dBASE/Xbase) reliably – transaction-like, web-based, and without direct file access by clients.
🔍 Why a Microservice?
Classic desktop programs access DBF files directly from the file system – which leads to well-known issues in multi-user environments:
🔒 File locks don’t work across network boundaries
⚠️ No protection against power outages, faulty clients, or race conditions
💣 Risk of inconsistent data due to concurrent access
The microservice architecture fully decouples this access:
Clients (desktop/web) send requests as .req files
A central microservice exclusively handles processing
➡️ Result: controlled, secure access without file system chaos
🔄 JSON-based Communication: Clear, Portable, Traceable
Communication takes place via .req files in JSON format:
Each change (e.g. editrecKunden_123_1710001234.req) contains:
recordId, columnName, newValue, etc.
The files are processed sequentially and deleted after success.
Advantages:
✅ Traceability
✅ Debug-friendly
✅ Also serves as an audit log
🧠 Hybrid Operation: Desktop & Web in Parallel
This solution is ideal for transition phases between traditional desktop and modern web architectures:
Microservice runs locally or on a server
Desktop apps generate .req files (e.g. via PHP, shell)
Web UIs (e.g. xbrowse) send JSON via HTTP or file bridge
➡️ Legacy software remains – only access is decoupled.
🔐 Technical Features
Locking with .lock and .protector files (transaction-like)
Timed auto-cleanup for crashed sessions
Logging of all actions (*.log)
Optional: heartbeat monitoring & UI integration
Transaction archive (transaction_archive.json) for recovery
🚀 Key Benefits at a Glance
Benefit Effect
Stability No concurrent DBF access
Traceability Changes documented via .req & .log
Safety Robust against crashes & power loss
Compatibility Desktop and web can run in parallel
Migration-Ready Ideal for stepwise migration to web solutions
🛠️ Project Summary for Initial Setup
Purpose:
A lightweight microservice to handle CRUD operations on DBF files in a hybrid desktop-web infrastructure. Communication is asynchronous via .req files in JSON format.
Technology:
Implemented in Harbour / FiveWin
Cyclical polling via TIMER
Power failure-resistant (requests persist)
Extendable with logging, heartbeat, parallel processing, archiving
Typical Use Case:
Middleware between modern web UIs and existing DBF systems – e.g. as a migration or modernization layer.
⚙️ Functional Scope
🔄 Polling via TIMER (e.g. every 1000 ms)
📂 File-based input: IMPORT\*.req
📦 Processing with hb_jsonDecode()
Supported actions:
addrecord
editrecord
deleterecord
✅ After successful processing: FERASE()
🔐 Field changes via FIELDPUT() with locking
📐 Architectural Principles
Strict separation by action type
Atomic Load → Process → Delete
No file system side effects during idle time
🧾 Example Code (Harbour / FiveWin)
Today I’d like to briefly introduce my current project:
Microservice CRUD for DBF
This is a lean, modular microservice that reliably performs CRUD operations (Create, Read, Update, Delete) on classic DBF files – without direct file access by clients. Ideal as a bridge between legacy desktop applications and modern web UIs.
If you're interested, you’ll find all the details below – including example code and architecture principles.
Best regards,
Otto
This project provides a lightweight, modular microservice that processes DBF files (dBASE/Xbase) reliably – transaction-like, web-based, and without direct file access by clients.
Classic desktop programs access DBF files directly from the file system – which leads to well-known issues in multi-user environments:
The microservice architecture fully decouples this access:
Clients (desktop/web) send requests as .req files
A central microservice exclusively handles processing
Communication takes place via .req files in JSON format:
Each change (e.g. editrecKunden_123_1710001234.req) contains:
recordId, columnName, newValue, etc.
The files are processed sequentially and deleted after success.
Advantages:
This solution is ideal for transition phases between traditional desktop and modern web architectures:
Microservice runs locally or on a server
Desktop apps generate .req files (e.g. via PHP, shell)
Web UIs (e.g. xbrowse) send JSON via HTTP or file bridge
Locking with .lock and .protector files (transaction-like)
Timed auto-cleanup for crashed sessions
Logging of all actions (*.log)
Optional: heartbeat monitoring & UI integration
Transaction archive (transaction_archive.json) for recovery
Benefit Effect
Stability No concurrent DBF access
Traceability Changes documented via .req & .log
Safety Robust against crashes & power loss
Compatibility Desktop and web can run in parallel
Migration-Ready Ideal for stepwise migration to web solutions
Purpose:
A lightweight microservice to handle CRUD operations on DBF files in a hybrid desktop-web infrastructure. Communication is asynchronous via .req files in JSON format.
Technology:
Implemented in Harbour / FiveWin
Cyclical polling via TIMER
Power failure-resistant (requests persist)
Extendable with logging, heartbeat, parallel processing, archiving
Typical Use Case:
Middleware between modern web UIs and existing DBF systems – e.g. as a migration or modernization layer.
Supported actions:
addrecord
editrecord
deleterecord
Strict separation by action type
Atomic Load → Process → Delete
No file system side effects during idle time
#include "fivewin.ch"
REQUEST DBFCDX
STATIC oWnd, oTimer
FUNCTION Main()
LOCAL oFont
DEFINE FONT oFont NAME "TAHOMA" SIZE 0, -14
DEFINE WINDOW oWnd FROM 100, 100 TO 800, 800 PIXEL TITLE "Generic CRUD Service"
ACTIVATE WINDOW oWnd ON INIT StartServiceTimer() VALID MsgYesNo("Exit?")
RETURN NIL
FUNCTION StartServiceTimer()
DEFINE TIMER oTimer OF oWnd INTERVAL 1000 ACTION CheckForRequests()
ACTIVATE TIMER oTimer
RETURN NIL
FUNCTION CheckForRequests()
LOCAL aDir := DIRECTORY("IMPORT\*.req", "DHS")
LOCAL cFile, cPath, cData, hReq
LOCAL cAction, cDbf
// 🔐 Sperre prüfen – andere Seite (z. B. PHP) ist noch aktiv
IF FILE( "updaterecord.lock" ) .OR. FILE( "updaterecord_protector.lock" )
RETURN NIL
ENDIF
IF EMPTY( aDir )
RETURN NIL
ENDIF
oTimer:Deactivate()
cFile := aDir[1,1]
cPath := "IMPORT\" + cFile
cData := MEMOREAD( cPath )
hb_jsonDecode( cData, @hReq )
cAction := LOWER( hReq["action"] )
cDbf := hReq["databasePath"]
DO CASE
CASE cAction == "addrecord"
DoAddRecord( cDbf, hReq )
CASE cAction == "editrecord"
DoEditRecord( cDbf, hReq )
CASE cAction == "deleterecord"
DoDeleteRecord( cDbf, hReq )
ENDCASE
FERASE( cPath )
oTimer:Activate()
RETURN NIL
FUNCTION DoAddRecord( cDbf, hData )
USE ( cDbf ) NEW ALIAS DATA SHARED
APPEND BLANK
dbRLock()
FillFieldsFromHash( hData )
dbUnLock()
CLOSE DATA
RETURN NIL
FUNCTION DoEditRecord( cDbf, hData )
LOCAL nRec := VAL( hData["recordId"] )
USE ( cDbf ) NEW ALIAS DATA SHARED
GOTO nRec
IF !EOF()
dbRLock()
FillFieldsFromHash( hData )
dbUnlock()
ENDIF
CLOSE DATA
RETURN NIL
FUNCTION DoDeleteRecord( cDbf, hData )
LOCAL nRec := VAL( hData["recordId"] )
USE ( cDbf ) NEW ALIAS DATA SHARED
GOTO nRec
IF !EOF()
dbRLock()
dbDelete()
dbUnlock()
ENDIF
CLOSE DATA
RETURN NIL
FUNCTION FillFieldsFromHash( h )
LOCAL aStruct := DBSTRUCT()
LOCAL i, cName, xVal
FOR i := 1 TO LEN( aStruct )
cName := aStruct[i,1]
IF h["columnName"] == cName
xVal := h["newValue"]
FIELDPUT( i, xVal )
ENDIF
NEXT
RETURN NIL