Skip to content

♻️ Functional decomposition roadmap: Kill the classes, embrace pure functions #128

@Robdel12

Description

@Robdel12

The Big Picture

We've been refactoring the codebase to be more testable and maintainable using functional decomposition - extracting pure functions from monolithic classes so we can test real behavior with simple input/output assertions.

Status: ✅ Complete

All services have been refactored or deleted:

Service Status Notes
TddService Refactored to src/tdd/ (PR #125)
ApiService Refactored to src/api/ (PR #127)
build-manager.js Kept pure functions, deleted class (PR #134)
auth-service.js Refactored to src/auth/ (PR #129)
config-service.js Deleted - was thin wrapper (PR #134)
project-service.js Deleted - was thin wrapper (PR #134)
api-service.js Deleted - was thin wrapper (PR #134)
server-manager.js Refactored to src/server-manager/ (PR #133, #134)
test-runner.js Refactored to src/test-runner/ (PR #133)
screenshot-server.js Refactored to src/screenshot-server/ (PR #133)
uploader.js Refactored to functional with DI (PR #133)
html-report-generator.js Refactored to src/report-generator/ (PR #134)
static-report-generator.js Uses report-generator module (PR #134)

Key PRs

The Pattern We Used

For each service:

  1. Create a new module (e.g., src/auth/ or src/server-manager/)
  2. Extract pure functions into core.js - these need zero mocks to test
  3. Create operations with dependency injection in operations.js
  4. Write tests with dependency injection - pass stubs as params, not vi.mock
  5. Delete thin class wrappers that only forwarded calls
  6. Delete mock-heavy tests - replaced with proper unit tests

Results

  • ✅ No vi.mock in new tests
  • ✅ Pure functions tested with input/output assertions
  • ✅ Dependency injection for anything with I/O
  • ✅ Net reduction in test code (less mocking boilerplate)
  • ✅ Commands use functional modules directly
  • createServices() simplified to only what plugins need

What's Left in src/services/

Only services that provide plugin API or need EventEmitter:

  • ServerManager class - plugins use EventEmitter interface
  • TestRunner class - plugins use EventEmitter interface
  • Pure functions in build-manager.js, uploader.js

These are documented as the public plugin API.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions