Contract Lifecycle Management Platform

Overview

An enterprise contract lifecycle management platform enabling organizations to draft, negotiate, approve, sign, and analyze legal documents in a secure, collaborative environment. Built on 9 independently deployable microservices with an NGINX API gateway, event-driven inter-service communication, eIDAS-compliant electronic signatures, and multi-party document workflows with version-controlled collaboration.

DomainLegal Tech — Contract Lifecycle Management
RoleLead Software Engineer / System Architect
TimelineArchitecture Design & Technical Investigation
StackNode.js, React, MongoDB, NGINX, Docker, Kubernetes, RabbitMQ, MinIO
Scale9 microservices, NGINX API gateway, 3 storage layers, eIDAS + DocuSign signing

Architecture

System Architecture

Service Responsibilities

ServicePurposeKey Technology
NGINX API GatewaySingle entry point; routing, SSL termination, rate limiting, auth delegationNGINX auth_request, proxy_pass
IAM ServiceAuthentication (local + Google SSO), JWT issuance/validation, role managementPassport.js, jsonwebtoken, bcrypt
Documents ServiceDocument CRUD, S3-backed versioning, clause library, pessimistic lockingMongoose, MinIO SDK
Workflow EngineDocument lifecycle state machine (Draft through Signed/Archived)Event publishing, state validation
Conversion ServiceFormat conversion (DOCX, HTML, PDF) via headless LibreOfficeLibreOffice CLI, Docker
E-Signature ServiceSigning orchestration with DocuSign, eIDAS compliance, digital certificatesDocuSign API, WebCrypto
Presence & ChatReal-time user presence and document-scoped chatSocket.io (WebSockets)
Comments ServiceThreaded, version-linked comments with resolution trackingMongoose
Notifications ServiceEvent-driven email and in-app alert dispatchNodemailer, event consumer
Analytics ServiceKPI aggregation, categorized reports, deadline trackingEvent consumer, aggregation pipelines

Key Architectural Decisions

1. Asynchronous Collaboration Over Real-Time Editing

Context: The platform requires multi-party document review and editing. The initial design considered real-time collaborative editing using Operational Transformation (OT) or Conflict-free Replicated Data Types (CRDTs) — the same algorithms powering Google Docs.

Decision: I chose an asynchronous, version-based editing model with pessimistic locking instead.

Rationale:

  • OT/CRDT introduces significant implementation complexity (conflict resolution algorithms, stateful WebSocket servers, editor library constraints)
  • The business workflow is inherently sequential: Author edits, then Reviewer reviews, then Approver approves — parties rarely need to type simultaneously
  • A locking + explicit save model provides clear version history and simpler conflict prevention
  • The “Revisions” feature (diff between versions) is fully achievable without real-time sync

Trade-off: Users cannot see each other’s keystrokes in real time. But the overall system is dramatically simpler to build, debug, scale, and maintain — a net positive for a platform where the review cycle spans days, not seconds.

2. NGINX auth_request Pattern for Distributed Authentication

Context: In a microservices architecture, every service needs to verify that incoming requests are authenticated and extract user identity. The naive approach — each service validating JWTs independently — spreads security logic across all services.

Decision: Centralize authentication at the API gateway using NGINX’s auth_request directive, which delegates token validation to the IAM Service via an internal sub-request before any traffic reaches downstream services.

How it works:

Result: Authentication happens once at the edge. Downstream services receive pre-validated X-User-ID and X-User-Roles headers, trusting them because they came through the gateway. Each service then applies its own fine-grained authorization (e.g., “can this editor modify this specific document?”) using a local permission configuration — no centralized authorization bottleneck.

3. Macro-State vs. Micro-State Separation

Context: Both the Workflow Engine and the Documents Service manage “state” for a document. This initially appeared to create a conflict in ownership.

Decision: Separate concerns into two distinct state layers:

Workflow Engine (Macro)Documents Service (Micro)
ManagesBusiness lifecycleOperational edit lock
StatesDraft, In Review, Signed, ArchivedLocked, Unlocked
DurationDays to weeksMinutes to hours
TriggersBusiness milestones (submit, approve, sign)Edit/Save/Cancel actions
Question“Where is this in the business process?”“Is someone editing right now?”

How they interact: The macro-state constrains the micro-state. A document in “Signed” state cannot be locked for editing. A document “In Review” can only be locked by designated reviewers. The Documents Service checks the workflow state before granting any lock.


Deep Dive: E-Signature Orchestration

This sequence diagram illustrates the most complex cross-service workflow in the platform — how a document moves from “ready for signature” through third-party signing to final sealed storage:

Complexity managed:

  • 6 services coordinated through events (no direct service-to-service coupling)
  • Format conversion happens on-demand (HTML editor format to signing-ready PDF)
  • DocuSign integration is fully isolated in one service — swappable for any other provider
  • Webhook-based completion avoids polling and keeps the flow async

Document Lifecycle


Technology Stack

LayerTechnologyPurpose
FrontendReact, Redux Toolkit, Socket.io Client, Rich Text EditorSPA with real-time chat and document editing
API GatewayNGINXRouting, SSL, rate limiting, auth delegation
BackendNode.js 20, Express.jsAll microservice runtimes
DatabaseMongoDB (per-service), Mongoose ODMDocument storage with schema validation
Object StorageMinIO (S3-compatible)Document files, versions, signed PDFs
AuthenticationPassport.js (local + Google), jsonwebtoken, bcryptMulti-strategy auth, stateless JWT sessions
MessagingRabbitMQAsync event-driven inter-service communication
Real-timeSocket.io (WebSockets)Presence tracking and document chat
ConversionLibreOffice (headless)DOCX/HTML/PDF format conversion
E-SignaturesDocuSign APIThird-party signing orchestration
EmailNodemailerNotification dispatch
ContainersDockerService packaging and isolation
OrchestrationKubernetesAuto-scaling, self-healing, rolling deployments
CI/CDGitHub ActionsAutomated test, build, and deploy pipelines

What I Would Do Differently Today

Architectural decisions are context-dependent. With the benefit of hindsight and the evolution of the ecosystem since the original design, here are the changes I would consider:

AreaOriginal ChoiceWhat I’d Evaluate TodayWhy
API GatewayRaw NGINX configKong, AWS API Gateway, or EnvoyBuilt-in observability, plugin ecosystem, reduced configuration burden
IAM ServiceCustom Node.js + Passport.jsKeycloak or Auth0Battle-tested identity platform with built-in SSO, MFA, user management UI; eliminates a full custom service
Inter-service commsREST + RabbitMQgRPC for sync calls + Kafka for eventsgRPC gives type-safe contracts with Protobuf and better performance; Kafka provides event replay and stream processing
Document versioningNew S3 object per versionEvent Sourcing patternStore a stream of change events rather than full snapshots; enables richer audit trails and undo/redo
AuthorizationPer-service config filesOpen Policy Agent (OPA)Centralized, declarative policy engine with Rego language; avoids duplicating permission maps across services
ObservabilityNot addressed in originalOpenTelemetry + Grafana stackDistributed tracing across 9 services is essential for debugging production issues
Real-time editingRemoved entirelyEvaluate Yjs or LiveblocksThe CRDT ecosystem has matured significantly; if the business case demands it, it’s now more feasible

These aren’t criticisms of the original architecture — they reflect how the ecosystem has evolved and how requirements might change at scale. The original decisions were sound for the constraints and timeline of the project.


Event-Driven Communication Map

All asynchronous inter-service communication flows through the message broker:


Architecture designed and documented as part of a technical investigation into a contract lifecycle management platform. All diagrams created with Mermaid.js.