backend-challenge-cli is a command-line interface (CLI) tool for managing invoice approval workflows. This application implements a comprehensive invoice approval system where companies can define workflow rules that automatically route approval requests to the appropriate approvers based on invoice amount, department, and manager approval requirements.
This solution was developed as part of the Light backend challenge, implementing an invoice approval workflow system in Go. The application allows companies to configure approval workflows through a set of rules that determine which approver should receive approval requests based on specific criteria.
- Interactive Invoice Processing: Process invoices through an interactive CLI workflow
- Workflow Rule Management: Create, update, delete, and list workflow rules
- Approver Management: Manage company approvers with full CRUD operations
- Multi-Channel Notifications: Support for both Slack and Email approval channels
- In-Memory SQLite Database: Fast, lightweight database with pre-seeded sample data
- Comprehensive CLI Interface: Full command-line interface with help and examples
To build and install the CLI tool, run the following commands:
# Build the application
go build -o backend-challenge-cli .
# Or install it globally
go install .The project includes a comprehensive Makefile with various build and utility commands:
# Build the CLI application
make build
# Run CLI with default company 'Light'
make run-cli
# Run CLI with custom company
make run-cli-custom COMPANY="MyCompany"
# Show CLI help
make help-cli
# Run tests
make test
# Run tests with coverage
make test-coverage
# Clean build artifacts
make clean
# Install dependencies
make deps
# Show all available make commands
make helpThe CLI supports several commands for managing workflow rules, approvers, and processing invoices. Each command also supports various flags for customization.
--company,-c: Company name for the workflow service (default: "Light")--departments,-d: Comma-separated list of departments (default: "Marketing,Finance")--slack-connection-string: Connection string for Slack notifications--email-connection-string: Connection string for email notifications--verbose,-v: Enable verbose output
Processes an invoice through the approval workflow interactively.
Usage:
backend-challenge-cli process-invoice
backend-challenge-cli invoice
backend-challenge-cli iExample:
# Process invoice with default company
backend-challenge-cli process-invoice
# Process invoice with custom company
backend-challenge-cli --company "MyCompany" process-invoice
# Process invoice with verbose output
backend-challenge-cli --verbose process-invoiceThis command will prompt you to enter:
- Invoice amount (USD)
- Department (Finance or Marketing)
- Whether manager approval is required
Creates a new workflow rule for the company.
Usage:
backend-challenge-cli create-workflow-rule --approver-id <id> --approval-channel <channel> [options]
backend-challenge-cli cwr --approver-id <id> --approval-channel <channel> [options]Options:
--min-amount,-min: Minimum amount for the rule (optional)--max-amount,-max: Maximum amount for the rule (optional)--department,-d: Department for the rule (optional)--approver-id,-aid: ID of the approver (required)--approval-channel,-ac: Approval channel (0=Slack, 1=Email) (required)--manager-approval,-ma: Whether manager approval is required (0=No, 1=Yes) (optional)
Examples:
# Create rule for invoices under $5k to Finance Team via Slack
backend-challenge-cli create-workflow-rule --max-amount 5000 --approver-id 1 --approval-channel 0
# Create rule for Marketing invoices $10k+ to CMO via Email
backend-challenge-cli cwr --min-amount 10000 --department "Marketing" --approver-id 4 --approval-channel 1
# Create rule with manager approval requirement
backend-challenge-cli create-workflow-rule --min-amount 5000 --max-amount 10000 --approver-id 2 --approval-channel 1 --manager-approval 1Updates an existing workflow rule.
Usage:
backend-challenge-cli update-workflow-rule --id <id> --approver-id <id> --approval-channel <channel> [options]
backend-challenge-cli uwr --id <id> --approver-id <id> --approval-channel <channel> [options]Examples:
# Update rule to change amount range and approver
backend-challenge-cli update-workflow-rule --id 1 --min-amount 200 --max-amount 600 --approver-id 2 --approval-channel 1
# Update rule to change department and approval channel
backend-challenge-cli uwr --id 2 --department "Finance" --approver-id 3 --approval-channel 0
# Update rule to add manager approval requirement
backend-challenge-cli update-workflow-rule --id 3 --min-amount 1000 --max-amount 5000 --approver-id 1 --approval-channel 1 --manager-approval 1Deletes a workflow rule by ID.
Usage:
backend-challenge-cli delete-workflow-rule --id <id>
backend-challenge-cli dwr --id <id>Example:
backend-challenge-cli delete-workflow-rule --id 1Retrieves a workflow rule by ID.
Usage:
backend-challenge-cli get-workflow-rule --id <id>
backend-challenge-cli gwr --id <id>Example:
backend-challenge-cli get-workflow-rule --id 1Lists all workflow rules for the company.
Usage:
backend-challenge-cli list-workflow-rules
backend-challenge-cli lwrExample:
backend-challenge-cli list-workflow-rulesCreates a new approver for the company.
Usage:
backend-challenge-cli create-approver --name <name> --role <role> --email <email> --slack-id <slack_id>
backend-challenge-cli ca --name <name> --role <role> --email <email> --slack-id <slack_id>Example:
backend-challenge-cli create-approver --name "John Doe" --role "Manager" --email "john@example.com" --slack-id "U123456"Updates an existing approver.
Usage:
backend-challenge-cli update-approver --id <id> --name <name> --role <role> --email <email> --slack-id <slack_id>
backend-challenge-cli ua --id <id> --name <name> --role <role> --email <email> --slack-id <slack_id>Example:
backend-challenge-cli update-approver --id 1 --name "John Doe Updated" --role "Senior Manager" --email "john.updated@example.com" --slack-id "U123456"Deletes an approver by ID.
Usage:
backend-challenge-cli delete-approver --id <id>
backend-challenge-cli da --id <id>Example:
backend-challenge-cli delete-approver --id 1Retrieves an approver by ID.
Usage:
backend-challenge-cli get-approver --id <id>
backend-challenge-cli ga --id <id>Example:
backend-challenge-cli get-approver --id 1Lists all approvers for the company.
Usage:
backend-challenge-cli list-approvers
backend-challenge-cli laExample:
backend-challenge-cli list-approversThe Go codebase is structured with a clean architecture pattern, consisting of three main services:
The core business logic service responsible for:
- Finding matching workflow rules based on invoice criteria
- Processing invoices through the approval workflow
- Managing the interactive CLI interface for invoice processing
- Coordinating between database and notification services
Handles all data management operations:
- CRUD operations for workflow rules
- CRUD operations for approvers
- Data validation and business rule enforcement
- API-to-database model conversion
Provides all database interactions:
- In-memory SQLite database implementation
- Database schema management
- Sample data seeding
- Store pattern for different entity types (companies, approvers, workflow rules)
The application uses an in-memory SQLite database that is automatically created and seeded with sample data when the application starts.
Figure 1: Database schema showing the relationships between companies, approvers, and workflow rules
- companies: Stores company information
- approvers: Stores employee information who can approve invoices
- workflow_rules: Defines the approval workflow rules
The database is pre-populated with sample data from the challenge requirements, including:
- Light company
- 4 approvers: Finance Team Member, Vera Sander (Finance Manager), Amanda Svensson (CFO), Sarah Johnson (CMO)
- 5 workflow rules implementing the approval logic from the challenge diagram
- Rule 1: Send to Finance Team Member via Slack when invoice < $5k
- Rule 2: Send to Finance Team Member via Email when $5k ≤ invoice < $10k
- Rule 3: Send to Finance Manager via Email when $5k ≤ invoice < $10k and manager approval required
- Rule 4: Send to CFO via Slack when invoice ≥ $10k (not marketing)
- Rule 5: Send to CMO via Email when invoice ≥ $10k and related to marketing
The system uses a sophisticated SQL query to find the most specific matching rule for each invoice. The query implements a priority-based matching system that ensures the most specific rule is selected when multiple rules could apply.
SELECT id, company_id, min_amount, max_amount, department,
is_manager_approval_required, approver_id, approval_channel
FROM workflow_rules
WHERE company_id = $1
AND (
-- Amount logic: inclusive lower bound, exclusive upper bound
(min_amount IS NULL OR $2 >= min_amount) AND
(max_amount IS NULL OR $2 < max_amount)
)
AND (department IS NULL OR department = $3)
AND (is_manager_approval_required IS NULL OR is_manager_approval_required = $4)
ORDER BY
(CASE WHEN min_amount IS NOT NULL THEN 1 ELSE 0 END +
CASE WHEN max_amount IS NOT NULL THEN 1 ELSE 0 END +
CASE WHEN department IS NOT NULL THEN 1 ELSE 0 END +
CASE WHEN is_manager_approval_required IS NOT NULL THEN 1 ELSE 0 END) DESC,
id
LIMIT 1The ORDER BY clause implements a specificity-based priority system:
-
Specificity Score: Each rule gets a score based on how many criteria are specified:
min_amount IS NOT NULL= +1 pointmax_amount IS NOT NULL= +1 pointdepartment IS NOT NULL= +1 pointis_manager_approval_required IS NOT NULL= +1 point
-
Rule Selection: The rule with the highest specificity score is selected first
-
Tie-breaking: If multiple rules have the same score, the rule with the lowest ID (created first) is selected
Scenario 1: Overlapping Amount Ranges
# Rule A: General rule for all invoices under $10k
min_amount: null
max_amount: 10000
department: null
manager_approval: null
# Score: 1 (only max_amount specified)
# Rule B: Specific rule for Finance invoices $5k-$10k
min_amount: 5000
max_amount: 10000
department: "Finance"
manager_approval: null
# Score: 3 (min_amount + max_amount + department)For a Finance invoice of $7,500:
- Both rules match
- Rule B wins (score 3 > score 1) - more specific
Scenario 2: Manager Approval Priority
# Rule A: General rule for Marketing invoices
min_amount: 10000
max_amount: null
department: "Marketing"
manager_approval: null
# Score: 2 (min_amount + department)
# Rule B: Specific rule for Marketing invoices requiring manager approval
min_amount: 10000
max_amount: null
department: "Marketing"
manager_approval: 1
# Score: 3 (min_amount + department + manager_approval)For a Marketing invoice of $15,000 requiring manager approval:
- Both rules match
- Rule B wins (score 3 > score 2) - more specific
Scenario 3: Exact Amount Match
# Rule A: General rule for invoices under $5k
min_amount: null
max_amount: 5000
department: null
manager_approval: null
# Score: 1 (only max_amount)
# Rule B: Specific rule for exactly $3k invoices
min_amount: 3000
max_amount: 3000
department: null
manager_approval: null
# Score: 2 (min_amount + max_amount)For an invoice of exactly $3,000:
- Both rules match
- Rule B wins (score 2 > score 1) - more specific
1. No Ordering (Assume No Overlapping Rules)
-- Simple approach without ORDER BY
SELECT * FROM workflow_rules
WHERE company_id = $1 AND [criteria...]
LIMIT 1- Pros: Simpler query, faster execution
- Cons: Requires careful rule design to avoid conflicts
- Use Case: When business rules are designed to be mutually exclusive
2. Explicit Priority Field
-- Add priority column to workflow_rules table
ALTER TABLE workflow_rules ADD COLUMN priority INTEGER DEFAULT 0;
-- Query with explicit priority
SELECT * FROM workflow_rules
WHERE company_id = $1 AND [criteria...]
ORDER BY priority DESC, id
LIMIT 1- Pros: Explicit control over rule precedence
- Cons: Requires manual priority management
- Use Case: When business needs explicit control over rule ordering
The current implementation uses the specificity-based approach because it provides the most intuitive behavior: more specific rules naturally take precedence over general ones, which aligns with typical business logic expectations.
The CLI provides comprehensive error handling with:
- Clear error messages for invalid inputs
- Network and database error handling
- Validation errors for business rules
- User-friendly error formatting with colored output
The project includes comprehensive unit tests for all major components. Run the test suite using:
# Run all tests
make test
# Run tests with coverage
make test-coverage
# Run tests with verbose output
make test-verbose
# Generate detailed coverage report
go test -coverprofile=coverage.out ./... && go tool cover -func=coverage.outThe current test coverage across the codebase is 47.7% of statements. Here's the breakdown by package:
| Package | Coverage | Status |
|---|---|---|
| common | 100.0% | ✅ Excellent |
| notification/email | 100.0% | ✅ Excellent |
| notification/slack | 100.0% | ✅ Excellent |
| db | 90.9% | ✅ Very Good |
| management | 51.2% | |
| workflow | 45.8% | |
| api | 0.0% | ❌ No Tests |
| cmd/cli | 0.0% | ❌ No Tests |
| config | 0.0% | ❌ No Tests |
| db/sqlite | 0.0% | ❌ No Tests |
# Test specific package
go test ./workflow/
# Test with race detection
go test -race ./...
# Test with coverage for specific package
go test -cover ./management/
# Run tests with detailed output
go test -v ./db/
# Run integration tests for workflow rules
go test ./workflow/ -v -run TestWorkflowRulesIntegrationKey test files in the project:
common/logger_test.go- Logger functionality testsdb/*_test.go- Database store and service testsmanagement/service_test.go- Management service testsnotification/*/service_test.go- Notification service testsworkflow/service_test.go- Workflow service testsworkflow/integration_test.go- Integration tests for all 5 workflow rules
The application has been thoroughly tested with the pre-seeded workflow rules to ensure correct rule matching and priority handling. All 5 workflow rules have been verified to work correctly both through manual CLI testing and automated integration tests.
Automated Integration Tests: The workflow/integration_test.go file contains comprehensive tests that automatically verify all 5 workflow rules work correctly. Anyone reviewing this repository can run these tests to verify the functionality:
# Run all integration tests
go test ./workflow/ -v -run TestWorkflowRulesIntegration| Test Case | Amount | Department | Manager Approval | Expected Rule | Actual Result | Status |
|---|---|---|---|---|---|---|
| Rule 1 | $3,000 | Finance | No | Finance Team Member via Slack | ✅ System User (Finance Team Member) via Slack | PASS |
| Rule 2 | $7,500 | Finance | No | Finance Team Member via Email | ✅ System User (Finance Team Member) via Email | PASS |
| Rule 3 | $7,500 | Finance | Yes | Finance Manager via Email | ✅ Vera Sander (Finance Manager) via Email | PASS |
| Rule 4 | $15,000 | Finance | No | CFO via Slack | ✅ Amanda Svensson (CFO) via Slack | PASS |
| Rule 5 | $15,000 | Marketing | No | CMO via Email | ✅ Sarah Johnson (CMO) via Email | PASS |
| Priority Test | $15,000 | Marketing | Yes | CMO via Email (Rule 5 wins) | ✅ Sarah Johnson (CMO) via Email | PASS |
✅ All 5 workflow rules work perfectly - Each rule correctly matches the expected criteria and routes to the right approver
✅ Priority system works - More specific rules (with department specified) take precedence over general rules
✅ Channel routing is correct - Slack vs Email channels are properly assigned
✅ Manager approval logic works - The system correctly differentiates between regular and manager approval requirements
✅ Amount ranges work correctly - Inclusive lower bounds and exclusive upper bounds are properly implemented
The SQL query with the specificity scoring system ensures that more specific rules take precedence over general ones, providing robust approval workflow behavior.
backend-challenge-go/
├── api/ # API models and interfaces
├── cmd/cli/ # CLI application entry point
├── common/ # Shared utilities (logging)
├── config/ # Configuration management
├── configs/ # Configuration files and sample data
├── db/ # Database layer and stores
├── management/ # Management service
├── notification/ # Notification services (Slack, Email)
├── workflow/ # Core workflow service
└── main.go # Application entry point
- urfave/cli/v2: CLI framework
- sqlite3: Database driver
- log/slog: Structured logging
