Architectural Decision Record (ADR) 002

Document Rule Processing System - Technology Stack

Unified Technology Stack

Status: Accepted ✅

Context

The system requires a robust platform for defining, processing, and executing complex rules against external data (Paperless-ngx). A critical constraint is the preference to maintain code and ecosystem consistency between the Rule Editor (client UI) and the Rule Processor (server backend), where possible.

Decision

The technology stack for the new migration project will be based on a Unified TypeScript Ecosystem:

  • Rule Processor (Server): Node.js with NestJS/Express and TypeScript.
  • Rule Editor (Client): React with TypeScript.
  • Shared Layer: A dedicated internal NPM package containing shared data models and type definitions for rules, actions, and contracts.

Options Considered

Option 1: Unified TypeScript Ecosystem (Selected)

Use Node/React/TypeScript across the entire stack.

High developer velocity due to minimal context switching; strong code contract enforcement via shared types between client and server; efficient handling of I/O-bound, event-driven processing typical for rules engines.

Can require careful management of concurrent processes if logic becomes extremely computationally heavy (mitigated by favoring asynchronous design patterns).

Option 2: Polyglot Stack (e.g., Go Backend + React Frontend)

Use a high-performance language like Go for the processor and TypeScript for the editor.

Excellent raw computational performance in the backend; strong separation of concerns.

Introduces significant complexity in data serialization and inter-language communication (marshalling); violates the preference for stack unification, increasing maintenance costs.

Option 3: Scientific Stack (e.g., Python Backend + React Frontend)

Use a language like Python for the processor due to its rich ecosystem of scientific libraries.

Unrivaled power for highly computational tasks (ML/statistical analysis).

Introduces significant runtime environment overhead; requires complex RPC mechanisms to integrate with the TS frontend; inappropriate if rules are primarily logical rather than statistical.

Consequences

Positive

  • Increased Cohesion: A single source of truth (the shared TypeScript contracts) governs system behavior, leading to fewer integration bugs between the editor and processor.
  • Developer Efficiency: Reduced learning curve for new team members, as the core stack language is uniform.
  • Scalability: Node.js's non-blocking I/O model provides a highly efficient foundation for scaling event processing (rules triggered by events).

Negative

The rule execution logic, while fast, relies on JavaScript runtime performance rather than the raw multi-threading capabilities of compiled languages like Java or Go in specific compute-intensive scenarios. This requires designing rules to be I/O-bound first, and computationally complex only when necessary.