nexgate/orchestrator
the orchestrator module, member of GSF-nexgate ZUGFeRD family.
The orchestrator module is the central control unit and main entry point for Go applications and microservices using NexGate.
It manages the high-level invoice generation lifecycle, transforms incoming JSON payloads or RPC network requests into
internal domain models, resolves multi-tenant configurations, stages binary assets, and delegates processing to
specialized writer engines.
Core Responsibilities
- Configuration Management: Initializes system parameters (
system.yaml) and tenant settings (seller.yaml).
- Payload Validation & Preprocessing: Transforms incoming JSON payloads into the strongly typed
MasterZugFerd domain structure. For WebService requests, an automated Preprocessor transparently stages Base64-encoded files or remote HTTP URLs into isolated temporary workspaces.
- Pipeline Execution: Coordinates rendering steps across engines (e.g., XML generation $\rightarrow$ PDF layout $\rightarrow$ ZUGFeRD PDF injection).
- Engine Delegation: Uses
api/writer factory logic to dynamically instantiate and invoke required engines (native vs. container).
- P2P / WebService Exposure: Exposes high-performance JSON-RPC 2.0 endpoints over WebSockets for distributed or cross-language worker environments.
Module Directory Structure
orchestrator/
├── config/ # Configuration loaders (system.yaml, seller.yaml, environment paths)
├── internal/
│ └── preprocessor/ # File staging, Base64 decoding, and URL hydration for RPC inputs
├── orchestrator.go # Core Orchestrator struct, constructors, and RunJsonJob entry point
├── webservice.go # WebSocket / JSON-RPC server setup (AsWebService)
├── webserviceHandler.go # RPC method implementations (nexgate.process, nexgate.echo)
├── generateBase.go # Pipeline step: Pure XML / PAR generation
├── generatePdf.go # Pipeline step: Layout PDF generation via LibreOffice
├── generateZugferd.go # Pipeline step: Hybrid ZUGFeRD / Factur-X assembly via Mustang
└── orchestrator_test.go # Comprehensive integration test suite (local & service mode)
Architecture & Data Flow
The orchestrator abstracts the complex multi-step rendering process behind a clean, single-method API (RunJsonJob).
+-----------------------------------+ +-----------------------------------+
| In-Process Call: RunJsonJob() | | Remote JSON-RPC Call: |
+-----------------+-----------------+ | nexgate.process (via WebSocket) |
| +-----------------+-----------------+
| |
| v
| +-----------------------------------+
| | Preprocessor & Asset Staging |
| | (Decodes Base64 / Downloads URLs) |
| +-----------------+-----------------+
| |
+--------------------+--------------------+
|
v
+---------------------------------------+
| 1. Parse JSON -> MasterZugFerd Struct |
+-------------------+-------------------+
|
v
+---------------------------------------+
| 2. Resolve Tenant & System Configs |
+-------------------+-------------------+
|
+---------------------+---------------------+
| | |
v v v
+--------------------+ +--------------------+ +--------------------+
| generateBase() | | generatePdf() | | generateZugferd() |
| (XML Generation) | | (LibreOffice PDF) | | (Mustang Assembly) |
+----------+---------+ +----------+---------+ +----------+---------+
| | |
+----------------------+---------------------+
|
v
+---------------------------------------+
| 3. Finalize Output & Return OutputPath |
+---------------------------------------+
WebService & RPC Integration
The orchestrator can be booted as a standalone WebService powered by nexutils/p2p/rpc (WebSocket JSON-RPC 2.0).
Starting the WebService
package main
import (
"context"
"time"
"nexgate/orchestrator"
)
func main() {
ctx := context.Background()
envRoot := "/opt/nexgate-env"
addr := "127.0.0.1:8080"
heartbeat := 2 * time.Second
// Boots the RPC Node listening for incoming WebSocket connections
if err := orchestrator.AsWebService(ctx, envRoot, addr, heartbeat); err != nil {
panic(err)
}
}
Registered RPC Handlers
nexgate.process: Main processing endpoint. Accepts job specifications, stages files, runs the pipeline, and returns Base64-encoded output DTOs.
nexgate.echo: Diagnostic health check endpoint.
Connecting via RPC Client (with Custom Read/Write Limits)
Because binary files (PDFs, attachments) are transmitted over WebSocket, clients should configure the node's WriteReadLimit (default is 1 MB):
import "nexutils/p2p/rpc"
clientNode := rpc.NewNode(rpc.Options{
Addr: "127.0.0.1:8081",
HeartbeatInterval: 2 * time.Second,
WriteReadLimit: 10 * 1024 * 1024, // 10 MB limit for large document payloads
})
peer, err := clientNode.ConnectToPeer("127.0.0.1:8080")
if err != nil {
log.Fatalf("Connection failed: %v", err)
}
Job Payload Specification (invoice.json)
The orchestrator consumes JSON payloads. Over direct RunJsonJob calls, file properties expect local file paths. Over nexgate.process RPC calls, file properties accept Inline Base64 Data Objects or HTTP URLs, which the Preprocessor automatically materializes.
Payload Structure Overview
| Section |
Mandatory? |
Description |
mandant.seller |
No |
Overrides target tenant folder (e.g., "testSeller" $\rightarrow$ env/sellers/testSeller). |
options.queue |
No |
Target pipeline queue ("pdf", "facturx", "base"). Defaults to "zugferd". |
options.template |
No |
Path, URL, or Base64 object for a custom LibreOffice template (.ott). |
options.attachments |
No |
Array of file paths, Base64 objects, or URLs to embed into the ZUGFeRD PDF. |
invoice.currency |
Yes |
ISO 4217 currency code (e.g., "EUR"). |
invoice.buyer |
Yes |
Buyer address and metadata. |
invoice.items |
Yes |
Array of line items (quantity, net price, tax rates). |
invoice.vats |
Yes |
Grouped VAT totals required for tax compliance. |
invoice.totals |
Yes |
Calculated totals (line_total_amount, tax_total_amount, grand_total_amount). |
Payload Examples
In-Process / File-Path Payload (min-invoice.json)
Used for direct orch.RunJsonJob() execution on local filesystems:
{
"invoice": {
"currency": "EUR",
"invoice_id": "RE-0814-min",
"issue_date": "20260105",
"service_date": "20260104",
"buyer": {
"id": "471115",
"name1": "Franz Fröhlich AG",
"street": "Musterstrasse 64",
"zip": "01221",
"city": "Musterdorf",
"country": "Deutschland"
},
"items": [
{
"pos": 1,
"description": "Go Entwicklungsservice",
"quantity": 10.5,
"unit": "Stunde",
"net_price": 95.00,
"line_total": 997.5,
"tax_rate": 19
}
],
"vats": [
{
"pos": 1,
"id": "S",
"description": "USt",
"vat_base": 997.50,
"vat_amount": 189.53,
"tax_rate": 19
}
],
"totals": {
"line_total_amount": 997.50,
"tax_total_amount": 189.53,
"grand_total_amount": 1187.03
}
}
}
RPC / Base64-Inline Payload (max-rpc-invoice.json)
Used for nexgate.process RPC calls where client files are transmitted in-payload:
{
"mandant": {
"seller": "testSeller"
},
"options": {
"queue": "combine",
"pdf": {
"name": "test_combine_invoice.pdf",
"data": "JVBERi0xLj...=="
},
"factur-x": {
"name": "test_combine_invoice.xml",
"data": "PD94bWwgdmVyc2lvbj0iMS4wI...=="
},
"attachments": [
{
"name": "test_attachment.png",
"data": "iVBORw0KGgoAAAANSUhEUg...=="
}
]
},
"invoice": {
"currency": "EUR",
"invoice_id": "RE-0815-combine-mit",
"buyer": { ... },
"items": [ ... ],
"vats": [ ... ],
"totals": { ... }
}
}
API Entry Points
// 1. Initialize Orchestrator with default environment resolution
orch, err := orchestrator.NewDefault()
// 2. Execute a complete invoice rendering pipeline in-process
result, err := orch.RunJsonJob(jsonBytes)
// 3. Alternatively, launch as a non-blocking WebSocket service
err = orchestrator.AsWebService(ctx, envRoot, "127.0.0.1:8080", 2*time.Second)
Integration & WebService Testing
The orchestrator contains a comprehensive integration test suite verifying both in-process engine pipelines and networked WebService execution.
| Test File |
Engine / Target |
Description |
orchestrator_test-suite_native_test.go |
NATIVE |
Direct execution without containers or network overhead. |
orchestrator_suite_test.go |
NATIVE & CONTAINER |
Full integration suite with Docker/Podman container isolation. |
orchestrator_webservice_test.go |
WEBSERVICE |
Spawns background RPC server, transmits Base64 payloads over WebSockets, and verifies staged PDF execution. |
1. Native Testing (Fast & Lightweight, Recommended for Getting Started)
If you do not have Docker or Podman installed—or simply want a lightweight, fast local test run—use the native test suite.
go test -v -tags=native ./...
2. Full Integration Testing
To run the full suite including containerized provider tests (requires Docker or Podman):
go test -v ./...