Skip to content
patterns questions

Patterns · 10 of 10middle~2 min

What is the Module pattern and how does it relate to ES modules?

#design-patterns#javascript#architecture

Module Pattern

The module pattern encapsulates related code into a self-contained unit with a public interface, hiding implementation details.

Classic IIFE module (pre-ES modules)

ts
const CartModule = (() => { // private - not accessible outside let items: CartItem[] = []; let discount = 0; function calculateTotal() { return items.reduce((sum, i) => sum + i.price, 0) * (1 - discount); } // public interface return { addItem(item: CartItem) { items = [...items, item]; }, removeItem(id: string) { items = items.filter(i => i.id !== id); }, setDiscount(d: number) { discount = d; }, getTotal() { return calculateTotal(); }, getItems() { return [...items]; }, // return copy - protect internal state }; })(); CartModule.addItem({ id: '1', price: 29.99 }); CartModule.getTotal(); // 29.99 // CartModule.items -> undefined - private

ES Modules - native module system

ts
// cart.ts let items: CartItem[] = []; // module-private export function addItem(item: CartItem) { items = [...items, item]; } export function getItems() { return [...items]; } export function getTotal() { return items.reduce((sum, i) => sum + i.price, 0); } // private - not exported, not accessible function applyTax(amount: number) { return amount * 1.2; }
ts
// anywhere import { addItem, getTotal } from './cart';

Module patterns in practice

ts
// barrel file - re-export public API // features/auth/index.ts export { useAuth } from './useAuth'; export { AuthProvider } from './AuthProvider'; export type { User, AuthState } from './types'; // internal hooks, utils, api calls stay private

Key benefits

  • Encapsulation - hide implementation, expose only what is needed.
  • Namespace - no global variable pollution.
  • Reusability - import only what you need.
  • Tree shaking - bundlers can remove unused exports.

ES modules are the native module pattern. A barrel file (index.ts) defines the public API of a feature folder, but use it with care: large barrels slow down dev servers and tests and can hurt tree shaking, because importing one export loads the whole folder. Many teams keep barrels only at feature boundaries or import files directly.

updated

did you know it?

related questions