Building a Document Approval Workflow System in Java

Overview

Document approval workflows are essential in enterprise applications where multiple stakeholders need to review and authorize documents before they become official. This tutorial demonstrates how to implement a structured approval system using Java, covernig the complete lifecycle from submission to final authorization.

Workflow Process

The approval system follows a sequential process where each stage requires specific actions from different participants. Below is the breakdown of the workflow:

Stage Description Responsible Party
1 Initialize request with required details Requester
2 Submit request for review System
3 Evaluate and authorize request Reviewer
4 Finalize and record outcome System

Implementation

Step 1: Define the Request Entity

The foundation of our approval system begins with a data model that captures all necesary information about the request.

public class ApprovalRequest {
    private String requestId;
    private String documentContent;
    private String submittedBy;
    private ApprovalStatus status;
    private LocalDateTime submissionDate;
    private LocalDateTime approvalDate;
    
    public enum ApprovalStatus {
        PENDING,
        UNDER_REVIEW,
        APPROVED,
        REJECTED
    }
    
    public ApprovalRequest(String requestId, String documentContent, String submittedBy) {
        this.requestId = requestId;
        this.documentContent = documentContent;
        this.submittedBy = submittedBy;
        this.status = ApprovalStatus.PENDING;
        this.submissionDate = LocalDateTime.now();
    }
    
    // Getters and setters omitted for brevity
}

Step 2: Create the Submission Service

This service handles the initial submission of approval requests and manages the state transitions.

public class SubmissionService {
    private Map<String, ApprovalRequest> requestRepository;
    
    public SubmissionService() {
        this.requestRepository = new HashMap<>();
    }
    
    public ApprovalRequest submitRequest(ApprovalRequest request) {
        if (request.getStatus() != ApprovalRequest.ApprovalStatus.PENDING) {
            throw new IllegalStateException("Request must be in PENDING state");
        }
        requestRepository.put(request.getRequestId(), request);
        request.setStatus(ApprovalRequest.ApprovalStatus.UNDER_REVIEW);
        return request;
    }
    
    public ApprovalRequest retrieveRequest(String requestId) {
        return requestRepository.get(requestId);
    }
}

Step 3: Implement the Reviewer Handler

The reviewer component is responsible for evaluating requests and making approval decisions.

public class ReviewerHandler {
    private String reviewerName;
    private List<String> approvalComments;
    
    public ReviewerHandler(String reviewerName) {
        this.reviewerName = reviewerName;
        this.approvalComments = new ArrayList<>();
    }
    
    public void evaluateRequest(ApprovalRequest request) {
        if (request.getStatus() != ApprovalRequest.ApprovalStatus.UNDER_REVIEW) {
            throw new IllegalStateException("Request is not under review");
        }
        // Review logic would go here
        System.out.println("Reviewer " + reviewerName + " is evaluating request: " + request.getRequestId());
    }
    
    public void makeDecision(ApprovalRequest request, boolean approved, String comments) {
        approvalComments.add(comments);
        request.setStatus(approved ? 
            ApprovalRequest.ApprovalStatus.APPROVED : 
            ApprovalRequest.ApprovalStatus.REJECTED);
        request.setApprovalDate(LocalDateTime.now());
    }
}

Step 4: Create the Completion Manager

The final stage involves recording the completion status and notifying relevant parties.

public class CompletionManager {
    private final SubmissionService submissionService;
    
    public CompletionManager(SubmissionService submissionService) {
        this.submissionService = submissionService;
    }
    
    public void finalizeRequest(String requestId) {
        ApprovalRequest request = submissionService.retrieveRequest(requestId);
        
        if (request == null) {
            throw new IllegalArgumentException("Request not found: " + requestId);
        }
        
        if (request.getStatus() != ApprovalRequest.ApprovalStatus.APPROVED &&
            request.getStatus() != ApprovalRequest.ApprovalStatus.REJECTED) {
            throw new IllegalStateException("Request must be decided before finalization");
        }
        
        System.out.println("Request " + requestId + " has been " + request.getStatus());
    }
}

Usage Example

The following demonstrates how these components work together in a typical scenario:

public class WorkflowDemo {
    public static void main(String[] args) {
        // Initialize services
        SubmissionService submissionService = new SubmissionService();
        CompletionManager completionManager = new CompletionManager(submissionService);
        ReviewerHandler reviewer = new ReviewerHandler("John Doe");
        
        // Create and submit request
        ApprovalRequest request = new ApprovalRequest(
            "REQ-001",
            "Contract document for project X",
            "Alice Smith"
        );
        
        // Submit the request
        submissionService.submitRequest(request);
        
        // Reviewer evaluates and approves
        reviewer.evaluateRequest(request);
        reviewer.makeDecision(request, true, "Document meets all requirements");
        
        // Complete the workflow
        completionManager.finalizeRequest("REQ-001");
    }
}

Tags: java Workflow approval-system design-patterns enterprise-java

Posted on Sat, 05 Sep 2026 16:40:50 +0000 by daverules