FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin for Harbour/xHarbour This is the code behind that community post. You know the one.
Posts: 6983
Joined: Fri Oct 07, 2005 07:07 PM
This is the code behind that community post. You know the one.
Posted: Thu Mar 26, 2026 07:54 AM

Hello friends,

Reading Benjamin's list made me think. Not because the projects aren't impressive – they are. But because most of us have built similar things, separately, and never connected the dots.

So instead of another list of projects, here is a pattern that came out of the last few months of work. Something everyone here can actually use.


---

The core idea: one adapter, one service

Most of us are connecting FiveWin to external systems – Oracle, SAT, ContPAQ, REST APIs of all kinds. And most of us are solving that problem differently every single time.

What if we had a common base?

The architecture is simple:

FiveWin (UI + Business Logic)
    ↓
Universal JSON Adapter
    ↓
Microservice (your own small Harbour server)
    ↓
External systems: Oracle, SAT, APIs, DB...

Two parts. Clear responsibilities.


---

Part 1: The JSON Adapter (FiveWin side)

The adapter has one job: talk JSON, know endpoints, nothing else.

Code (harbour): Select all Collapse
// -----------------------------------------------------------------------
// Universal JSON Adapter – FiveWin side
// -----------------------------------------------------------------------
FUNCTION ApiRequest( cEndpoint, hData )
   LOCAL hConfig, cUrl, cResponse

   // Load endpoint config from JSON file
   hConfig  = LoadEndpoint( cEndpoint )
   cUrl     = hConfig["base_url"] + hConfig["path"]

   // HTTP call (WinHttp / Curl / whatever you use)
   cResponse = HttpPost( cUrl, hb_jsonEncode( hData ), hConfig["headers"] )

   RETURN hb_jsonDecode( cResponse )

RETURN NIL

// -----------------------------------------------------------------------
// Example: Create an order
// -----------------------------------------------------------------------
FUNCTION CreateOrderInERP()
   LOCAL hOrder, hResponse, cOrderId

   hOrder = hb_Hash()
   hOrder["order_number"] = "INV-2026-0001"
   hOrder["customer_id"]  = 12345
   hOrder["total"]        = 299.99
   hOrder["currency"]     = "EUR"

   hOrder["items"] = { ;
      { "sku" => "A100", "qty" => 1, "price" => 199.99 }, ;
      { "sku" => "B200", "qty" => 2, "price" => 50.00 } ;
   }

   hResponse = ApiRequest( "orders_create", hOrder )

   IF ! Empty( hResponse )
      cOrderId = hb_HGet( hResponse, "id" )
      MsgInfo( "Order created: " + cOrderId, "Success" )
   ELSE
      MsgStop( "Transfer failed", "Error" )
   ENDIF

RETURN NIL

FiveWin knows nothing about Oracle. Nothing about SAT. Just: send this, get that back.


---

Part 2: The Endpoint configuration

Instead of hardcoding URLs and auth into your code, define each endpoint in a simple JSON file:

Code (json): Select all Collapse
{
  "name":     "orders_create",
  "base_url": "http://localhost:8080",
  "path":     "/api/orders",
  "method":   "POST",
  "headers":  { "Authorization": "Bearer YOUR_TOKEN" },
  "timeout":  30
}

Three practical options for managing these configs:

Hash in code – fast to start, not scalable. Fine for version 1.

JSON files (recommended to start) – one file per endpoint: oracle_orders.json, sat_diot.json, oxxo.json. Easy to change without recompile. AI can generate them directly.

Database – dynamic, multi-user, more complex. A topic for phase 2.


---

Part 3: The Microservice (Harbour server side)

This is where things get interesting. The microservice is not a big system. It is a small Harbour server that exposes clean endpoints and handles all the complexity that does not belong in FiveWin.

Code (harbour): Select all Collapse
// -----------------------------------------------------------------------
// Mini Harbour Microservice – example endpoints
// -----------------------------------------------------------------------
FUNCTION HandleRequest( cPath, cMethod, cBody )
   LOCAL hData := hb_jsonDecode( cBody )

   DO CASE
   CASE cPath == "/api/health"
      RETURN JsonOk( { "status" => "ok" } )

   CASE cPath == "/api/orders" .AND. cMethod == "POST"
      RETURN ProcessOrder( hData )

   CASE cPath == "/api/diot" .AND. cMethod == "POST"
      RETURN ProcessDiot( hData )

   OTHERWISE
      RETURN JsonError( "Unknown endpoint" )
   ENDCASE

RETURN NIL

// -----------------------------------------------------------------------
FUNCTION ProcessOrder( hData )
   // Here: connect to Oracle, ContPAQ, whatever you need
   // FiveWin never sees this complexity
   LOCAL hResult := hb_Hash()
   hResult["id"]     = "ORD-001"
   hResult["status"] = "created"
RETURN JsonOk( hResult )

// -----------------------------------------------------------------------
FUNCTION JsonOk( hData )
   hData["success"] = .T.
RETURN hb_jsonEncode( hData )

FUNCTION JsonError( cMsg )
RETURN hb_jsonEncode( { "success" => .F., "error" => cMsg } )

The microservice handles: business logic, API integration, authentication, data transformation. FiveWin handles none of that. It just calls ApiRequest() and gets an answer.


---

Why this matters – and why Functions, not Classes

Notice what is not here: no class hierarchy, no factory, no inheritance chain. Just functions with clear inputs and outputs.

That is intentional. For this kind of integration work, functions are the right tool:

  • You see exactly what goes in and comes out
  • AI generates them reliably and correctly
  • You can open the code two years later and still understand it
  • The microservice pattern works because each function does one thing

Classes have their place. But for connecting systems, moving data, and building integrations – functions win every time.


---

The connection to AI

This architecture works especially well with AI assistance because:

Ask ChatGPT or Claude: "generate a JSON endpoint config for Oracle order API" β†’ you get a working file in seconds.

Ask ChatGPT or Claude: "write a Harbour function that processes a DIOT request" β†’ clean, testable function, immediately usable.

The adapter stays the same. Only the endpoint config and the service logic change per project. That is the real productivity gain – not magic, just a reusable structure.


---

One more thought – where this architecture leads

Once the adapter and microservice are in place, something interesting becomes possible.

Right now, FiveWin calls a service and gets data back. Clean, simple, useful. But the next logical step in this architecture is not just getting data – it is getting reasoning.

Instead of:

FiveWin β†’ asks for data β†’ gets answer

imagine:

FiveWin β†’ describes a situation β†’ service thinks β†’ returns decision

The microservice does not just fetch an Oracle record. It reasons about it. Checks availability, cross-references bookings, weighs options – and returns a structured answer that FiveWin can act on directly.

The technical foundation for this already exists in the community. The jump from "adapter calls API" to "adapter calls reasoning engine" is smaller than it looks – and the same JSON structure handles both.

For our kind of business software – hotel systems, ERP, inventory, billing – this would change what "integration" means entirely.

Worth a separate thread when we have the adapter basics sorted.


---

What I would find interesting to discuss here

We all have experience with pieces of this. Oracle connections, SAT integrations, REST calls. Nobody has put it together as a shared base yet.

Some open questions worth discussing:

  • How are others handling authentication (Bearer tokens, OAuth) in Harbour?
  • JSON file vs. database for endpoint configs – what works in practice?
  • How does this connect to the reasoning loop work already happening in the community?

Happy to share more code if there is interest.

Drop your experiences below – sharing the patterns behind them benefits all of us, especially by learning from each other’s experience.

Best regards,
Otto

Continue the discussion