Hernando Abella
Chapter 3Functional ProgrammingOOPArchitecture

Functional Programming vs Object-Oriented Design in Node.js

Explore two powerful programming paradigms, compare their strengths and weaknesses, and learn when to use each approach in Node.js applications.

๐Ÿ“– 22 min read๐Ÿง‘โ€๐Ÿ’ป Hernando Abella๐Ÿ“˜ Node.js Design Patterns
StackNode.jsJavaScriptTypeScriptExpressNestJSFastify

JavaScript and Node.js support multiple programming paradigms. Unlike languages that strongly favor a single approach, Node.js allows developers to build applications using both Functional Programming and Object-Oriented Design.

As Node.js applications grow in complexity, choosing the right design style becomes increasingly important. Some teams prefer immutable data and pure functions, while others rely on classes, inheritance, and encapsulation. In practice, many successful Node.js applications use a combination of both.


Understanding Programming Paradigms

A programming paradigm is a way of organizing code and solving problems. Two of the most influential paradigms in modern software development are:

Functional Programming

FunctionsImmutabilityPure operationsData transformationsComposition

Object-Oriented Programming

ObjectsClassesEncapsulationInheritancePolymorphism

Functional Programming in Node.js

Functional Programming treats functions as first-class citizens and emphasizes minimizing side effects. The core idea is simple: data flows through functions that transform it into new values.

Pure Functions

javascript ยท pure.js
// Pure function - same input = same output, no side effects
function calculateTax(amount) {
  return amount * 0.08;
}

// Predictable and easy to test
console.log(calculateTax(100)); // 8
console.log(calculateTax(100)); // 8

Impure Functions

javascript ยท impure.js
let taxRate = 0.08;

// Impure - depends on external state
function calculateTax(amount) {
  return amount * taxRate;
}

// If taxRate changes, behavior changes

Immutability

javascript ยท immutable.js
// Mutating (bad)
user.name = "Alice";

// Immutable (good)
const updatedUser = {
  ...user,
  name: "Alice"
};

Function Composition

javascript ยท composition.js
const validate = user => user.email;
const normalize = user => ({
  ...user,
  email: user.email.toLowerCase()
});
const save = user => database.insert(user);

// Compose functions together
const processUser = user => save(normalize(validate(user)));

โœ“ Advantages: Easier testing, predictable behavior, better concurrency, improved reusability


Object-Oriented Programming in Node.js

Object-Oriented Programming organizes software around objects that combine data and behavior, modeling real-world entities and business concepts.

Basic Example

javascript ยท class.js
class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  updateEmail(email) {
    this.email = email;
  }
}

const user = new User("Alice", "alice@example.com");
user.updateEmail("alice.new@example.com");

Encapsulation

javascript ยท encapsulation.js
class BankAccount {
  #balance = 0;  // Private field

  deposit(amount) {
    this.#balance += amount;
  }

  getBalance() {
    return this.#balance;
  }
}

const account = new BankAccount();
account.deposit(100);
console.log(account.getBalance()); // 100
// account.#balance // Error - private

Inheritance

javascript ยท inheritance.js
class Employee {
  work() {
    console.log("Working");
  }
}

class Developer extends Employee {
  code() {
    console.log("Coding");
  }
}

const dev = new Developer();
dev.work(); // Working
dev.code(); // Coding

Polymorphism

javascript ยท polymorphism.js
class StripePayment {
  process(amount) {
    console.log(`Processing $${amount} with Stripe`);
  }
}

class PayPalPayment {
  process(amount) {
    console.log(`Processing $${amount} with PayPal`);
  }
}

function checkout(paymentProvider, amount) {
  paymentProvider.process(amount);
}

checkout(new StripePayment(), 100);
checkout(new PayPalPayment(), 100);

โœ“ Advantages: Natural domain modeling, encapsulation, reusability, familiar structure


Comparing Both Approaches

