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 that power 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, so parties rarely need to type at the same time
  • 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. That is a good trade 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, where each service validates JWTs on its own, 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, so there is no central 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. It shows 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, so it is 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.