FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin for Harbour/xHarbour Microservice Architecture for DBF Files
Posts: 6983
Joined: Fri Oct 07, 2005 07:07 PM
Microservice Architecture for DBF Files
Posted: Sun Jun 29, 2025 07:43 AM
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)
#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
Posts: 6983
Joined: Fri Oct 07, 2005 07:07 PM
Re: Microservice Architecture for DBF Files
Posted: Mon Jun 30, 2025 06:54 AM
Here you can see the direct speed comparison of a DBF table with 1,200,000 entries.
In both cases, the file is opened with a dBASE-compatible editor and sorted by the "STRASSE" field.

Directly on the server: 6 seconds
Over the network: 21 seconds

This exact performance difference is what the microservice concept addresses.
By offloading processing to the server, network bottlenecks and file locking issues are avoided.

Continue the discussion