AspectFunctionalObject-Oriented
State ManagementImmutable dataMutable objects
Core Building BlockFunctionsClasses/Objects
Data FlowTransformationsMethod calls
TestingEasy (pure functions)Requires mocks
ReusabilityFunction compositionInheritance
Domain ModelingAlgebraic data typesClasses/Interfaces

User Management Example

Functional Style

javascript ยท functional-user.js
function createUser(name, email) {
  return {
    id: crypto.randomUUID(),
    name,
    email
  };
}

function updateEmail(user, newEmail) {
  return {
    ...user,
    email: newEmail
  };
}

// Usage
let user = createUser("Alice", "alice@example.com");
user = updateEmail(user, "alice.new@example.com");

Object-Oriented Style

javascript ยท oop-user.js
class User {
  constructor(name, email) {
    this.id = crypto.randomUUID();
    this.name = name;
    this.email = email;
  }

  updateEmail(newEmail) {
    this.email = newEmail;
  }
}

// Usage
const user = new User("Alice", "alice@example.com");
user.updateEmail("alice.new@example.com");

How Modern Frameworks Use Both

Express

Functional

Everything revolves around middleware functions and composition.

Fastify

Functional

Mostly functional with plugin composition and hooks.

NestJS

Object-Oriented

Uses classes, decorators, dependency injection, and modules.


The Hybrid Approach

Many experienced Node.js developers use a hybrid model:

๐Ÿ“ Functional for
  • Data transformations
  • Validation
  • Utility functions
  • Business rules
  • Middleware
๐Ÿ—๏ธ OOP for
  • Services
  • Repositories
  • Domain models
  • Dependency injection
  • Large application architecture
javascript ยท hybrid.js
class UserService {
  constructor(repository) {
    this.repository = repository;
  }

  async createUser(userData) {
    // Functional: pure data transformation
    const normalizeEmail = (email) => email.toLowerCase().trim();
    const validateUser = (user) => {
      if (!user.email) throw new Error("Email required");
      return user;
    };
    
    // Compose functional transformations
    const user = {
      ...userData,
      email: normalizeEmail(userData.email)
    };
    
    validateUser(user);
    
    // OOP: service method call
    return this.repository.save(user);
  }
}

โœ“ The service is object-oriented while business logic remains functional. This often provides the best balance.


Choosing the Right Approach

โœ… Consider FP When

  • Predictability is important
  • Complex data transformations exist
  • Testability is a priority
  • Team is comfortable with FP concepts

โœ… Consider OOP When

  • Modeling business entities
  • Building enterprise systems
  • Using dependency injection
  • Organizing large applications

Common Mistakes

โš ๏ธ FP Mistakes

  • Excessive abstraction
  • Overuse of currying
  • Unreadable compositions
  • Avoiding objects entirely

โš ๏ธ OOP Mistakes

  • Deep inheritance trees
  • Massive classes
  • Excessive mutable state
  • Tight coupling

Conclusion

Node.js provides the flexibility to use both Functional Programming and Object-Oriented Design effectively. Functional Programming offers predictability, immutability, and simpler testing, while Object-Oriented Programming excels at modeling complex domains and organizing large systems.

Rather than treating the two paradigms as competitors, modern Node.js applications often benefit from combining them. The most effective Node.js developers understand both approaches and choose the right tool for each problem.

Functional techniques can simplify business logic and data transformations, while object-oriented structures provide organization and architectural clarity, creating applications that are maintainable, scalable, and easy to evolve over time.


๐Ÿ“˜ From the Book

Node.js Design Patterns

Master FP vs OOP, SOLID principles, DRY, KISS, YAGNI, and essential design patterns for building scalable Node.js applications with real-world examples.

๐ŸŽฏ FP vs OOP๐Ÿ—๏ธ Design Patternsโšก Best Practices๐Ÿ”ง Clean Architecture
Get it on Amazon โ†’
Node.js Design Patterns book cover