The Problem
PayPagar needed a comprehensive payroll advance platform where companies could onboard, get approved by multiple levels of checkers (admin and superadmin), and manage employee salary advance requests. The system required complex approval workflows, dynamic form generation based on company requirements, and seamless integration across five different portals.
What Made It Hard
Multi-Level Approval System
The platform required a sophisticated approval workflow where:
- Companies submit onboarding applications with custom fields
- Admin checkers review and approve/reject applications
- Superadmin checkers perform final verification
- Each approval level required field-level granularity (specific fields could be flagged for revision)
Dynamic Form Generation
Each company had unique onboarding requirements, requiring:
- 15+ different input types (text, number, file upload, multi-select, etc.)
- Runtime form generation based on company configuration
- Real-time validation with custom rules
- State management for complex nested forms
System Scale
Managing state and data flow across 5 interconnected applications:
- Brand site for marketing
- Self-service company onboarding portal
- Admin checker dashboard
- Company management portal
- Backend API coordinating everything
My Solution
Architecture Design
Built a 5-app ecosystem with clear separation of concerns:
- Brand Site - Next.js marketing site showcasing platform features
- Company Onboarding Portal - React SPA with Redux for complex form state
- Admin/Checker Portal - Dashboard for multi-level approval workflows
- Company Portal - Post-approval management interface
- Backend API - Node.js/Express with PostgreSQL for centralized data
Dynamic Form Engine
Created a reusable form generation system:
- JSON schema-based form definitions
- 15+ input component types with validation
- Real-time field-level validation
- Nested form support with dynamic sections
Approval Workflow State Machine
Implemented a state machine pattern for approval workflows:
- Clear state transitions (Pending → Admin Review → Superadmin Review → Approved/Rejected)
- Field-level comments and revision requests
- Audit trail for all approval actions
- Role-based access control
State Management Strategy
Used Redux with normalized state shape:
- Entities separated by domain (companies, applications, users)
- Optimistic updates for better UX
- Thunk middleware for async operations
- Selector pattern for derived state
Technical Decisions
Why Redux over Context API?
- Complex state with deep nesting (multi-step forms)
- Need for time-travel debugging
- Multiple components reading same state
- Clear action history for debugging approval workflows
Why Separate Apps vs Monolith?
- Different user personas with distinct needs
- Independent deployment cycles
- Reduced bundle size (brand site doesn't need form logic)
- Team autonomy (frontend/backend teams could work independently)
Why PostgreSQL?
- ACID compliance critical for financial transactions
- Complex relational data (companies → applications → approvals)
- Built-in JSON support for dynamic form schemas
- Strong TypeScript integration with Sequelize
Impact
Business Metrics
- 60% reduction in company onboarding time (from manual process)
- 5 applications delivered across the ecosystem
- 15+ input types supported in dynamic form system
- 3-tier approval workflow with field-level granularity
Technical Achievements
- Zero-downtime deployment via Docker + AWS ECS
- 95+ Lighthouse performance score on brand site
- Sub-200ms API response times
- Type-safe frontend-to-backend contracts
Developer Experience
- Reusable form component library
- Comprehensive Storybook documentation
- Automated testing with Jest + React Testing Library
- CI/CD pipeline with GitHub Actions
What I'd Do Differently
Form State Management
Would explore React Hook Form or Formik instead of custom Redux solution:
- Less boilerplate for form state
- Built-in validation patterns
- Better performance with uncontrolled components
API Design
Would consider GraphQL for the admin portal:
- Reduce over-fetching (admin dashboard needs specific fields)
- Better type safety with generated types
- Single endpoint for complex queries
Testing Strategy
Would implement more E2E tests for approval workflows:
- User flows are complex and prone to regression
- Integration tests catch more bugs than unit tests
- Playwright/Cypress would provide better confidence
Monitoring
Would add structured logging and APM from day one:
- Easier debugging of approval workflow issues
- Performance monitoring for form submissions
- User behavior analytics for UX improvements
Key Takeaways
- Start with state machines for complex workflows - they prevent invalid states
- Separate concerns early - 5 apps was the right call despite initial overhead
- Dynamic forms are hard - invest in solid abstractions before scaling
- Field-level granularity matters - users need precise feedback, not just "form invalid"
- Redux isn't dead - for complex state, it still provides the best DX