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
1// Pure function - same input = same output, no side effects
2function calculateTax(amount) {
3  return amount * 0.08;
4}
5
6// Predictable and easy to test
7console.log(calculateTax(100)); // 8
8console.log(calculateTax(100)); // 8

Impure Functions

javascript · impure.js
1let taxRate = 0.08;
2
3// Impure - depends on external state
4function calculateTax(amount) {
5  return amount * taxRate;
6}
7
8// If taxRate changes, behavior changes

Immutability

javascript · immutable.js
1// Mutating (bad)
2user.name = "Alice";
3
4// Immutable (good)
5const updatedUser = {
6  ...user,
7  name: "Alice"
8};

Function Composition

javascript · composition.js
1const validate = user => user.email;
2const normalize = user => ({
3  ...user,
4  email: user.email.toLowerCase()
5});
6const save = user => database.insert(user);
7
8// Compose functions together
9const 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
1class User {
2  constructor(name, email) {
3    this.name = name;
4    this.email = email;
5  }
6
7  updateEmail(email) {
8    this.email = email;
9  }
10}
11
12const user = new User("Alice", "alice@example.com");
13user.updateEmail("alice.new@example.com");

Encapsulation

javascript · encapsulation.js
1class BankAccount {
2  #balance = 0;  // Private field
3
4  deposit(amount) {
5    this.#balance += amount;
6  }
7
8  getBalance() {
9    return this.#balance;
10  }
11}
12
13const account = new BankAccount();
14account.deposit(100);
15console.log(account.getBalance()); // 100
16// account.#balance // Error - private

Inheritance

javascript · inheritance.js
1class Employee {
2  work() {
3    console.log("Working");
4  }
5}
6
7class Developer extends Employee {
8  code() {
9    console.log("Coding");
10  }
11}
12
13const dev = new Developer();
14dev.work(); // Working
15dev.code(); // Coding

Polymorphism

javascript · polymorphism.js
1class StripePayment {
2  process(amount) {
3    console.log(`Processing $${amount} with Stripe`);
4  }
5}
6
7class PayPalPayment {
8  process(amount) {
9    console.log(`Processing $${amount} with PayPal`);
10  }
11}
12
13function checkout(paymentProvider, amount) {
14  paymentProvider.process(amount);
15}
16
17checkout(new StripePayment(), 100);
18checkout(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
1function createUser(name, email) {
2  return {
3    id: crypto.randomUUID(),
4    name,
5    email
6  };
7}
8
9function updateEmail(user, newEmail) {
10  return {
11    ...user,
12    email: newEmail
13  };
14}
15
16// Usage
17let user = createUser("Alice", "alice@example.com");
18user = updateEmail(user, "alice.new@example.com");

Object-Oriented Style

javascript · oop-user.js
1class User {
2  constructor(name, email) {
3    this.id = crypto.randomUUID();
4    this.name = name;
5    this.email = email;
6  }
7
8  updateEmail(newEmail) {
9    this.email = newEmail;
10  }
11}
12
13// Usage
14const user = new User("Alice", "alice@example.com");
15user.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
1class UserService {
2  constructor(repository) {
3    this.repository = repository;
4  }
5
6  async createUser(userData) {
7    // Functional: pure data transformation
8    const normalizeEmail = (email) => email.toLowerCase().trim();
9    const validateUser = (user) => {
10      if (!user.email) throw new Error("Email required");
11      return user;
12    };
13    
14    // Compose functional transformations
15    const user = {
16      ...userData,
17      email: normalizeEmail(userData.email)
18    };
19    
20    validateUser(user);
21    
22    // OOP: service method call
23    return this.repository.save(user);
24  }
25}

✓ 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
Share X LinkedIn
Hernando Abella

Hernando Abella

Software engineer and author. I write about Python, AI, and software architecture. Author of 55+ programming books and creator of interactive coding challenges.