DEV Community

Cover image for Some Tips Typescript Design Pattern
Taki089.Dang
Taki089.Dang

Posted on

Some Tips Typescript Design Pattern

Refer to TypeScript Design Patterns Book by Vilic Vane
Link Book here: https://www.amazon.com/TypeScript-Design-Patterns-Vilic-Vane/dp/178528083X

Analysis of Chapter 2: The Challenge of Increasing Complexity

Chapter 2 focuses on the complexities that arise as software systems grow in size and features. It explores how unstructured codebases can quickly become unmanageable and provides strategies to address this by identifying patterns and abstractions that improve maintainability.


Key Themes

  1. The Problem of Growing Complexity
  2. Building the Basics
  3. Common Mistakes in Development
  4. Improvement Techniques

1. The Problem of Growing Complexity

The chapter begins with a simple example: a client-server synchronization system. Initially, the system handles a single data type with basic logic, but as more features are added—such as handling multiple data types, multiple clients, or conflict resolution—the codebase becomes tangled and harder to maintain.

Real-Life Example:

Imagine a human resource system designed to sync employee data between the client and server. Initially, it syncs only the employee names. Later, you need to add additional fields like roles, salaries, and vacation days. Without a structured approach, the system becomes cumbersome and error-prone as new fields are introduced.


2. Building the Basics

To handle basic synchronization, the system compares timestamps of data entries between the client and server. The server sends data with the latest timestamp to the client, while the client sends back modified data with newer timestamps.

Basic Code Example in TypeScript:

type DataItem = { id: number; value: string; timestamp: number };

function syncToClient(serverData: DataItem[], clientData: DataItem[]): DataItem[] {
  return serverData.filter(serverItem => {
    const clientItem = clientData.find(item => item.id === serverItem.id);
    return !clientItem || clientItem.timestamp < serverItem.timestamp;
  });
}

function syncToServer(clientData: DataItem[], serverData: DataItem[]): DataItem[] {
  return clientData.filter(clientItem => {
    const serverItem = serverData.find(item => item.id === clientItem.id);
    return !serverItem || serverItem.timestamp < clientItem.timestamp;
  });
}
Enter fullscreen mode Exit fullscreen mode

This approach solves basic synchronization but lacks scalability as the system evolves.


3. Common Mistakes in Development

The author highlights several pitfalls encountered in poorly structured systems:

  • Unclear Relationships: Data is often passed as-is from the server to the client without addressing underlying dependencies or relationships.
  • Redundant Code: Repeated logic for similar operations increases maintenance overhead.
  • Lack of Abstraction: Direct handling of complex logic leads to a tangled and hard-to-maintain codebase.

Real-Life Example:

If the HR system links employees to departments and departments to organizations, failing to address these relationships during data synchronization might result in inconsistencies, such as assigning employees to non-existent departments.


4. Improvement Techniques

The chapter proposes strategies to overcome these challenges:

  • Identify Abstractions: Extract repeated logic into reusable functions or classes.
  • Break Down Complex Processes: Divide complex synchronization tasks into smaller, manageable components.
  • Apply Design Patterns: Use patterns like the Strategy Pattern to encapsulate synchronization logic, making it more modular and reusable.

Improved Code Using Strategy Pattern:

interface SyncStrategy {
  sync(data: DataItem[], reference: DataItem[]): DataItem[];
}

class SyncToClientStrategy implements SyncStrategy {
  sync(data: DataItem[], reference: DataItem[]): DataItem[] {
    return data.filter(item => {
      const ref = reference.find(r => r.id === item.id);
      return !ref || ref.timestamp < item.timestamp;
    });
  }
}

class SyncToServerStrategy implements SyncStrategy {
  sync(data: DataItem[], reference: DataItem[]): DataItem[] {
    return reference.filter(item => {
      const ref = data.find(r => r.id === item.id);
      return !ref || ref.timestamp < item.timestamp;
    });
  }
}

// Applying Strategy
const syncStrategy: SyncStrategy = new SyncToClientStrategy();
const syncedData = syncStrategy.sync(serverData, clientData);
Enter fullscreen mode Exit fullscreen mode

Key Takeaways and Lessons

  1. Understand Requirements: Analyze data relationships and dependencies before implementing new features.
  2. Design for Scalability: Use patterns to build a flexible and maintainable system.
  3. Simplify Logic: Avoid overloading classes or functions with too much complexity.

Top comments (0)