Back to list
aiskillstore

solid-principles

by aiskillstore

Security-audited skills for Claude, Codex & Claude Code. One-click install, quality verified.

102🍴 3📅 Jan 23, 2026

SKILL.md


name: solid-principles description: SOLID principles adapted for functional and TypeScript-first development.

SOLID Principles for Node.js/TypeScript

Overview

SOLID principles adapted for functional and TypeScript-first development.

S - Single Responsibility Principle

A module/function should have only one reason to change.

Violation

// Bad: Does validation, processing, and notification
const processOrder = async (order: Order) => {
  // Validation
  if (!order.items.length) throw new Error('Empty order');
  if (order.total < 0) throw new Error('Invalid total');

  // Processing
  const processed = { ...order, status: 'processed' };
  await db.orders.save(processed);

  // Notification
  await emailService.send(order.userId, 'Order confirmed');

  return processed;
};

Correct

// Good: Separate responsibilities
const validateOrder = (order: Order): Result<Order, ValidationError> => {
  if (!order.items.length) return Result.fail(emptyOrderError());
  if (order.total < 0) return Result.fail(invalidTotalError());
  return Result.ok(order);
};

const saveOrder = (db: Database) =>
  async (order: Order): Promise<Order> => {
    const processed = { ...order, status: 'processed' };
    await db.orders.save(processed);
    return processed;
  };

const notifyUser = (notifier: Notifier) =>
  async (userId: string, message: string): Promise<void> => {
    await notifier.send(userId, message);
  };

// Compose in orchestrator
const processOrder = async (order: Order) => {
  const validation = validateOrder(order);
  if (validation.isFailure) return validation;

  const saved = await saveOrder(db)(validation.value);
  await notifyUser(emailService)(saved.userId, 'Order confirmed');

  return Result.ok(saved);
};

O - Open/Closed Principle

Open for extension, closed for modification.

Violation

// Bad: Must modify function to add new discount types
const calculateDiscount = (type: string, amount: number): number => {
  if (type === 'percentage') return amount * 0.1;
  if (type === 'fixed') return 10;
  if (type === 'loyalty') return amount * 0.15;
  return 0;
};

Correct

// Good: Extend via new strategies without modifying existing code
type DiscountStrategy = (amount: number) => number;

const discountStrategies: Record<string, DiscountStrategy> = {
  percentage: (amount) => amount * 0.1,
  fixed: () => 10,
  loyalty: (amount) => amount * 0.15,
};

// Easy to extend
discountStrategies.holiday = (amount) => amount * 0.25;

const calculateDiscount = (type: string, amount: number): number =>
  discountStrategies[type]?.(amount) ?? 0;

L - Liskov Substitution Principle

Subtypes must be substitutable for their base types.

Violation

// Bad: Square breaks Rectangle contract
class Rectangle {
  constructor(public width: number, public height: number) {}
  setWidth(w: number) { this.width = w; }
  setHeight(h: number) { this.height = h; }
  area() { return this.width * this.height; }
}

class Square extends Rectangle {
  setWidth(w: number) {
    this.width = w;
    this.height = w; // Breaks expectation!
  }
}

Correct

// Good: Use composition and explicit types
type Shape = {
  area: () => number;
};

const createRectangle = (width: number, height: number): Shape => ({
  area: () => width * height,
});

const createSquare = (side: number): Shape => ({
  area: () => side * side,
});

I - Interface Segregation Principle

Clients should not depend on interfaces they don't use.

Violation

// Bad: Fat interface
interface DataService {
  read(id: string): Promise<Data>;
  write(data: Data): Promise<void>;
  delete(id: string): Promise<void>;
  backup(): Promise<void>;
  restore(): Promise<void>;
  migrate(): Promise<void>;
}

// Client only needs read
const reportGenerator = (service: DataService) => {
  // Only uses service.read(), but depends on entire interface
};

Correct

// Good: Segregated interfaces
type Reader<T> = {
  read: (id: string) => Promise<T>;
};

type Writer<T> = {
  write: (data: T) => Promise<void>;
};

type Deletable = {
  delete: (id: string) => Promise<void>;
};

// Client depends only on what it needs
const reportGenerator = (reader: Reader<ReportData>) => {
  // Only depends on read capability
};

// Compose interfaces as needed
type DataService = Reader<Data> & Writer<Data> & Deletable;

D - Dependency Inversion Principle

Depend on abstractions, not concretions.

Violation

// Bad: Direct dependency on implementation
import { PrismaClient } from '@prisma/client';

const createUserService = () => {
  const prisma = new PrismaClient(); // Hardcoded!

  return {
    findUser: (id: string) => prisma.user.findFirst({ where: { id } }),
  };
};

Correct

// Good: Depend on abstraction
type UserRepository = {
  findById: (id: string) => Promise<User | null>;
  save: (user: User) => Promise<User>;
};

const createUserService = (repo: UserRepository) => ({
  findUser: (id: string) => repo.findById(id),
  createUser: async (data: CreateUserData) => {
    const user = { id: generateId(), ...data };
    return repo.save(user);
  },
});

// Inject implementation
const prismaRepo: UserRepository = {
  findById: (id) => prisma.user.findFirst({ where: { id } }),
  save: (user) => prisma.user.create({ data: user }),
};

const service = createUserService(prismaRepo);

SOLID in Practice

Factory Function Pattern

// Follows all SOLID principles
type Dependencies = {
  userRepo: UserRepository;
  orderRepo: OrderRepository;
  paymentGateway: PaymentGateway;
  logger: Logger;
};

const createOrderProcessor = (deps: Dependencies) => {
  const validateOrder = (order: Order): Result<Order, ValidationError> => {
    // Single responsibility: validation only
  };

  const processPayment = async (order: Order): Promise<Result<Payment, PaymentError>> => {
    // Single responsibility: payment only
  };

  return {
    process: async (order: Order): Promise<Result<ProcessedOrder, OrderError>> => {
      const validation = validateOrder(order);
      if (validation.isFailure) return validation;

      const payment = await processPayment(validation.value);
      if (payment.isFailure) return payment;

      // Compose results
      return Result.ok({ order: validation.value, payment: payment.value });
    },
  };
};

Testing SOLID Code

describe('OrderProcessor', () => {
  it('should process valid order', async () => {
    // Easy to test due to dependency injection
    const deps = {
      userRepo: createFakeUserRepo(),
      orderRepo: createFakeOrderRepo(),
      paymentGateway: { charge: jest.fn().mockResolvedValue(Result.ok({})) },
      logger: { info: jest.fn() },
    };

    const processor = createOrderProcessor(deps);
    const result = await processor.process(createTestOrder());

    expect(result.isSuccess).toBe(true);
  });
});

Score

Total Score

60/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

+5
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon