KEMBAR78
System Design Notes | PDF | Computing | Information Technology
0% found this document useful (0 votes)
17 views241 pages

System Design Notes

The document outlines the System Design Life Cycle (SDLC), detailing its phases such as requirement gathering, system design, implementation, testing, deployment, and maintenance, while emphasizing the importance of functional and non-functional requirements. It also discusses techniques for gathering system requirements, the significance of capacity estimation, and key concepts of Object-Oriented Programming (OOP) including classes, objects, inheritance, and polymorphism. Overall, it highlights best practices for effective software development and the importance of stakeholder involvement.

Uploaded by

palash jain
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views241 pages

System Design Notes

The document outlines the System Design Life Cycle (SDLC), detailing its phases such as requirement gathering, system design, implementation, testing, deployment, and maintenance, while emphasizing the importance of functional and non-functional requirements. It also discusses techniques for gathering system requirements, the significance of capacity estimation, and key concepts of Object-Oriented Programming (OOP) including classes, objects, inheritance, and polymorphism. Overall, it highlights best practices for effective software development and the importance of stakeholder involvement.

Uploaded by

palash jain
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 241

System Design Life Cycle (SDLC)

De nition:
The SDLC is a structured process used to develop and maintain software systems
effectively, ensuring each phase is well-planned and executed.

Phases of SDLC:
Requirement Gathering & Analysis: Understand user needs, gather requirements
from stakeholders.
System Design: High-Level Design (HLD) for system architecture and Low-Level
Design (LLD) for detailed components.
Implementation (Coding): Writing code based on design with best practices like
version control.
Testing: Includes Unit, Integration, Load, and User Acceptance Testing to ensure
functionality.
Deployment: Releasing the system to production, often in phases to monitor
feedback.
Maintenance: Ongoing updates, bug xes, and feature enhancements (Corrective,
Adaptive, Perfective).

Key Considerations: Scalability, security, thorough testing, and iterative feedback.

Common Pitfalls: Skipping requirement analysis, over-engineering, inadequate


testing.

Conclusion:
SDLC ensures structured, ef cient, and reliable system development, adaptable
through its iterative nature.

Functional vs Non-functional Requirements


When designing software systems like websites, apps, or enterprise solutions,
understanding the difference between functional and non-functional requirements is
key.

Functional Requirements: Specify what the system should do—its features and
operations.
E-commerce Example:
Product search capability
Adding items to a shopping cart
Processing payments
Admin updating product details

Non-functional Requirements: Focus on how the system performs to ensure a


quality user experience.
fi
fi
fi
E-commerce Example:
Search results load in under 3 seconds
Encryption of payment transactions for security
Handling 1 million users during peak hours

Key Differences:
Functional: "What should the system do?" (Features like login, order placement)
Non-functional: "How should the system perform?" (Speed, security, scalability)

Common Mistakes:
Ignoring non-functional aspects like performance
Vague requirements (e.g., "System should be fast" vs. "Load in under 2 seconds")
Excluding key stakeholder input

Takeaway: Both functional and non-functional requirements are vital. Functional


de nes the system's features, while non-functional ensures it performs ef ciently
under real conditions.

Examples:
Online Banking:
Functional: Log in, check balance
Non-functional: Respond in <2 seconds, encrypted transactions
Food Delivery App:
Functional: Browse menu, place order
Non-functional: Load menu in 1 second, handle 50,000 concurrent orders

By de ning both clearly, you build robust, user-friendly systems that meet
expectations.

Techniques for Gathering and Analysing System Requirements


1. What is Requirements Gathering?
It's the process of understanding and documenting the needs of users,
stakeholders, and regulatory bodies to ensure the system solves the right problems.
Functional Requirements: What the system should do (e.g., search functionality).
Non-functional Requirements: How well the system performs (e.g., system
speed).

2. Techniques for Gathering Requirements:


Interviews: In-depth chats with stakeholders (e.g., talking to warehouse staff for an
inventory system).
Workshops: Group discussions to capture diverse inputs (ideal for cross-functional
projects).
Surveys/Questionnaires: Collecting feedback from many users (e.g., bank asking
customers about website features).
fi
fi
fi
Observation: Watching users in action to spot issues (e.g., observing cashiers to
improve checkout software).
Prototyping: Visual models for stakeholder feedback (helpful when users can’t
articulate needs clearly).
Use Case Analysis: Speci c scenarios to de ne system interactions (e.g.,
customer booking a hotel room).

3. Analysing Requirements:
Prioritisation: Focus on must-haves (e.g., secure checkout for e-commerce).
Feasibility Analysis: Check technical and budget viability (e.g., real-time analytics
for millions of users).
Validation: Con rm requirements with stakeholders.
Grouping: Organise by categories (e.g., booking, payment processing).

4. Tools:
Excel/Google Sheets: For small projects.
Jira: Popular in agile environments.

5. Best Practices:
Involve the right people.
Ask open-ended questions.
Document clearly with measurable details.
Validate with stakeholders.
Use prototypes.
Be exible for changes.

Conclusion:
Effective requirements gathering and analysis ensure the nal system meets user
needs, avoids costly errors, and performs well.

System Requirements with Examples

Example 1: E-commerce Website


Stakeholders: Business owners, marketing teams, users
Key Functional Requirements:
User Registration: Sign up via email/social media
Product Search: Search by name, category, price
Shopping Cart: Add/review items before checkout
Payment Processing: Support multiple payment methods
Nonfunctional Requirements:
Performance: Load in under 2 seconds during peak hours
Scalability: Handle up to 10,000 users simultaneously
Security: Encrypt transactions and personal data
Validation: Use prototypes (wireframes) for stakeholder feedback
fl
fi
fi
fi
fi
Example 2: Healthcare Appointment System
Stakeholders: Doctors, nurses, admin staff, IT
Key Functional Requirements:
Appointment Scheduling: Book based on doctor availability
Patient Records: Access medical histories
Noti cations: Send reminders via SMS/email
Nonfunctional Requirements:
Performance: Handle 500 concurrent bookings
Reliability: 24/7 system availability
Data Privacy: Comply with healthcare data regulations
Validation: Observe current work ows to re ne requirements

Key Steps:
Prioritise Requirements: Focus on critical functionalities (e.g., payment processing,
security)
Feasibility: Check time/budget constraints
Documentation: Use tools like Jira/Excel to track requirements

Conclusion:
System requirements are vital for successful development. Functional requirements
de ne what the system does, while nonfunctional ones ensure how it performs.
Stakeholder involvement ensures user needs are met effectively.

Capacity Estimation: A Concise Guide

What is Capacity Estimation?


Capacity estimation predicts how much demand a system can handle, ensuring
smooth performance, growth planning, and cost ef ciency.
User Experience: Maintains fast, responsive systems during high traf c.
Growth Planning: Allocates resources wisely for future expansion.
Cost Ef ciency: Avoids unnecessary resource expenditure.

Key Metrics & Examples:


Daily Active Users (DAU):
Example: 40,000 DAU, 25% growth → 50,000 DAU.
Queries Per Second (QPS):
Example: 10,000 users × 5 requests/user ÷ 86,400 sec ≈ 0.578 QPS.
Storage Requirements:
Example: 50,000 users × 3 MB/user = 150 GB/day.
Response Time: Target under 1 second.
Error Rates:
Example: 200 errors in 100,000 requests → 0.2% error rate.
Concurrent Users:
fi
fi
fi
fl
fi
fi
fi
Example: 50,000 DAU × 20% active → 10,000 concurrent users.

Techniques:
Traf c Analysis: Review historical data.
Load Testing: Simulate user traf c (e.g., Apache JMeter).
Forecasting: Predict demand based on trends.
Stress Testing: Identify system limits.
Capacity Planning: Prepare for future scaling.

Tools:
Apache JMeter, LoadRunner: Load testing.
Prometheus, Grafana: Performance monitoring.

Real-World Example: Black Friday E-commerce Sale


Metrics: 200,000 DAU, 1,600,000 requests → 18.52 QPS, 1 TB storage, 50,000
concurrent users.
Testing: Simulate users, stress test beyond expectations.
Planning: Scale infrastructure, optimize resources.

Conclusion:
Effective capacity estimation ensures your system is ready for both everyday use
and traf c spikes, balancing performance, growth, and cost.

Introduction to OOPs
Procedural Programming:
De nition: Divides programs into functions/procedures.
Key Points:
Sequential Execution (linear ow)
Global Data (shared data, risks data integrity)
Modularity & Reusability (manageable code, reusable functions)
Challenges: Scalability issues, data security risks, limited abstraction.

Object-Oriented Programming (OOP):


Real-Life Example: Anita's Bakery
Class: Blueprint (Bakery - de nes name, location, treats).
Objects: Cakes, cookies (attributes: avour, size; behaviours: bake, decorate).

Core Concepts:
Class: Blueprint (e.g., Car with make, model, start/stop methods).
Object: Real-world entities (e.g., Account, Customer in banking).
Abstraction: Focus on essential features, hiding complexities.

Advantages of OOP:
Modularity: Break into manageable classes.
fi
fi
fi
fi
fl
fi
fl
Reusability: Inheritance, composition reduce redundancy.
Abstraction, Encapsulation: Simplify design, secure data.
Polymorphism: Flexibility via common interfaces.
Hierarchical Organization: Re ects real-world relationships.
Ease of Maintenance: Cleaner, organized code for updates.

Classes and Objects


Classes and Objects in OOP:
Classes are blueprints for creating objects. They de ne attributes (data members)
and methods (functions). Objects are instances of classes, holding speci c data
and behaviour.

Class Example in C++:

Output:

Go out and play


Siddhartha Hazra

Key Points:
fl
fi
fi
Attributes: Represent data (e.g., roll, name).
Methods: De ne behaviours (e.g., bunkClass(), takeLeave()).
Objects: Instances like sid that access class members.

Conclusion:
Classes and objects help model real-world entities, promoting code reusability and
organisation.

Constructors in C++
Constructors in C++ are special member functions automatically invoked when an
object of a class is created. They help initialise objects and set up their initial state.

Rules for Constructors in C++


Constructor Name: Must match the class name.
No Return Type: Constructors do not return any value, not even void.
Accessibility: Can be declared as public, protected, or private.
Default Constructor: Automatically provided by the compiler if no constructors are
de ned.
Constructor Overloading: Multiple constructors with different parameters can
exist within the same class.

Types of Constructors
Default Constructor (No-Argument Constructor)
Does not take any parameters.
Automatically provided if no other constructors are de ned.

Example:
fi
fi
fi
Parameterized Constructor
Takes arguments to initialise objects with speci c values.

Example:

Understanding constructors in C++ helps in ef cient object creation and


initialisation, enhancing code exibility and readability.

this Keyword

The this keyword in C++ is a pointer that refers to the current object instance. It
helps in accessing instance variables, invoking methods, and distinguishing
between local variables and class members when they have the same name.

Practical Applications:
fl
fi
fi
Bene ts:

Clarity: Highlights that a variable belongs to the current object.


Avoiding Ambiguity: Resolves con icts between local and instance variables.
Method Chaining: Facilitates uent interface design.

The this pointer in C++ is essential for object-oriented programming, enhancing


code readability and maintainability.
fi
fl
fl
Access Modi ers

De nition:

Access modi ers in C++ control the visibility and accessibility of classes, methods,
and variables, supporting encapsulation and data hiding.

Types of Access Modi ers:

Default Access:

In C++, class members are private by default, while struct members are public by
default.
fi
fi
fi
fi
Conclusion:

Choosing the right access modi er ensures better encapsulation, security, and code
organization in C++.

Object-Oriented Programming (OOP) Concepts

Fields in OOP

Fields represent attributes or properties of objects, de ning their state.

Example (C++):

Types of Fields:

Instance Fields: Unique to each object.


Static Fields: Shared across instances.
Constant Fields: Immutable after initialisation.
fi
fi
Method Overloading

Allows multiple functions with the same name but different parameters.

Example (C++):

Bene ts:

Improved readability
Flexibility with data types
Enhanced reusability

Data Hiding & Encapsulation

Data Hiding: Protects internal state, exposing only necessary functionalities.


Encapsulation: Bundling data and methods, restricting direct access.

Example (C++):
fi
Advantages:

Data security
Flexibility in access control
Reusability
Simpli ed testing

Abstraction in OOP

De nition:
Abstraction is the process of hiding implementation details and showing only essential
features of an object. It allows focusing on what an object does, not how it does it.

Real-Life Example:
An ATM: Users interact with a simple interface to withdraw cash or check balances, while
the complex internal workings (card veri cation, balance checks) are hidden.

Achieving Abstraction in C++:


Using Abstract Classes (with pure virtual functions)
Using Interfaces (achieved via abstract classes in C++)

C++ Example:
fi
fi
fi
Output:
Savings Account Balance: Rs 500

Key Points:
Account is an abstract class with pure virtual functions deposit() and withdraw().
SavingsAccount implements these functions.
Users of SavingsAccount need not know internal implementation details.

Why Abstraction?
Manages Complexity: Simpli es large systems.
Enhances Modularity: Promotes clean, reusable code.
Encapsulation: Hides sensitive internal workings.
Supports Polymorphism: Enables uniform handling of diverse objects.
Simpli es Design: Focuses on essential features.

Conclusion:
Abstraction is essential in OOP for creating exible, maintainable, and scalable software. In
C++, it is achieved through abstract classes and pure virtual functions, promoting clean
design and reducing complexity.
fi
fi
fl
Interfaces (Abstract Classes in C++)
In C++, similar functionality is achieved through abstract classes with pure virtual
functions.

Syntax Example (C++):

Key Points:
Animal is an abstract class with pure virtual functions.
Dog and Bird classes implement these functions.
Demonstrates polymorphism and abstraction.

Inheritance
Inheritance allows one class to acquire properties and behaviours of another.

Syntax Example (C++):


Key Points:
Employee is the base class.
Manager and Developer are derived classes.
Demonstrates code reuse and method overriding.

Polymorphism in C++

Polymorphism, meaning "many forms," is a core concept in object-oriented


programming (OOP) that allows objects to behave differently based on their context
while sharing a common interface. It provides exibility, modularity, and
maintainability in code.
fl
Real-Life Analogy

Consider "Ravi," who plays multiple roles: father, husband, and employee. Each
role has distinct responsibilities. Similarly, polymorphism allows objects to have
different behaviours based on their speci c implementations.

Types of Polymorphism in C++

Compile-Time Polymorphism (Method Overloading/Operator Overloading)


Occurs when decisions are made at compile time.
Method Overloading: De ning multiple functions with the same name but different
parameter lists.

Output:
Sum of 10 and 20: 30
Sum of 10, 20, and 30: 60
Sum of 10.5 and 20.5: 31

Runtime Polymorphism (Method Overriding using Virtual Functions)


Achieved through inheritance and virtual functions. The function call is resolved at
runtime.
Example:
fi
fi
Output:
Car Fuel Ef ciency: 4 km/l
Motorcycle Fuel Ef ciency: 0.375 km/l

Conclusion

Polymorphism in C++ enhances code exibility and reusability through compile-time


(method/ operator overloading) and runtime (virtual functions) mechanisms. This
enables the development of modular, adaptable, and maintainable software
systems.

Relationships in Object-Oriented Programming (OOP)

Introduction to Relationships

In OOP, class relationships are key to designing robust, scalable applications.


These include part-of, has-a, association, aggregation, and composition
relationships.
fi
fi
fl
Class Object Interaction

Classes de ne blueprints for objects with attributes and methods. Objects interact
by exchanging data and invoking methods, allowing modular, maintainable, and
scalable code.

Part-of Relationship

Signi es one class as part of another, e.g., a Computer class with CPU, RAM, and
Hard Drive components.
Components depend on the larger entity for their existence.

Has-a Relationship

A class contains references to objects of another class without controlling their


lifecycle.
Example: A Company has-a list of Employee objects.

Association

A generic connection between classes, including both part-of and has-a


relationships.
Focuses on object interactions without lifecycle dependencies.

Aggregation (C++ Example)

A weak relationship where parts exist independently of the whole.

Books exist even if the library is destroyed.


fi
fi
Composition (C++ Example)

A strong relationship where parts depend on the whole.


The engine can't exist without the car.

Conclusion

Understanding these relationships enables the creation of well-structured,


maintainable, and scalable OOP systems in C++.

Introduction to Object-Oriented Analysis and Design (OOAD)


Object-Oriented Analysis and Design (OOAD) is a methodology in software
engineering focused on analysing, designing, and developing systems based on
object-oriented principles. Using C++ as a reference, OOAD leverages classes,
objects, inheritance, and polymorphism to create ef cient and maintainable
software.

Object-Oriented Analysis (OOA)


OOA involves identifying real-world entities as objects in C++, de ning their
attributes (data members) and behaviours (member functions).
Identifying Objects: Recognise entities relevant to the problem domain (e.g., class
Car).
De ning Attributes and Behaviours: Specify properties (int speed) and methods
(void accelerate()).
Modelling Relationships: Use inheritance (class ElectricCar : public Car),
association, and aggregation.
Use Cases: Analyse system-user interactions to de ne requirements.
Class Diagrams: Visualise C++ classes, attributes, methods, and relationships.
Iterative Re nement: Continuously update models based on stakeholder
feedback.

Object-Oriented Design (OOD)


OOD transforms analysis models into implementable C++ code:
Abstraction: Focus on essential features (abstract classes, pure virtual functions).
Encapsulation: Bundle data and methods within classes, controlling access with
access speci ers (private, public).
Inheritance: Enable code reuse through base and derived classes.
Polymorphism: Implement dynamic behaviour using virtual functions.
Modularity: Structure code into independent classes for easier maintenance.
Design Patterns: Apply best practices like Singleton, Factory, etc., in C++.

Bene ts of OOAD
fi
fi
fi
fi
fi
fi
fi
Modularity: Independent classes (class User, class Product).
Flexibility & Scalability: Easily extendable with new classes.
Reusability: Code reuse via inheritance and libraries.
Maintainability: Clear class structures enhance readability.
Interoperability: De ned interfaces for component interaction.

Real-World Applications
Banking Systems: Model accounts and transactions.
Healthcare: Manage patient records.
Aerospace: Flight control systems.
Telecom: Billing systems.
E-commerce: Shopping platforms.

Conclusion
OOAD, with C++ references, facilitates robust, scalable, and maintainable software
systems. By applying OOP principles like encapsulation, inheritance, and
polymorphism, developers can create effective solutions tailored to user needs.

Introduction to the Uni ed Modeling Language (UML)

UML is like a blueprint for software development, ensuring clear communication


among developers, architects, and stakeholders. It helps in visualising, designing,
and documenting how a system works.

What is UML?

UML (Uni ed Modeling Language) is a standardised system of notations to


visualise, specify, construct, and document software systems. It acts as a common
language for software engineers, designers, and stakeholders.

Key Features of UML:

Standardised Notations: Symbols and diagrams to represent classes, objects,


relationships, and behaviours.
Abstraction & Modularity: Breaks complex systems into manageable
components, enhancing reusability.
Multiple Viewpoints: Supports different perspectives like architectural and
behavioural views.
fi
fi
fi
Tool Support: Offers modelling tools with features like code generation (applicable
for C++), version control, and collaboration.
Flexibility & Scalability: Adapts to methodologies like object-oriented (e.g., C++),
structured, and agile approaches.

UML in Use:

Requirements Analysis: Use case diagrams capture user interactions and system
goals.
Design & Architecture: Class and component diagrams de ne system structure,
ideal for C++ designs.
Behavioural Modelling: Sequence, activity, and state machine diagrams model
system behaviour.
Documentation: UML diagrams serve as visual documentation.
Communication: Acts as a common language for teams.

Conceptual Modelling in UML:

Object: Real-world entities.


Class: De nes attributes and methods (parallels C++ classes).
Abstraction: Focus on essentials.
Inheritance: Promotes code reuse in C++.
Polymorphism: Enables exible behaviours, core in C++.
Encapsulation: Data hiding and modular design, key in C++ classes.

UML complements C++ development by aiding in code structure visualisation,


system design, and improving collaboration.

Unified Modelling Language (UML) is a powerful tool for

visualising and specifying software systems. It has three main building blocks:
things, relationships, and diagrams. UML enables developers to create
fi
fl
fi
comprehensive models of system architecture and behaviour. Let’s look at the basic
UML building blocks:

1. Things: These represent real-world entities or concepts within the software


system. They are categorised into structural things, behavioural things, grouping
things, and annotational things.

1. - Structural things represent the static aspects of the system, such as


classes, objects, and components.

2. - Behavioural things represent the dynamic aspects of the system, including


activities, states, and interactions.
3. - Grouping things organise and encapsulate related elements within the
model, facilitating modular design and clarity.
4. - Annotational things provide additional information or comments within the
diagram to enhance understanding and documentation.
5.

These things form the basis for constructing UML diagrams, enabling developers to
represent various aspects of the system comprehensively.

1. Structural things: These represent physical aspects of the system, such as


classes, objects, interfaces, use cases, actors, components, and nodes.
2. Object: A class instance during runtime.

3. Interface: A contract defining a set of methods that a class must implement.

A use case outlines a user’s goal or requirement and the system’s interactions with
them.

An actor is an external entity (like a user or system) that interacts with the system.
A component is a modular part of a system, usually a software component or
subsystem.

Node: A physical element in the system architecture, like a hardware node or


server.

2. Behavioural aspects.

1. Activity diagrams: Illustrate the flow of activities or tasks within a system.


Interaction diagrams show how objects or components interact in a particular
situation.

Interaction Diagram

3. Grouping:
Packages are the primary means of organising and grouping elements in a UML
model. They enable modular design and management by encapsulating related
components. Packages have a hierarchical structure, allowing for nested levels of
organisation.

Annotation is used to capture remarks, descriptions, and comments related to


UML model elements. It functions as yellow sticky notes, providing additional
information and enhancing clarity and communication within the model by providing
context and explanations for various elements.
2. Relationships: These establish connections between things in a UML diagram,

adding semantic information. Common types include:

● - Association: Represents a structural relationship between classes or


objects.

● - Dependency: Indicates a relationship where one element relies on another.


● - Inheritance: Illustrates a hierarchical relationship where one class inherits
attributes and behaviours from another.
● - Aggregation and Composition: Depicts part-whole relationships between
classes or objects.
By defining relationships, developers can convey system dependencies and
interactions effectively.

3. Diagrams: These visualise the UML model, providing different


perspectives on the system architecture and behaviour. Common types include:
● - Class Diagram: Illustrates the system’s static structure, depicting classes,
attributes, and relationships.

● - Use Case Diagram: Describes the system’s functionalities from the user’s
perspective, showing interactions between actors and use cases.
● - Sequence Diagram: Represents the system’s dynamic behaviour,
illustrating the sequence of interactions between objects over time.
● - Activity Diagram: Models the flow of control within the system, depicting
activities and decision points.
Each diagram offers a unique view of the system, catering to different stakeholders
and software development phases.
Note: We’ll explore the relationships and diagrams of building blocks in
upcoming articles.

● UML offers flexibility and clarity, making it suitable for non-technical


stakeholders. Its widespread adoption and community support facilitate
collaborative work and knowledge sharing. UML aids in breaking down
complex systems into manageable components, enhancing clarity and
scalability in system design and development.

In conclusion, Unified Modelling Language (UML) is a foundational pillar in


modern software engineering, providing a standardised and comprehensive
approach to modelling and visualising software systems. Its diverse diagrams and
notations enable developers, designers, and stakeholders to communicate, design,
and analyse complex systems effectively. UML promotes collaboration, enhances
understanding, and streamlines the software development process. As technology
evolves, UML remains an indispensable tool for capturing, documenting, and
communicating the intricacies of software systems across various domains and
industries.

Use case diagram

A Use Case Diagram, part of the Unified Modelling Language (UML), visualises the
interaction between users and the system to achieve specific tasks. It’s particularly
useful in low-level design, clarifying system requirements and illustrating entity
interactions. This article explains Use Case Diagrams, their elements, and their use
in software design, with helpful examples.

What is a Use Case Diagram?


A Use Case Diagram depicts the interaction between actors (users or other
systems) and a system to achieve goals. It helps understand:
● Actors (external entities interacting with the system)

● Use cases (what actors can do with the system)


● System boundaries

Key Components of a Use Case Diagram


Actors, Use Cases, and System Boundary represent interactions between users
and the system.

1. Actors
Actors are external entities interacting with the system. They can be humans,
external devices, or other software systems. There are two types:

● Primary Actor: The main user or initiator of the interaction, triggering the
system to begin a use case.

● Secondary Actor: Supports the primary actor in achieving the goal, like a
supporting actor in a film.

Examples of Actors: For an online shopping system, the primary actor could
be the customer (initiates the shopping process), while the secondary actor
could be a payment gateway (supports the transaction).

Diagram Notation for Actors: Actors are represented as stick figures in Use Case
Diagrams.
Use cases are specific actions or tasks that actors can perform within a system.
Each use case represents a function or goal that meets the needs of the actors.

Examples of use cases include browsing products, adding items to the cart,
and making payments in an online shopping system. These are interactions
between the customer and the system.

Use cases are represented by ovals, each labelled with the action it represents.

The System Boundary defines the scope of a system being modelled, visually
separating it from the external world and encapsulating all its use cases and actors.
In Use Case Diagrams, it’s represented as a rectangle enclosing all the use cases,
with the system name at the top.

Use Case Diagrams are crucial in low-level design for several

reasons: they clarify system requirements by mapping interactions between actors


and the system, highlight responsibilities by defining tasks, improve communication
with stakeholders through simple visual representations, and guide implementation
by ensuring all necessary interactions are covered.

Example of Use Case Diagram and Best Practices for Creating Use Case
Diagrams.
● Identify key actors that will interact with the system.
● Define clear use cases that represent specific actions or goals.
● Limit scope with system boundaries to focus on relevant use cases.
● Use consistent UML notation for actors, use cases, and boundaries.

Use Case Diagrams provide a high-level view of a system’s functionality, illustrating


interactions between actors and use cases. They clarify system requirements,
ensure all necessary actions are covered, and serve as a communication tool for
discussing system behaviour with stakeholders. By representing interactions and
accomplishments, they’re invaluable tools in designing software systems that meet
user needs and provide a clear guide for implementation.

Class diagram

Class diagrams are crucial in object-oriented modelling, providing a graphical


representation of system structure and relationships. They serve as a blueprint for
software development, aiding design, analysis, and communication.

A class diagram is a static structure diagram in UML, visualising a system’s


structure in terms of classes and their relationships. Classes represent abstract
entities, encapsulating data and behaviours. They form templates for creating
objects, instances of classes.

● Class diagrams offer a concise overview of a software system, showcasing


attributes, classes, functions, and relationships. They visually organise class
names, attributes, and functions, aiding development. As a structural
diagram, they encompass classes, interfaces, associations, collaborations,
and constraints, making them essential for understanding and designing
complex systems.
1. Class diagrams are essential in software development due to the complexity
of systems and the need for effective management. They provide a clear
visual representation, simplifying complex systems and enhancing
maintainability and scalability. Class diagrams serve as a common language
for communication, reducing misunderstandings. They help validate system
designs early, identifying flaws and inconsistencies.
2. Code generation automates the process, speeding up implementation and
ensuring consistency. Class diagrams aid planning and resource allocation
by identifying classes, attributes, and methods. They provide valuable
documentation of a system’s architecture, aiding future maintenance and
knowledge transfer.

A class diagram consists of a rectangle divided into three sections: the class name,
attributes, and methods. For instance, the Car class has a class name, attributes,
and methods.

In a class diagram, a class is represented by a rectangle. The class name is in the


rst section, followed by the attributes (brand, model, year) in the second section,
and the methods (startEngine(), accelerate(), brake()) in the third section. Attributes
and methods are denoted by their visibility (public, private, or protected) followed by
their names and types. Access modifiers are crucial for defining the visibility of
attributes and methods within a class. Public members are accessible from
anywhere in the system, private members can only be accessed from within the
fi
class, and protected members are accessible within the class and its derived
classes.

Access modifiers and relationships in class diagrams.

Object-oriented modelling uses associations to facilitate communication and


interaction between objects. Associations can be classified into two main types:
class association (inheritance) and object association.

● Class association, also known as inheritance, involves creating a new


class from existing ones, inheriting their attributes and behaviours. This
results in a hierarchical relationship where a child class inherits
characteristics from its parent(s).
● To represent inheritance in a class diagram, draw a solid line with a hollow
arrowhead from the child class to the parent class.
Class diagram illustrating multi-level inheritance.

● Object association is a crucial concept in object-oriented programming


and modelling, representing relationships between objects in a system.
These relationships define how objects interact and collaborate to achieve
specific functionality. Object associations can be categorised into several
types, each with a specific purpose.
1. Simple Association: This is the most basic form of object relationship, indicating a
connection between two objects without implying ownership or containment. It
represents a general relationship where one object is related to another object. It
enables one object to reference another without implying ownership or dependency.
The class diagram for a simple association.

● Each Book object has a reference to an Author object through the author
attribute.
● Each Book can have only one Author, but an Author can have multiple
Books.
● This represents a simple association where a Book is associated with an
Author through a reference.

Aggregation describes a relationship between a container object and the objects it


contains. An object may contain an aggregate of another object. This relationship is
denoted by a line with an unfilled diamond head pointing towards the container.

Aggregation is considered a weaker relationship than composition because:

● Aggregate objects are not considered integral parts of the container; they
are merely associated with it.

● Aggregate objects can exist independently of the container object.

Here’s an example to illustrate aggregation:


The class diagram of aggregation.

Composition is a relationship where an object is made up of smaller objects that are


considered integral parts of the whole. It’s denoted by a line with a filled diamond
head at the composer class pointing to the component class. Composition is a
strong relationship because:

● The composed object becomes a part of the composer object.

● Composed objects can’t exist independently; their existence is tightly


coupled with the composer object.

Here’s an example to illustrate composition:


The

class diagram of composition shows how one class depends on another for
its implementation. Changes to the dependent class can affect it. Dependency is
represented by a dashed arrow from the dependent class to the class it depends
on.
● Class diagrams represent dependencies in software systems. For instance,
the Order class depends on the EmailService class to send email
notifications, relying on the sendEmail() method in EmailService.

● Class diagrams are useful for designing software systems, understanding


existing systems, and communicating design decisions and requirements.
They can also be used for code generation, testing, and maintenance.

In conclusion, class diagrams are essential tools in object-oriented modelling,


providing a visual representation of system structure and relationships. They enable
developers to effectively design, analyse, and communicate software solutions,
contributing to the success of projects throughout the software development
lifecycle.

Sequence diagram

Sequence diagrams, a form of communication diagram within the Unified Modelling


Language (UML), illustrate interactions between actors and objects. They depict
message exchanges between entities, conveying their types and nature.

● Sequence diagrams also provide a detailed representation of the sequence


of events in a specific use case. They offer insights into the flow of control
and the logic behind operations and functions.

By visualising interactions and messages, sequence diagrams are invaluable tools


for system analysis, design, and documentation. They help stakeholders
understand the system’s behaviour, identify bottlenecks, and validate functionality.
In summary, sequence diagrams model the dynamic aspects of a system,
capturing events and interactions that drive its behaviour. They provide a
clear representation of how components collaborate to achieve objectives.

Components of Sequence Diagrams:

1. Lifeline: Each entity participating in an interaction is represented horizontally


with a lifeline illustrating its existence. Lifelines indicate when entities are activated
or deactivated, providing a timeline perspective. Different entities, such as objects,
actors, entities, or boundaries, are represented using lifelines, each with its distinct
timeline. Lifelines should not overlap to maintain clarity and coherence.
Below is an illustration showing how to visualise a lifeline in UML. The horizontal
boxes represent the interacting objects, with their lifelines shown as dotted lines
below.

In a sequence diagram, a lifeline represents an entity participating in an interaction.


It’s a dotted line below the object’s name, showing its existence and activation
during the interaction.

Activation bars are graphical elements in sequence diagrams that represent the
active period of an object during an interaction. They illustrate the duration when an
object processes messages, either sending or receiving them. Activation bars are
simple vertical boxes drawn on the lifeline of the corresponding object.

● In a sequence diagram, the activation bar below an object’s lifeline


represents its duration of activity and message processing. Messages
represent interactions between objects, exchanging information or triggering
actions. They can be horizontal and flow in any direction. Different types of
messages are represented by different arrows or notations. Synchronous
messages are where the sender waits for the receiver to respond before
proceeding, denoted by a solid line with a filled arrowhead from sender to
receiver.

● Synchronous messages are messages where the sender waits for a


response from the receiver before continuing to send messages.
Asynchronous messages, on the other hand, are represented by a dotted
line with an open arrowhead from the sender to the receiver, indicating a
fire-and-forget communication where the sender doesn’t expect an
immediate response.
Synchronous return messages are responses sent by receivers to
acknowledge the receipt and processing of synchronous messages. They
indicate that the receiver has processed the message and sent a response.
These messages are represented by a dotted line with a filled arrowhead
pointing from the receiver to the sender.

A synchronous return message indicates the creation of a new object or entity as a


result of a message or operation. It represents the instantiation of a new object and
its association with the sender. Create messages are represented by a dashed line
with an arrowhead pointing from the sender to the newly created object.
A destroy message signifies the termination of an object during a sequence of
events. It represents the object’s end and removal from the system. Destroy
messages are represented by a dashed line with an arrowhead pointing from the
sender to the object being destroyed.

A destroy message.

● Lost and Found Messages:


● Lost and found messages are messages with unknown senders or
receivers.
● A lost message is a received message with an unknown sender, while a
found message is an initiated message with an unknown receiver.
● These messages are represented by arrows ending or starting with a circle,
respectively.
● Sequence diagrams are a visual representation of system behaviour,
showing the sequence of interactions and message flows between objects
or components. They serve as a communication tool for stakeholders, aiding
requirements analysis by capturing expected interactions and identifying
potential design flaws. Sequence diagrams also help validate system
designs by visualising the expected sequence of interactions and ensuring
they meet functional requirements. Furthermore, they assist in testing and
debugging by providing a clear understanding of component interactions and
message flows.

Activity diagram

Activity diagrams are a crucial tool in software engineering and systems analysis.
They represent workflows, showing the sequence of activities within a system,
process, or workflow. Introduced as part of the Unified Modelling Language (UML),
activity diagrams are a standard method for visualising and communicating system
control flow.

Components of an Activity Diagram:

1. Initial Node: Represents the workflow’s start.

- Drawing: Use a solid circle to represent the initial node. Place it at the
diagram’s beginning to indicate the workflow’s start.
Actions represent the work performed during a workflow and
are the main building blocks of a process diagram. They are drawn as
rounded rectangles with the activity name inside.

3. Flow Final:

A flow final marks the end of a single path in an activity diagram, signifying
the completion of a specific sequence of actions.

Draw a circle with a hollow centre to represent the flow final. Place it at the
end of the path to indicate its completion.
The activity final represents the conclusion of all activities within the
diagram, indicating the end of the entire process or workflow. Draw a solid
circle with a hollow centre to represent it, placing it at the diagram’s end to
signify the process’s conclusion.

Control flow shows the sequence of actions in a diagram. It connects


actions, defining their order of execution. Use arrows to represent control
flow. Draw an arrow from one action to another to indicate the direction of
execution between activities.

Object flow represents the movement of objects or data throughout an


activity, tracking the flow of resources or information between activities. Use
a dashed line with arrows to represent object flow, connecting actions to
show the movement of objects or data.
A decision represents a branch in a workflow where the direction of
execution depends on conditions or criteria. Draw a diamond shape to
represent it. Write the condition for each possible path beside the diamond.
Connect the diamond to the outgoing paths with arrows labelled by the
corresponding conditions.

Merge is similar to a decision, but it signifies the merging of multiple paths


into a single flow. It consolidates divergent paths into a unified sequence of
actions. To represent a merge, draw a diamond shape similar to a decision
and connect it to the incoming paths using arrows. This indicates that
multiple paths converge at this point.
Fork and Join are control structures that allow concurrent
activities to occur simultaneously. Fork splits a single flow of
control into multiple parallel paths, while Join merges multiple parallel paths
back into a single flow, synchronising them. To represent a fork, draw a bar
perpendicular to the flow of control to split it into parallel paths. To represent
a join, draw a bar perpendicular to the flow of control to merge the parallel
paths back into a single flow.

Activity diagrams use a standardised notation to represent components.


Here are common symbols:
- Activity: Rounded rectangle.
- Transition: Arrow connecting activities.
- Decision/Branch: Diamond with multiple outgoing transitions.
- Start Node: Solid circle.
- End Node: Solid circle with a hollow circle inside.
- Fork: Bar splitting into multiple parallel paths.
- Join: Bar merging multiple parallel paths.

Activity diagrams have practical applications in software development,


business process modelling, and system analysis.

● In software development, they model the workflow of a software system,


including user interactions and data processing. They help developers
understand task sequences and serve as a blueprint for implementation.
● In business process modelling, they model and analyse business processes
within an organisation. By visually representing activities, decisions, and
dependencies, they identify inefficiencies and areas for improvement.
● In system analysis and design, they capture and document system
behaviour. They enable stakeholders to visualise component interactions,
subsystems, and external entities, facilitating communication and
consensus.

To create effective activity diagrams, follow these best practices:

● Keep it simple: Focus on capturing essential workflow aspects without


unnecessary complexity. Use clear and concise labels for activities and
transitions.

● Ensure consistency in notation and layout throughout the diagram to


improve readability. Use descriptive labels for activities and transitions to
convey their purpose accurately. Regularly review and validate the diagram
with stakeholders to ensure alignment with requirements. Activity diagrams
are iterative and should be refined and updated as the system
understanding improves or requirements change.
Activity diagrams are powerful tools for modelling, analysing, and documenting
system or process flows. They enhance communication, understanding, and the
design and implementation of complex systems.

Intro to SOLID Principles

In software development, making programs that work is just the beginning. Making
sure they're easy to understand, change, and grow is just as important. That's
where SOLID design principles come in. Think of them as rules to help build
software that's like a sturdy building, easy to maintain and expand upon.

In software development, creating robust, scalable, and maintainable code is


imperative. One methodology that aids in achieving these goals is SOLID design
principles. SOLID is an acronym that stands for five key principles of object-oriented
programming and design, namely the Single Responsibility Principle (SRP), Open/
Closed Principle (OCP), Liskov Substitution Principle (LSP), Interface Segregation
Principle (ISP), and Dependency Inversion Principle (DIP). These principles, coined
by Robert C. Martin, emphasize building modular, flexible, and easy-to-extend
software.
SOLID design principle in object-oriented design

SOLID Design Principles

Software complexity continues to grow, and as projects expand, the challenges of


maintaining and enhancing them increase as well. Without proper design principles,
software systems can become dif cult to manage, prone to bugs, and hard to
modify or extend. This is where SOLID principles come into play, especially within
the context of C++ development.

Bene ts of SOLID Principles in C++ Development

Implementing SOLID principles in C++ offers multiple advantages:


Improved Maintainability: Modular, well-structured code simpli es debugging,
updates, and refactoring.
Enhanced Scalability: Facilitates code extension without disrupting existing
functionality, crucial for large-scale C++ applications.
Reduced Coupling: Encourages loose coupling and high cohesion, making
components easier to manage and test.
Increased Testability: Clear abstractions and responsibilities improve unit testing
and integration testing ef ciency.
fi
fi
fi
fi
Facilitated Collaboration: Clear design patterns and best practices promote
consistent development across teams.

✅ SOLID Principles
SOLID is an acronym for 5 key design principles of object-oriented programming.
Applying them leads to modular, extensible, maintainable, and testable systems.

🧱 S – Single Responsibility Principle (SRP)

🔹 De nition

A class should have only one reason to change. It should focus on a single responsibility.

💡 Real-World Analogy

Think of tools: a screwdriver should only screw, not cut or hammer.

✅ Bene ts

• Easier debugging and testing

• Clear separation of concerns

• Improves reusability

❌ Bad Example (Mixed Concerns)

class Task {
public:
void markAsDone();
void saveToFile(); // Persistence
void sendNotification(); // Communication
};
✅ Good Example (SRP Applied)

class Task { /* Only business logic */ };


class TaskRepository { void save(const Task&); };
fi
fi
class TaskNotifier { void notify(const Task&); };

🛠 How to Apply SRP

• 🔍 Ask: “Would a change in X require touching this class?”

• 🔧 Break down classes that have UI, business logic, and I/O mixed together.

• 👥 Assign responsibilities clearly for testing and team ownership.

🧱 O – Open-Closed Principle (OCP)

🔹 De nition

Software should be open for extension but closed for modi cation.

💡 Real-World Analogy

Plugin system: new le formats added to VLC media player without modifying the core.

✅ Bene ts

• Add new features safely

• Prevent regression in tested code

✅ Good Example (Noti cation System)

class INotificationChannel {
public:
virtual void send(const std::string& msg) = 0;
};

class EmailChannel : public INotificationChannel { void


send(...) override; };
class SMSChannel : public INotificationChannel { void
send(...) override; };

class NotificationService {
fi
fi
fi
fi
fi
std::vector<std::shared_ptr<INotificationChannel>>
channels;
public:
void
addChannel(std::shared_ptr<INotificationChannel>);
void notifyAll(const std::string& msg);
};

🛠 How to Apply OCP

• 🧱 Program to interfaces (INotificationChannel)

• 🔌 Use inheritance or composition to plug in new behavior

• 🚫 Avoid large if-else or switch statements that check types

🧱 L – Liskov Substitution Principle (LSP)

🔹 De nition

Derived classes should be substitutable for their base classes without altering program
behavior.

💡 Real-World Analogy

A Car is a type of Vehicle. If someone replaces your vehicle with a car, it should still work
as expected.

✅ Bene ts

• Polymorphism without surprises

• Cleaner, predictable inheritance

❌ Violation Example

class Bird { virtual void fly(); };


class Ostrich : public Bird { void fly() override; /*
throws! */ }; // Violates LSP
fi
fi
✅ Fixed Example

class Bird { virtual void layEggs(); };


class FlyingBird : public Bird { virtual void fly(); };
class Sparrow : public FlyingBird { void fly()
override; };
class Ostrich : public Bird { /* no fly method */ }; //
✅ Follows LSP

🛠 How to Apply LSP

• 🔍 Ensure derived classes don’t violate expectations from the base

• ✅ Use only necessary behavior in base classes

• 🧪 Unit test derived classes through base class pointers

🧱 I – Interface Segregation Principle (ISP)

🔹 De nition

Clients should not be forced to depend on interfaces they do not use.

💡 Real-World Analogy

You don’t want a microwave with 50 buttons if you only ever use Start, Stop, and Timer.

✅ Bene ts

• Reduces unnecessary coupling

• Makes interfaces clean and purposeful

❌ Violation Example

class IMachine {
public:
virtual void print();
virtual void scan();
fi
fi
virtual void fax(); // Not needed for every machine
};
✅ ISP Applied

class IPrinter { virtual void print(); };


class IScanner { virtual void scan(); };

class AllInOne : public IPrinter, public IScanner { /


*...*/ };
class BasicPrinter : public IPrinter { /*...*/ };

🛠 How to Apply ISP

• 🔧 Break large interfaces into smaller, role-speci c ones

• 👩💻 Favor composition of small interfaces

• ✅ Keep only the minimal contract required for each client

🧱 D – Dependency Inversion Principle (DIP)

🔹 De nition

High-level modules should not depend on low-level modules. Both should depend on
abstractions.

💡 Real-World Analogy

You charge your phone using a USB interface, not a wire directly soldered to the battery.

✅ Bene ts

• Decouples components

• Enables mocking for testing

• Supports plug-and-play architecture

✅ Good Example (with INotificationChannel)


fi
fi
fi
class NotificationService {
std::vector<std::shared_ptr<INotificationChannel>>
channels;
public:
void
addChannel(std::shared_ptr<INotificationChannel>);
void notifyAll(const std::string& msg);
};
• NotificationService depends only on the interface, not on
EmailChannel, SMSChannel, etc.

🛠 How to Apply DIP

• 🧱 Use interfaces or abstract classes for low-level dependencies

• 🧩 Inject dependencies via constructor (Dependency Injection)

• ✅ Avoid directly creating objects inside high-level modules (new


EmailChannel())

📦 Combined Use Case: Noti cation System


Each principle in action:

Princip
Applied As
le

SRP
EmailChannel, SMSChannel, NotificationService each have
one job
Add new channels like SlackChannel without touching
OCP
NotificationService
LSP All channels conform to INotificationChannel and can be substituted
ISP Each sender implements only send() — minimal interface
DIP NotificationService depends on abstraction, not concrete channels
fi
🧾 Summary Table
Principl
Keyword Goal Technique
e
Split classes by
SRP Responsibility One reason to change
concern
OCP Extensibility Add, don't modify Use polymorphism
Substitutabilit Replace base with
LSP Respect base contracts
y derived
ISP Interface Size Keep interfaces lean Split interfaces
DIP Abstraction Depend on interfaces Inversion + injection

#include <iostream>
#include <vector>
#include <string>
#include <memory>

//✅ SRP: Represents the message being sent. Handles only


message content.
class Message {
std::string recipient;
std::string content;

public:
Message(std::string to, std::string text)
: recipient(std::move(to)), content(std::move(text)) {}

std::string getRecipient() const { return recipient; }


std::string getContent() const { return content; }
};

//✅ ISP: Interface with only the method that each channel
must implement
class INotificationChannel {
public:
virtual void send(const Message& msg) = 0;
virtual ~INotificationChannel() {}
};

//✅ SRP: Handles only email-related logic


//✅ LSP: Can be used via INotificationChannel*
class EmailChannel : public INotificationChannel {
public:
void send(const Message& msg) override {
std::cout << "[Email] To: " << msg.getRecipient()
<< " | Content: " << msg.getContent() << "\n";
}
};

//✅ SRP: Handles only SMS-related logic


class SMSChannel : public INotificationChannel {
public:
void send(const Message& msg) override {
std::cout << "[SMS] To: " << msg.getRecipient()
<< " | Content: " << msg.getContent() << "\n";
}
};

//✅ OCP: Easily add a new channel without changing existing


logic
class PushNotificationChannel : public INotificationChannel {
public:
void send(const Message& msg) override {
std::cout << "[Push] To: " << msg.getRecipient()
<< " | Content: " << msg.getContent() << "\n";
}
};

//✅ DIP: High-level module depends on abstraction, not


concrete channels
//✅ SRP: Only coordinates sending notifications
class NotificationService {
std::vector<std::shared_ptr<INotificationChannel>> channels;

public:
// Add any type of notification channel that implements the
interface
void addChannel(std::shared_ptr<INotificationChannel>
channel) {
channels.push_back(channel);
}

// Notify all configured channels


void notify(const Message& msg) {
for (const auto& channel : channels) {
channel->send(msg); // Polymorphic call — LSP + OCP
}
}
};
// ✅ Main: Usage example that ties it all together
int main() {
// Create a message
Message msg("john@example.com", "Your report is ready for
download.");

// Setup notification service with multiple channels


NotificationService service;
service.addChannel(std::make_shared<EmailChannel>());
service.addChannel(std::make_shared<SMSChannel>());
service.addChannel(std::make_shared<PushNotificationChannel>(
)); // OCP in action

// Notify all channels


service.notify(msg);

return 0;
}

🧠 Introduction to Design Patterns

In software engineering, as systems grow in complexity, writing e cient, reusable,


and maintainable code becomes crucial. Design patterns are time-tested solutions
to common design problems, distilled from the experience of expert developers.
They help organize code, improve exibility, and promote best practices.

📘 What Are Design Patterns?

• De nition: Reusable, general solutions to recurring software design


problems.
• Purpose: Provide a structured approach to solving design challenges.
• Nature: Not code, but templates or blueprints for solving problems.
• Language-Agnostic: Applicable across programming languages and
platforms.
• Bene ts:
• Promote code reusability and scalability
• Improve communication among developers
• Encourage modular and maintainable design
⚠ Note: Misusing or overusing patterns can lead to unnecessary complexity.

🍳 Real-Life Analogy
fi
fi
fl
ffi
Think of a restaurant kitchen:

• Recipes = Design Patterns


• Ingredients = Data
• Cooking Techniques = Functions
• Final Dish = User Interface
Just as chefs follow recipes to prepare consistent meals, developers use design
patterns to build robust software.

🧩 Types of Design Patterns

1. Creational Patterns
Focus on object creation, ensuring exibility and control.

• Example: Singleton Pattern


Ensures only one instance of a class exists (e.g., a global logger).
2. Structural Patterns
Deal with class and object composition.

• Example: Adapter Pattern


Like using a plug adapter to connect incompatible interfaces.

3. Behavioral Patterns
De ne how objects interact and communicate.

• Example: Observer Pattern


In a stock app, users (observers) get updates when stock prices (subject)
change.
4. Architectural Patterns
De ne the high-level structure of software systems.

• Example: Microservices Architecture


Breaks an app into independent services (e.g., payments, inventory).

✅ Advantages of Design Patterns

• Proven Solutions: Based on expert experience


• Reusable Templates: Save time and e ort
• Elegant Code: Promotes clarity and modularity
• Scalable Architecture: Provides a solid foundation for growth

⚠ Risks of Not Knowing Design Patterns

• Unnecessary Complexity: Poor pattern use can clutter code


• Misuse: Without understanding, patterns may be wrongly applied
• Missed Optimization: Opportunities for reuse and scalability may be lost
• Steep Learning Curve: Requires time and practice to master
fi
fi
fl
ff
🏁 Conclusion

Design patterns are essential tools for building clean, scalable, and maintainable
software. They encapsulate best practices and o er a shared vocabulary for
developers. While mastering them takes e ort, the payo is signi cant in terms of
code quality and development e ciency.

🧩 Singleton Design Pattern (C++)

🔍 De nition
The Singleton Pattern ensures that a class has only one instance and provides a
global point of access to it. It is commonly used for managing shared resources
like:

• Logging
• Con guration settings
• Database connections

❓ Why Use Singleton?


Without Singleton:

• Memory Wastage: Multiple instances consume more memory.


• Inconsistency: Di erent instances may hold di erent states.
• Redundant Initialization: Repeated setup of the same resource.

🚫 Problem Without Singleton (Incorrect Example)

#include <iostream>
using namespace std;

class Logger {
public:
Logger() {
cout << "Logger created\n";
}

void log(const string& message) {


cout << "Log: " << message << endl;
}
};

int main() {
Logger logger1;
logger1.log("First message");
fi
fi
ff
ffi
ff
ff
ff
ff
fi
Logger logger2;
logger2.log("Second message");

if (&logger1 == &logger2)
cout << "Same instance\n";
else
cout << "Different instances\n";
}

🧨 Output: Di erent instances are created.

✅ Correct Singleton Implementation (Lazy Initialization)

#include <iostream>
#include <mutex>
using namespace std;

class Logger {
private:
static Logger* instance;
static mutex mtx;

// Private constructor
Logger() {
cout << "Logger initialized\n";
}

public:
// Delete copy constructor and assignment operator
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;

static Logger* getInstance() {


lock_guard<mutex> lock(mtx); // Thread-safe
if (instance == nullptr) {
instance = new Logger();
ff
}
return instance;
}

void log(const string& message) {


cout << "Log: " << message << endl;
}
};

// Initialize static members


Logger* Logger::instance = nullptr;
mutex Logger::mtx;

int main() {
Logger* logger1 = Logger::getInstance();
logger1->log("First message");

Logger* logger2 = Logger::getInstance();


logger2->log("Second message");

if (logger1 == logger2)
cout << "Same instance\n";
else
cout << "Different instances\n";
}

🧵 Thread-Safe Singleton with Double-Checked Locking

static Logger* getInstance() {


if (instance == nullptr) {
lock_guard<mutex> lock(mtx);
if (instance == nullptr) {
instance = new Logger();
}
}
return instance;
}

🛠 Types of Singleton Initialization

Type Description

Lazy Initialization Instance created only when needed. Saves memory.


Eager Initialization Instance created at program start. Simple but may
waste memory.

✅ Advantages
• Memory E cient: Only one instance.
• Global Access: Easy to access from anywhere.
• Centralized Control: Ideal for shared resources.

⚠ Disadvantages
• Di cult to Test: Global state can interfere with unit tests.
• Limited Extensibility: Hard to subclass or replace.
• Lifecycle Management: Hard to destroy or reset the instance.

🌍 Real-World Use Cases


• Logger: Centralized logging system.
• Con guration Manager: Shared app settings.
• Database Connection Pool: Single access point to DB.

🏁 Conclusion
The Singleton Pattern is a powerful tool for managing shared resources and
ensuring consistent behavior across an application. However, it should be used
judiciously, especially in multithreaded environments and testable codebases.

🧬 Prototype Design Pattern

🔍 What Is It?
The Prototype Pattern is a creational design pattern that allows you to create new
objects by cloning an existing object (the prototype), rather than instantiating new
ones from scratch.

🎯 Why Use It?

• Avoid Redundancy: Repeated object creation with the same con guration
violates the DRY(Don’t Repeat Yourself) principle.
• Improve Performance: Cloning is faster than reinitializing complex objects.
• Enhance Flexibility: Easily tweak cloned objects.
• Minimize Errors: Reduces manual setup and inconsistencies.

🧠 Real-Life Analogy

Imagine a photocopy machine:


ffi
fi
ffi
fi
• You scan a document once (the prototype).
• You make multiple copies (clones) without redoing the setup.

❌ Without Prototype Pattern (Problematic Design)

class Car {
string model, color, engine;
public:
Car(string m, string c, string e) : model(m), color(c),
engine(e) {}
void display() { cout << model << " | " << color << " | "
<< engine << endl; }
};

int main() {
Car car1("Model S", "Red", "Electric");
Car car2("Model S", "Red", "Electric");
Car car3("Model S", "Red", "Electric");

car1.display();
car2.display();
car3.display();
}

🚫 SOLID Violations

Principle Violation

SRP Car handles both data and instantiation logic.

OCP Adding new car types requires modifying


constructor logic.

DRY Repeated constructor calls with the same data.

DIP Client is tightly coupled to the Car class.

✅ With Prototype Pattern (Improved Design)

#include <iostream>
#include <memory>
using namespace std;

// Prototype Interface
class CarPrototype {
public:
virtual unique_ptr<CarPrototype> clone() const = 0;
virtual void display() const = 0;
virtual ~CarPrototype() = default;
};

// Concrete Class
class Car : public CarPrototype {
string model, color, engine;
public:
Car(string m, string c, string e) : model(m), color(c),
engine(e) {}

unique_ptr<CarPrototype> clone() const override {


return make_unique<Car>(*this); // Deep copy
}

void display() const override {


cout << model << " | " << color << " | " << engine <<
endl;
}
};

// Client
int main() {
unique_ptr<CarPrototype> prototype =
make_unique<Car>("Model S", "Red", "Electric");

auto car1 = prototype->clone();


auto car2 = prototype->clone();
auto car3 = prototype->clone();

car1->display();
car2->display();
car3->display();
}

🧩 SOLID Principles in Action

Principle Improvement

SRP Car handles only data; cloning logic is abstracted.

OCP New car types can be added without modifying


existing code.
DIP Client depends on CarPrototype, not concrete Car.

LSP Any subclass of CarPrototype can be used interchangeably.

🌍 Real-World Use Cases

1. Video Game Characters


• Clone a base enemy character and tweak health or speed.
2. Document Templates
• Clone a resume or invoice template and ll in user-speci c data.
3. UI Component Libraries
• Clone a base button or card and apply di erent styles.
4. 3D Modeling
• Clone a tree model to populate a forest scene.

✅ Bene ts

• E cient Object Creation


• Flexible Customization
• Less Error-Prone
• Decouples Instantiation Logic

⚠ Limitations

• Not Ideal for Immutable Objects


• Deep Copy Complexity
• Overhead in Simple Projects

📌 When to Use

• When object creation is resource-intensive.


• When you need many similar objects.
• When object con guration is complex or repetitive.

🏁 Conclusion

The Prototype Pattern is a powerful tool for e cient and exible object creation,
especially when aligned with SOLID principles. It reduces redundancy, improves
maintainability, and supports scalable design. Use it when you want to clone
objects safely and cleanly without tying your code to speci c classes.
ffi
fi
fi
fi
ff
ffi
fi
fl
fi
🎨 Decorator Design Pattern (C++ Style)

🧩 What Is It?
The Decorator Pattern is a structural design pattern that allows you to dynamically
add behavior to an object without modifying its structure. It wraps the original
object in a series of decorator classes that each add new functionality.

🍕 Real-World Analogy: Custom Pizza

At a pizza shop:

• You start with a basic pizza.


• You add toppings like cheese, olives, mushrooms.
• Each topping wraps the pizza and adds its own cost and description.
This is exactly how the Decorator Pattern works in code.

❌ Without Decorator Pattern (Class Explosion)

class Pizza {
public:
virtual string getDescription() { return "Basic Pizza"; }
virtual double getCost() { return 5.00; }
};

class CheesePizza : public Pizza {


public:
string getDescription() override { return "Cheese Pizza";
}
double getCost() override { return 7.00; }
};

class CheesePizzaWithOlives : public CheesePizza {


public:
string getDescription() override { return "Cheese Pizza
with Olives"; }
double getCost() override { return 8.00; }
};
🚫 Problems
• SRP Violation: Each class handles both base and added behavior.
• OCP Violation: Adding new toppings requires modifying or creating new
classes.
• Scalability Issue: Every new combination = new class.

✅ With Decorator Pattern (Flexible & Extensible)

// Component Interface
class Pizza {
public:
virtual string getDescription() = 0;
virtual double getCost() = 0;
virtual ~Pizza() = default;
};

// Concrete Component
class BasicPizza : public Pizza {
public:
string getDescription() override { return "Basic
Pizza"; }
double getCost() override { return 5.00; }
};

// Abstract Decorator
class PizzaDecorator : public Pizza {
protected:
Pizza* pizza;
public:
PizzaDecorator(Pizza* p) : pizza(p) {}
string getDescription() override { return pizza-
>getDescription(); }
double getCost() override { return pizza->getCost(); }
};

// Concrete Decorators
class Cheese : public PizzaDecorator {
public:
Cheese(Pizza* p) : PizzaDecorator(p) {}
string getDescription() override { return pizza-
>getDescription() + ", Cheese"; }
double getCost() override { return pizza->getCost() +
2.00; }
};

class Olives : public PizzaDecorator {


public:
Olives(Pizza* p) : PizzaDecorator(p) {}
string getDescription() override { return pizza-
>getDescription() + ", Olives"; }
double getCost() override { return pizza->getCost() +
1.00; }
};

class Mushrooms : public PizzaDecorator {


public:
Mushrooms(Pizza* p) : PizzaDecorator(p) {}
string getDescription() override { return pizza-
>getDescription() + ", Mushrooms"; }
double getCost() override { return pizza->getCost() +
1.50; }
};

// Client
int main() {
Pizza* pizza = new BasicPizza();
pizza = new Cheese(pizza);
pizza = new Olives(pizza);
pizza = new Mushrooms(pizza);

cout << pizza->getDescription() << " | Cost: $" << pizza-


>getCost() << endl;
delete pizza; // Clean up
}

🧩 SOLID Principles in Action

Principle How Decorator Pattern Helps

SRP Each class has a single responsibility (e.g., Cheese


only adds cheese).

OCP New toppings can be added without modifying existing


code.

DIP Client depends on the Pizza interface, not concrete classes.

LSP Decorators can be used wherever a Pizza is expected.


✅ Advantages

• Separation of Concerns: Each decorator handles one feature.


• Dynamic Behavior: Add/remove features at runtime.
• Flexible Combinations: Any order or mix of decorators is possible.
• Avoids Inheritance Overload: No need for dozens of subclasses.

⚠ Disadvantages

• Increased Complexity: Many small classes can be hard to manage.


• Debugging Di culty: Behavior is spread across multiple layers.
• Performance Overhead: Each decorator adds a layer of indirection.

📌 When to Use

• When you need to add behavior dynamically.


• When inheritance leads to class explosion.
• When you want to compose behavior rather than inherit it.

🏁 Conclusion

The Decorator Pattern is a powerful tool for extending object behavior without
modifying the original class. It promotes clean, modular, and scalable design,
especially in systems where exibility and customization are key—like pizza
ordering apps, GUI frameworks, or text formatting tools.

🧱 Facade Design Pattern (C++ Style)

🔍 What Is It?
The Facade Pattern is a structural design pattern that provides a simpli ed
interface to a complex subsystem. It hides the complexities of the system and
provides a uni ed interface to the client.

🎬 Real-World Analogy: Home Theater System

To watch a movie, you’d need to:

• Turn on the ampli er and set volume


• Turn on the DVD player and play the movie
• Turn on the projector and set it to widescreen
Instead, a remote control (facade) does all of this with one button: "Watch Movie".

❌ Without Facade Pattern

int main() {
ffi
fi
fi
fl
fi
Amplifier amp;
DVDPlayer dvd;
Projector projector;

amp.on();
amp.setVolume(5);
dvd.on();
dvd.play("Inception");
projector.on();
projector.wideScreenMode();
}

🚫 Problems
• Tight Coupling: Client knows too much about subsystem internals.
• Low Maintainability: Any change in subsystem logic a ects the client.
• Poor Readability: Complex logic scattered in client code.

✅ With Facade Pattern

class HomeTheaterFacade {
Amplifier* amp;
DVDPlayer* dvd;
Projector* projector;

public:
HomeTheaterFacade(Amplifier* a, DVDPlayer* d, Projector*
p)
: amp(a), dvd(d), projector(p) {}

void watchMovie(const string& movie) {


cout << "Get ready to watch a movie...\n";
amp->on();
amp->setVolume(5);
dvd->on();
dvd->play(movie);
projector->on();
projector->wideScreenMode();
}
};
ff
int main() {
Amplifier amp;
DVDPlayer dvd;
Projector projector;

HomeTheaterFacade homeTheater(&amp, &dvd, &projector);


homeTheater.watchMovie("Inception");
}

🧩 SOLID Principles in Action

Principle How Facade Pattern Helps

SRP Facade encapsulates coordination logic in one place.

OCP Subsystems can evolve independently of the client.

DIP Client depends on the facade abstraction, not


subsystem details.

✅ Advantages

• Simpli es Complex Systems: One interface instead of many.


• Loosely Coupled Code: Client is isolated from subsystem changes.
• Improves Readability: Cleaner and more maintainable code.
• Encapsulates Logic: Centralizes orchestration logic.

⚠ Disadvantages

• Reduced Flexibility: Client can’t access subsystem features directly.


• Extra Layer: Adds a layer that may be unnecessary for simple systems.
• Overhead: Slight performance cost due to indirection.

📌 When to Use

• When working with complex subsystems.


• When you want to decouple client code from subsystem logic.
• When you need to centralize control and improve maintainability.
• When you want to restrict access to sensitive subsystem operations.

🏁 Conclusion

The Facade Pattern is ideal for simplifying complex systems and decoupling
clients from subsystem internals. It improves maintainability, readability, and
fi
modularity, especially in large-scale applications like media players, compilers, or
enterprise systems.

🧱 Proxy Design Pattern (C++ Style)

🔍 What Is It?
The Proxy Pattern is a structural design pattern that provides a surrogate or
placeholder for another object to control access to it. It allows you to add
functionality like lazy loading, access control, or logging without modifying the
original object.

🖼 Real-World Analogy: Lazy Image Loading

In a photo album app:

• Loading all high-res images at once is slow and memory-intensive.


• Instead, show placeholders (proxies) and load the real image only when
needed.

❌ Without Proxy Pattern

class RealImage {
string filename;
public:
RealImage(string fname) : filename(fname) {
loadImageFromDisk();
}

void loadImageFromDisk() {
cout << "Loading " << filename << endl;
}

void display() {
cout << "Displaying " << filename << endl;
}
};

int main() {
RealImage image1("image1.jpg");
image1.display();

RealImage image2("image2.jpg");
image2.display();
}

🚫 Problems
• Eager Loading: Images are loaded even if not displayed.
• Resource Waste: Unnecessary memory and CPU usage.
• Tight Coupling: Client is tightly bound to the real object.

✅ With Proxy Pattern (Lazy Initialization)

class Image {
public:
virtual void display() = 0;
virtual ~Image() = default;
};

class RealImage : public Image {


string filename;
void loadImageFromDisk() {
cout << "Loading " << filename << endl;
}
public:
RealImage(string fname) : filename(fname) {
loadImageFromDisk();
}

void display() override {


cout << "Displaying " << filename << endl;
}
};

class ProxyImage : public Image {


RealImage* realImage = nullptr;
string filename;
public:
ProxyImage(string fname) : filename(fname) {}

void display() override {


if (!realImage)
realImage = new RealImage(filename);
realImage->display();
}
~ProxyImage() {
delete realImage;
}
};
int main() {
Image* image1 = new ProxyImage("image1.jpg");
image1->display(); // Loads and displays

Image* image2 = new ProxyImage("image2.jpg");


image2->display(); // Loads and displays

delete image1;
delete image2;
}

🧩 SOLID Principles in Action

Principle How Proxy Pattern Helps

SRP Proxy handles access control; RealImage handles image


logic.

OCP You can add new proxy types (e.g., logging, caching)
without modifying RealImage.

DIP Client depends on the Image interface, not concrete classes.

✅ Advantages

• Lazy Initialization: Load resources only when needed.


• Access Control: Restrict or monitor access to sensitive objects.
• Encapsulation: Hide complex logic from the client.
• Logging & Caching: Easily add cross-cutting concerns.

⚠ Disadvantages

• Increased Complexity: Adds an extra layer of abstraction.


• Performance Overhead: Slight delay due to indirection.
• Maintenance: More classes to manage.

📌 When to Use

• When dealing with resource-intensive objects.


• When you need access control, logging, or caching.
• When working with legacy code that cannot be modi ed directly.
fi
🏁 Conclusion

The Proxy Pattern is a powerful tool for controlling access, improving performance,
and encapsulating complexity. It’s especially useful in systems where resource
management, security, or lazy loading is critical. While it introduces some
complexity, the bene ts in scalability and maintainability often outweigh the trade-
o s.

🔔 Observer Design Pattern (C++ Style)

🔍 What Is It?
The Observer Pattern is a behavioral design pattern that de nes a one-to-many
dependency between objects. When the subject changes state, all its observers
are noti ed and updated automatically.

🌦 Real-World Analogy: Weather Updates

A weather station (subject) broadcasts updates to:

• Mobile apps
• Websites
• Email alerts
Each subscriber (observer) receives updates automatically when the weather
changes—no need to keep checking manually.

🧩 Key Components

Role Description

Subject Maintains a list of observers and notifies them of


changes.

Observer Defines an interface for receiving updates.

ConcreteSubject Stores state and notifies observers when it changes.

ConcreteObserver Implements the update logic based on subject


state.

✅ C++ Implementation: Weather Monitoring System

1. Observer Interface
ff
fi
fi
fi
class Observer {
public:
virtual void update(float temp, float humidity, float
pressure) = 0;
virtual ~Observer() = default;
};

2. Subject Interface

class Subject {
public:
virtual void registerObserver(Observer* o) = 0;
virtual void removeObserver(Observer* o) = 0;
virtual void notifyObservers() = 0;
virtual ~Subject() = default;
};

3. Concrete Subject (WeatherData)

#include <vector>
using namespace std;

class WeatherData : public Subject {


vector<Observer*> observers;
float temperature, humidity, pressure;

public:
void registerObserver(Observer* o) override {
observers.push_back(o);
}

void removeObserver(Observer* o) override {


observers.erase(remove(observers.begin(),
observers.end(), o), observers.end());
}

void notifyObservers() override {


for (auto* o : observers)
o->update(temperature, humidity, pressure);
}

void setMeasurements(float t, float h, float p) {


temperature = t;
humidity = h;
pressure = p;
notifyObservers();
}
};

4. Concrete Observers

class CurrentConditionsDisplay : public Observer {


public:
void update(float temp, float humidity, float pressure)
override {
cout << "Current Conditions: " << temp << "°C, " <<
humidity << "% humidity\n";
}
};

class StatisticsDisplay : public Observer {


public:
void update(float temp, float humidity, float pressure)
override {
cout << "Statistics: Temp = " << temp << ", Pressure
= " << pressure << endl;
}
};

5. Client Code

int main() {
WeatherData weatherData;

CurrentConditionsDisplay currentDisplay;
StatisticsDisplay statsDisplay;

weatherData.registerObserver(&currentDisplay);
weatherData.registerObserver(&statsDisplay);

weatherData.setMeasurements(25.5, 65, 1013);


weatherData.setMeasurements(26.0, 70, 1012);
}
🧩 SOLID Principles in Action

Principle How Observer Pattern Helps

SRP Observers and subject have distinct responsibilities.

OCP New observers can be added without modifying the


subject.

DIP Subject and observers depend on abstractions, not


concrete classes.

✅ Advantages

• Real-Time Updates: Observers are noti ed immediately.


• Loose Coupling: Subject and observers are independent.
• Dynamic Subscription: Observers can join or leave at runtime.
• Scalable: Easily extendable with new observers.

⚠ Disadvantages

• Noti cation Overhead: Too many observers can impact performance.


• Uncontrolled Order: Noti cation order is not guaranteed.
• Complex Debugging: Tracing updates across many observers can be tricky.

📌 When to Use

• When multiple objects need to react to changes in another object.


• When you want to decouple the subject from its observers.
• In event-driven systems, UI frameworks, data binding, etc.

🏁 Conclusion

The Observer Pattern is ideal for publish-subscribe systems where multiple


components need to stay in sync with a central source. It promotes exibility,
extensibility, and clean separation of concerns, making it a go-to pattern for real-
time and reactive applications.

🧭 Template Design Pattern

🔍 What Is It?
fi
fi
fi
fl
The Template Pattern is a behavioral design pattern that de nes the skeleton of an
algorithm in a base class and lets subclasses override speci c steps without
changing the algorithm’s structure.

🍵 Real-World Analogy: Beverage Preparation

Making tea and co ee involves:

• Boiling water
• Brewing (tea bag or co ee grounds)
• Pouring into a cup
• Adding condiments
The work ow is the same, but some steps di er. The Template Pattern captures
this shared structure while allowing customization.

🧩 Key Characteristics

Feature Description

Skeleton in Superclass Base class defines the algorithm structure.

Custom Steps in Subclasses override specific steps.


Subclasses

Reusability Common logic is reused across


implementations.

❌ Without Template Pattern (Problematic Design)

class Beverage {
public:
void prepareRecipe() {
boilWater();
if (type == "Tea") steepTeaBag();
else if (type == "Coffee") brewCoffeeGrinds();
pourInCup();
addCondiments();
}

private:
void boilWater() { cout << "Boiling water\n"; }
void steepTeaBag() { cout << "Steeping tea\n"; }
void brewCoffeeGrinds() { cout << "Brewing coffee\n"; }
void pourInCup() { cout << "Pouring into cup\n"; }
void addCondiments() {
fl
ff
ff
ff
fi
fi
if (type == "Tea") cout << "Adding Lemon\n";
else if (type == "Coffee") cout << "Adding Sugar and
Milk\n";
}

string type;
};

🚫 Problems
• OCP Violation: Adding new beverages requires modifying the base class.
• SRP Violation: One class handles all beverage types.
• Poor Extensibility: Hard to scale or maintain.

✅ With Template Pattern (Improved Design)

class Beverage {
public:
void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}

protected:
void boilWater() { cout << "Boiling water\n"; }
void pourInCup() { cout << "Pouring into cup\n"; }

virtual void brew() = 0;


virtual void addCondiments() = 0;
};

class Tea : public Beverage {


void brew() override { cout << "Steeping the tea\n"; }
void addCondiments() override { cout << "Adding Lemon\n";
}
};

class Coffee : public Beverage {


void brew() override { cout << "Brewing the coffee
grinds\n"; }
void addCondiments() override { cout << "Adding Sugar and
Milk\n"; }
};

int main() {
Beverage* tea = new Tea();
tea->prepareRecipe();

Beverage* coffee = new Coffee();


coffee->prepareRecipe();

delete tea;
delete coffee;
}

🧩 SOLID Principles in Action

Principle How Template Pattern Helps

SRP Each class has a single responsibility.

OCP New behaviors can be added via subclasses without


modifying the base class.

LSP Subclasses can be used in place of the base class


without breaking functionality.

✅ Advantages

• Code Reusability: Shared logic is centralized.


• Open/Closed Principle: Add new behaviors without changing existing code.
• Improved Readability: Clear separation of common and custom steps.
• Consistency: Ensures a consistent algorithm structure across
implementations.

⚠ Trade-O s

• Complexity in Large Systems: Too many steps or subclasses can be hard to


manage.
• Fixed Work ow: Not ideal for highly dynamic or conditional work ows.
• LSP Risk: Overriding steps incorrectly may break the algorithm.

📌 When to Use

• When multiple classes share a common algorithm structure.


• When you want to enforce a consistent work ow.
• When only some steps vary between implementations.

🧰 Use Cases

• File processing systems (e.g., CSV, XML, JSON parsers)


• Report generation tools
fl
ff
fl
fl
• Game level design (shared mechanics, di erent rules)
• User registration ows (email, social login, etc.)

🏁 Conclusion

The Template Pattern is a powerful tool for standardizing work ows while allowing
customization. It promotes clean architecture, code reuse, and maintainability,
especially in systems with repetitive but slightly varying logic.

🔌 Adapter Design Pattern

🔍 What Is It?
The Adapter Pattern is a structural design pattern that allows objects with
incompatible interfaces to work together. It acts as a bridge between the client and
an existing class (adaptee) by converting the interface of one class into another
expected by the client.

🖨 Real-World Analogy: Legacy Printer Integration

Imagine:

• A modern system expects a printDocument() method.


• A legacy printer only supports print().
Instead of rewriting the legacy printer, you create an adapter that translates
printDocument() into print().

🧩 Key Components

Component Role

Client Uses the modern interface.

Adaptee The legacy class with an incompatible interface.

Adapter Implements the modern interface and delegates to


the adaptee.

❌ Without Adapter Pattern

class LegacyPrinter {
public:
fl
ff
fl
void print() {
cout << "Printing using legacy printer...\n";
}
};

int main() {
LegacyPrinter printer;
printer.print(); // Client must know legacy interface
}

🚫 Problems
• Tight Coupling: Client depends on legacy implementation.
• OCP Violation: Adding new printers requires modifying client code.
• Poor Maintainability: Hard to scale or integrate new systems.

✅ With Adapter Pattern

// Target Interface
class ModernPrinter {
public:
virtual void printDocument() = 0;
virtual ~ModernPrinter() = default;
};

// Adaptee
class LegacyPrinter {
public:
void print() {
cout << "Printing using legacy printer...\n";
}
};

// Adapter
class PrinterAdapter : public ModernPrinter {
LegacyPrinter* legacyPrinter;
public:
PrinterAdapter(LegacyPrinter* lp) : legacyPrinter(lp) {}

void printDocument() override {


legacyPrinter->print(); // Translate call
}
};
// Client
int main() {
LegacyPrinter* oldPrinter = new LegacyPrinter();
ModernPrinter* printer = new PrinterAdapter(oldPrinter);

printer->printDocument(); // Client uses modern


interface
delete oldPrinter;
delete printer;
}

🧩 SOLID Principles in Action

Principle How Adapter Pattern Helps

SRP Adapter handles translation logic; client and adaptee


stay focused.

OCP New adapters can be added without modifying existing


code.

DIP Client depends on the ModernPrinter abstraction, not the legacy clas

✅ Advantages

• Flexibility: Integrate legacy systems without modifying them.


• Loose Coupling: Client is decoupled from the adaptee.
• Reusability: Adapters can be reused across di erent clients.
• Maintainability: Changes are isolated to the adapter.

⚠ Limitations

• Added Complexity: Introduces an extra layer.


• Performance Overhead: Slight delay due to indirection.
• Not Ideal for Major Refactors: Better to redesign if the legacy system is too
outdated.

📌 When to Use

• When integrating legacy systems with modern interfaces.


• When you want to reuse existing code without modi cation.
• When you need to bridge incompatible interfaces.

🧰 Use Cases
ff
fi
• Connecting old APIs to new systems.
• Adapting third-party libraries to your application.
• GUI frameworks adapting widgets to di erent platforms.

🏁 Conclusion

The Adapter Pattern is a powerful tool for bridging gaps between incompatible
systems. It promotes exibility, reusability, and clean architecture, especially when
working with legacy code or third-party libraries. While it introduces a bit of
complexity, its bene ts in integration and maintainability make it a valuable design
pattern in real-world software development.

🌳 Composite Design Pattern

🔍 What Is It?
The Composite Pattern is a structural design pattern that allows you to compose
objects into tree structures to represent part-whole hierarchies. It lets clients treat
individual objects and compositions uniformly.

🗂 Real-World Analogy: File System

In a le system:

• Files are individual items.


• Folders can contain les or other folders.
You can perform operations like showDetails() on both les and folders without
knowing their type—this is the essence of the Composite Pattern.

🧩 Key Components

Component Role

Component Declares common operations (e.g., draw(), showDetails()).


Interface

Leaf Represents individual objects (e.g., File, Circle).

Composite Represents groups of components (e.g., Folder, Drawing).

❌ Without Composite Pattern


fi
fi
fi
fl
ff
fi
class Circle {
public:
void draw(string color) {
cout << "Drawing Circle with color " << color <<
endl;
}
};

class Rectangle {
public:
void draw(string color) {
cout << "Drawing Rectangle with color " << color <<
endl;
}
};

class Drawing {
vector<Circle> circles;
vector<Rectangle> rectangles;
public:
void draw(string color) {
for (auto& c : circles) c.draw(color);
for (auto& r : rectangles) r.draw(color);
}
};

🚫 Problems
• SRP Violation: Drawing manages both shapes and rendering logic.
• OCP Violation: Adding a new shape requires modifying Drawing.
• No Hierarchy: Cannot nest drawings inside other drawings.

✅ With Composite Pattern

class Shape {
public:
virtual void draw(string color) = 0;
virtual ~Shape() = default;
};

class Circle : public Shape {


public:
void draw(string color) override {
cout << "Drawing Circle with color " << color <<
endl;
}
};
class Rectangle : public Shape {
public:
void draw(string color) override {
cout << "Drawing Rectangle with color " << color <<
endl;
}
};

class Composite : public Shape {


vector<Shape*> shapes;
public:
void add(Shape* shape) { shapes.push_back(shape); }
void remove(Shape* shape) {
shapes.erase(remove(shapes.begin(), shapes.end(),
shape), shapes.end());
}

void draw(string color) override {


for (auto* shape : shapes)
shape->draw(color);
}
};

int main() {
Shape* circle1 = new Circle();
Shape* circle2 = new Circle();
Shape* rectangle = new Rectangle();

Composite* group1 = new Composite();


group1->add(circle1);
group1->add(rectangle);

Composite* group2 = new Composite();


group2->add(circle2);
group2->add(group1);

group2->draw("Blue");

delete circle1;
delete circle2;
delete rectangle;
delete group1;
delete group2;
}

🧩 SOLID Principles in Action


Principle How Composite Pattern Helps

SRP Each class has a single responsibility.

OCP New shapes can be added without modifying existing


code.

DIP Client depends on the Shape abstraction, not concrete classes.

✅ Bene ts

• Uniformity: Treat individual and composite objects the same.


• Scalability: Easily extend the structure with new components.
• Maintainability: Centralized logic and clean hierarchy.
• Recursive Composition: Supports deeply nested structures.

⚠ Limitations

• Complex Debugging: Recursive structures can be harder to trace.


• Overhead: May introduce unnecessary abstraction for simple use cases.
• Type Safety: Runtime errors if components are misused.

📌 When to Use

• When you need to represent hierarchical structures (e.g., le systems, UI


components).
• When you want to treat individual and group objects uniformly.
• When you need recursive composition.

🧰 Use Cases

• File systems ( les and folders)


• GUI frameworks (buttons, panels, windows)
• Drawing applications (shapes and groups)
• Organization charts (employees and departments)

🏁 Conclusion

The Composite Pattern is ideal for managing tree-like structures where individual
and composite objects need to be treated uniformly. It promotes clean
architecture, code reuse, and scalability, making it a go-to pattern for hierarchical
data modeling.

🔁 Iterator Design Pattern


fi
fi
fi
🔍 What Is It?
The Iterator Pattern is a behavioral design pattern that provides a standardized
way to access elements of a collection sequentially without exposing its internal
structure.

📚 Real-World Analogy: Library Book Browsing

Imagine a library:

• You don’t need to know how books are stored (shelves, boxes, etc.).
• You just use a catalog (iterator) to browse through them one by one.

🧩 Key Components

Component Role

Iterator Interface Defines hasNext() and next() methods.

Concrete Iterator Implements the iterator for a specific collection.

Container Interface Declares getIterator() method.

Concrete Container Implements the container and returns an iterator.

❌ Without Iterator Pattern

class Book {
string title;
public:
Book(string t) : title(t) {}
string getTitle() { return title; }
};

class Library {
vector<Book> books;
public:
void addBook(Book book) { books.push_back(book); }
vector<Book>& getBooks() { return books; }
};
int main() {
Library lib;
lib.addBook(Book("Book 1"));
lib.addBook(Book("Book 2"));

for (int i = 0; i < lib.getBooks().size(); ++i)


cout << lib.getBooks()[i].getTitle() << endl;
}

🚫 Problems
• Encapsulation Violation: Internal structure is exposed.
• Tight Coupling: Client depends on the container’s implementation.
• Code Duplication: Every client must write iteration logic.

✅ With Iterator Pattern

class Iterator {
public:
virtual bool hasNext() = 0;
virtual Book next() = 0;
virtual ~Iterator() = default;
};

class Container {
public:
virtual Iterator* getIterator() = 0;
virtual ~Container() = default;
};

class Library : public Container {


vector<Book> books;

class BookIterator : public Iterator {


vector<Book>& books;
size_t index = 0;
public:
BookIterator(vector<Book>& b) : books(b) {}
bool hasNext() override { return index <
books.size(); }
Book next() override { return books[index++]; }
};

public:
void addBook(Book book) { books.push_back(book); }
Iterator* getIterator() override {
return new BookIterator(books);
}
};

int main() {
Library lib;
lib.addBook(Book("Book 1"));
lib.addBook(Book("Book 2"));

Iterator* it = lib.getIterator();
while (it->hasNext()) {
cout << it->next().getTitle() << endl;
}
delete it;
}

🧩 SOLID Principles in Action

Principle How Iterator Pattern Helps

SRP Separates iteration logic from collection logic.

OCP New iteration strategies can be added without


modifying the collection.

DIP Client depends on the Iterator abstraction, not the concrete collectio

✅ Advantages

• Encapsulation: Hides internal structure of the collection.


• Reusability: Centralized iteration logic.
• Flexibility: Works with di erent types of collections.
• Consistency: Standard interface for traversal.

⚠ Disadvantages

• Overhead: Adds complexity for simple collections.


• Limited Functionality: Basic iterators don’t support reverse or ltered
traversal.

📌 When to Use

• When you want to abstract iteration logic.


• When the internal structure of the collection should be hidden.
• When multiple clients need to traverse the same collection.
ff
fi
🧰 Use Cases

• Collections in libraries (e.g., STL in C++)


• Tree or graph traversal
• UI components (e.g., tabbing through elements)
• File system navigation

🏁 Conclusion

The Iterator Pattern is a powerful tool for standardized and secure traversal of
collections. It promotes clean architecture, encapsulation, and code reuse, making
it ideal for scalable and maintainable systems.

🧠 Strategy Design Pattern

🔍 What Is It?
The Strategy Pattern is a behavioral design pattern that enables selecting an
algorithm or behavior at runtime. It de nes a family of algorithms, encapsulates
each one, and makes them interchangeable without altering the context class.

📝 Real-World Analogy: Document Saving Formats


Imagine a document editor that supports:
• Saving as Plain Text
• Saving as HTML
• Possibly more formats in the future
Instead of using if-else or switch statements, the Strategy Pattern allows you
to plug in the desired saving behavior dynamically.

❌ Without Strategy Pattern

class Document {
string text;
public:
Document(string t) : text(t) {}

void save(string format) {


if (format == "plain")
cout << "Saving as plain text: " << text << endl;
else if (format == "html")
cout << "Saving as HTML: <html><body>" << text <<
"</body></html>" << endl;
else
fi
cout << "Unknown format: " << format << endl;
}
};

🚫 Problems
• SRP Violation: Document handles both content and saving logic.
• OCP Violation: Adding new formats requires modifying the class.
• Scalability Issues: More formats = more conditions.

✅ With Strategy Pattern


1. Strategy Interface

class SaveStrategy {
public:
virtual void save(const string& text) = 0;
virtual ~SaveStrategy() = default;
};

2. Concrete Strategies

class PlainTextStrategy : public SaveStrategy {


public:
void save(const string& text) override {
cout << "Saving as plain text: " << text << endl;
}
};

class HTMLStrategy : public SaveStrategy {


public:
void save(const string& text) override {
cout << "Saving as HTML: <html><body>" << text << "</
body></html>" << endl;
}
};

3. Context Class (Document)

class Document {
string text;
SaveStrategy* strategy = nullptr;
public:
Document(string t) : text(t) {}
void setSaveStrategy(SaveStrategy* s) {
strategy = s;
}

void save() {
if (strategy)
strategy->save(text);
else
cout << "No save strategy set.\n";
}
};

4. Client Code

int main() {
Document doc("Hello, world!");

PlainTextStrategy plain;
HTMLStrategy html;

doc.setSaveStrategy(&plain);
doc.save();

doc.setSaveStrategy(&html);
doc.save();
}

🧩 SOLID Principles in Action

Principle How Strategy Pattern Helps

SRP Each class has a single responsibility.

OCP New strategies can be added without modifying


existing code.

DIP Document depends on the SaveStrategy abstraction.

✅ Advantages
• Encapsulation of Algorithms: Each strategy is isolated.
• Runtime Flexibility: Swap behaviors dynamically.
• Improved Maintainability: No need to modify the context class.
• Reusability: Strategies can be reused across di erent contexts.

⚠ Disadvantages
• Class Overhead: Each strategy requires a new class.
• Context Awareness: The context must know which strategy to use.

📌 When to Use
• When you have multiple ways to perform an operation.
• When you want to eliminate conditional logic.
• When you need to add new behaviors without modifying existing code.

🧰 Use Cases
• Payment processing (credit card, PayPal, crypto)
• Sorting algorithms (bubble, quick, merge)
• Compression formats (ZIP, RAR, TAR)
• Game AI behaviors (aggressive, defensive, evasive)

🏁 Conclusion
The Strategy Pattern is a powerful tool for encapsulating interchangeable
behaviors. It promotes clean code, exibility, and scalability, especially in systems
where operations vary but share a common interface. It’s a go-to pattern for
eliminating complex conditionals and adhering to SOLID principles.

🔗 Chain of Responsibility Design Pattern

🔍 **What Is It?
The Chain of Responsibility Pattern** is a behavioral design pattern that allows a
request to be passed along a chain of handlers. Each handler decides whether to
process the request or pass it to the next handler in the chain.

📞 Real-World Analogy: Customer Support System


• Support Agent handles low-priority issues.
• Supervisor handles medium-priority issues.
• Manager handles high-priority issues.
Each handler checks if it can process the request. If not, it forwards it to the next
handler.

❌ Without Chain of Responsibility


fl
ff
class SupportHandler {
public:
void handleTicket(string priority) {
if (priority == "low")
cout << "Support Agent is handling the low-
priority ticket.\n";
else if (priority == "medium")
cout << "Supervisor is handling the medium-
priority ticket.\n";
else if (priority == "high")
cout << "Manager is handling the high-priority
ticket.\n";
else
cout << "Invalid priority level.\n";
}
};

🚫 Problems
• SRP Violation: One class handles all logic.
• OCP Violation: Adding a new handler requires modifying the method.
• Tight Coupling: Hard to extend or maintain.

✅ With Chain of Responsibility Pattern


1. Abstract Handler

class SupportHandler {
protected:
SupportHandler* nextHandler = nullptr;
public:
void setNextHandler(SupportHandler* handler) {
nextHandler = handler;
}

virtual void handleRequest(const string& priority) = 0;


virtual ~SupportHandler() = default;
};

2. Concrete Handlers

class SupportAgent : public SupportHandler {


public:
void handleRequest(const string& priority) override {
if (priority == "low")
cout << "Support Agent is handling the low-
priority ticket.\n";
else if (nextHandler)
nextHandler->handleRequest(priority);
}
};

class Supervisor : public SupportHandler {


public:
void handleRequest(const string& priority) override {
if (priority == "medium")
cout << "Supervisor is handling the medium-
priority ticket.\n";
else if (nextHandler)
nextHandler->handleRequest(priority);
}
};

class Manager : public SupportHandler {


public:
void handleRequest(const string& priority) override {
if (priority == "high")
cout << "Manager is handling the high-priority
ticket.\n";
else if (nextHandler)
nextHandler->handleRequest(priority);
else
cout << "No handler available for priority: " <<
priority << endl;
}
};

3. Client Code

int main() {
SupportAgent agent;
Supervisor supervisor;
Manager manager;

agent.setNextHandler(&supervisor);
supervisor.setNextHandler(&manager);

agent.handleRequest("low");
agent.handleRequest("medium");
agent.handleRequest("high");
agent.handleRequest("urgent"); // Unhandled case
}

🧩 SOLID Principles in Action

Principle How Chain of Responsibility Helps

SRP Each handler has a single responsibility.

OCP New handlers can be added without modifying existing


ones.

DIP Client depends on the abstract SupportHandler, not concrete classe

✅ Bene ts
• Loose Coupling: Sender and receiver are decoupled.
• Flexible Chain Con guration: Handlers can be reordered or replaced.
• Open for Extension: New handlers can be added easily.
• Improved Maintainability: Each handler is self-contained.

⚠ Drawbacks
• Debugging Complexity: Tracing the request path can be di cult.
• Unhandled Requests: If no handler processes the request, it may go
unhandled.
• Performance Overhead: Long chains may introduce latency.

📌 When to Use
• When multiple objects can handle a request.
• When the handler isn’t known in advance.
• When you want to decouple sender and receiver.

🧰 Use Cases
• Customer Support Systems
• Event Handling in GUIs
• Middleware in Web Frameworks
• Logging Systems

🏁 Conclusion
fi
fi
ffi
The Chain of Responsibility Pattern is a powerful way to delegate request handling
across a chain of objects. It promotes exibility, extensibility, and clean separation
of concerns, making it ideal for systems where multiple handlers may process a
request.

fl
✅ Tic Tac Toe – Low-Level Design (LLD) Simpli ed

🎯 Why This Matters for Interviews

Despite its simplicity, Tic Tac Toe is a perfect entry point to demonstrate:

• ✅ Class design & responsibilities

• ✅ System interactions

• ✅ Game state management

• ✅ Encapsulation of rules

• ✅ Scalability in design thinking (NxN grid, AI, multiplayer)

⚠ In interviews, you're not expected to code everything. You're expected to design the
system, de ne clear class roles, and explain how the components interact.

🧠 Step 1: Think Like a System Designer

Ask:

• Actors? → Players

• Entities? → Game, Board, Cell, Rules

• Responsibilities?

◦ Game → controls ow

◦ Board → stores state

◦ Rules → checks winner

◦ Player → provides move

• Interaction Flow?
fi
fl
fi
◦ Player → Game → Board → Cell

• Encapsulation?

◦ Win/draw logic goes in GameRules

• Scalability?

◦ Use dynamic grid size to support 4x4, 5x5

📦 Core Components (with Real-World Analogy)

Real-world
Component Responsibility
analogy
Player Human player Provides name and symbol
Holds state ('X', 'O', or
Cell Square on board
empty)
Board Game board Manages grid, updates cell
GameRule
Referee Validates winning moves
s
Controls flow, turns, win/
Game Match
draw

🧱 Design Overview

class Player {
std::string name;
char symbol; // 'X' or 'O'

public:
Player(const std::string& name, char symbol)
: name(name), symbol(symbol) {}

char getSymbol() const { return symbol; }


std::string getName() const { return name; }
};

class Cell {
int row, col;
char value; // 'X', 'O', or ' '

public:
Cell(int r, int c) : row(r), col(c), value(' ') {}
bool isEmpty() const { return value == ' '; }
void setValue(char val) { value = val; }
char getValue() const { return value; }
};

class Board {
int size;
std::vector<std::vector<Cell>> grid;

public:
Board(int size) : size(size), grid(size,
std::vector<Cell>(size, Cell(0,0))) {
for (int i = 0; i < size; ++i)
for (int j = 0; j < size; ++j)
grid[i][j] = Cell(i, j);
}

bool markCell(int row, int col, char symbol) {


if (grid[row][col].isEmpty()) {
grid[row][col].setValue(symbol);
return true;
}
return false;
}

bool isFull() const {


for (auto& row : grid)
for (auto& cell : row)
if (cell.isEmpty()) return false;
return true;
}

const std::vector<std::vector<Cell>>& getGrid() const


{ return grid; }

};

class GameRules {
public:
bool isWinningMove(const Board& board, char symbol) {
const auto& grid = board.getGrid();
int size = grid.size();

// Check rows, cols, diagonals


for (int i = 0; i < size; ++i) {
bool rowWin = true, colWin = true;
for (int j = 0; j < size; ++j) {
if (grid[i][j].getValue() != symbol) rowWin =
false;
if (grid[j][i].getValue() != symbol) colWin =
false;
}
if (rowWin || colWin) return true;
}

// Diagonals
bool mainDiag = true, antiDiag = true;
for (int i = 0; i < size; ++i) {
if (grid[i][i].getValue() != symbol) mainDiag =
false;
if (grid[i][size - 1 - i].getValue() != symbol)
antiDiag = false;
}

return mainDiag || antiDiag;


}
};

class Game {
Player p1, p2;
Player* currentPlayer;
Board board;
GameRules rules;

public:
Game(const Player& a, const Player& b, int boardSize)
: p1(a), p2(b), board(boardSize), currentPlayer(&p1)
{}

void switchTurn() {
currentPlayer = (currentPlayer == &p1) ? &p2 : &p1;
}

void play() {
int row, col;
while (true) {
std::cout << currentPlayer->getName() << " (" <<
currentPlayer->getSymbol() << "), enter row and col: ";
std::cin >> row >> col;

if (!board.markCell(row, col, currentPlayer-


>getSymbol())) {
std::cout << "Invalid move. Try again.\n";
continue;
}

if (rules.isWinningMove(board, currentPlayer-
>getSymbol())) {
std::cout << currentPlayer->getName() << "
wins!\n";
break;
}

if (board.isFull()) {
std::cout << "It's a draw!\n";
break;
}

switchTurn();
}
}
};

🔍 Interview Questions at Each Step

Q: Why keep the board state in a separate class?


➡ Separation of concern. It allows Game to focus on ow control, while Board handles
the grid.

Q: What if you wanted to scale to NxN grid?


➡ Pass size into Board constructor. All win-checking logic uses size.

Q: How would you add an AI or a leaderboard?


➡ Inject AIPlayer implementing IPlayer interface. Use leaderboard service class to
track game results.

🧩 Key Functional Requirements

• 2 players with distinct symbols ('X', 'O')

• Board of NxN size

• Mark cells and prevent overwrite

• Check for win or draw

• Show turns and results

💡 How to Think Before You Code

• SRP: Keep each class doing one job

• Open/Closed Principle: AI, NxN grid, leaderboard are pluggable


fl
• Visualize Data Flow:

Player input → Game → Board → GameRules → Result

• Avoid putting validation logic into input/output layers

🎓 Conceptual + Technical Interview Questions

Core LLD

• Q: Can you extend this game to 4 players or 4x4 grid?

◦ A: Yes. Make Board dynamic and Game support a vector of Player

• Q: How would you add a scoreboard system?

◦ A: Create ScoreBoard class to persist and rank player wins

• Q: Where will you inject AI/Minimax logic?

◦ A: Create IPlayer interface. AIPlayer implements logic internally

• Q: Could your classes support undo functionality?

◦ A: Add move history stack in Game, pop and revert board state

OOP & SOLID

• Q: Which classes follow SRP?

◦ A: Board (manages grid), GameRules (rule checks), Player


(identity), Game (control)

• Q: How does Game use encapsulation?

◦ A: It controls access to players, board, rules, and handles the full play loop

• Q: Would you use strategy pattern to plug in different rule sets?

◦ A: Yes. IGameRules can de ne isWinningMove() for different win


conditions (e.g., 5-in-a-row, hex grid)
fi
✅ Low-Level Design of a Logger System – Interview-
Oriented Summary

🎯 Interview Objective

Design a system that:

• ✅ Reads a log le (small or large)

• ✅ Parses entries to detect error messages

• ✅ Maintains a count of each unique error

• ✅ Outputs the top N most frequent errors

🧠 Step 1: Think Like a Designer First

Ask:

• What are the core responsibilities?

• How will the classes interact?

• Where can I decouple logic? (parsing, ltering, reporting)

• Can the system handle large log les (e.g. 10GB)?

• Can I scale or extend this for real-world use?

🧱 Key Components and Class Responsibilities

Class Responsibility
LogReader Reads lines from file (streaming I/O)
Extracts relevant info (e.g., error
LogParser message)
ErrorAggregator Tracks and counts unique error strings
fi
fi
fi
ErrorReporter Outputs the top N most frequent errors
LogFilter Filters logs by timestamp or other rules
(Optional)
Logger (Coordinator) Ties components together (main driver)

🧩 Class Interaction Flow

+-----------+
| LogReader |
+-----------+
|
v
+-----------+
| LogParser |
+-----------+
|
v
+--------------------+
| ErrorAggregator |
+--------------------+
|
v
+----------------+
| ErrorReporter |
+----------------+

🧠 Design Before Code

Ask yourself:

• ❓ Will I read the entire le into memory? → ❌ No. Use streaming line-by-line.

• ❓ Do I want to support timestamp ltering? → ✅ Add a LogFilter.

• ❓ Should I use multithreading? → Optional. Good for parsing or aggregation in


real-time.

• ❓ What if the le is 10GB? → ✅ Stream and process in batches; don't load


everything at once.
fi
fi
fi
🧱 Real-World Design Challenges and Solutions

Challenge Design Consideration


10GB file Stream with std::ifstream; process line-by-line
Use unordered_map; optionally hash & compress error
Many unique errors
strings
Slow processing Add multithreading and a queue-based architecture
Log format changes Plug-in architecture with ILogParser strategy interface
Faulty logs/
Add fallback parsing; maintain invalid.log for bad lines
timestamps

💡 C++ Oriented System Structure

//
-------------------------------------------------------------
// 1. LogReader (Single Responsibility Principle)
//
-------------------------------------------------------------
// Responsibility: Read log file line-by-line.
// Pattern Used: **Iterator-like Behavior** (custom line
reader).
//
-------------------------------------------------------------
class LogReader {
std::ifstream file;
public:
explicit LogReader(const std::string& filePath) {
file.open(filePath);
// Could add error handling if file fails to open.
}

// Check if there are more lines in the file.


bool hasNextLine() {
return file.peek() != EOF;
}

// Get the next line from the file.


std::string getNextLine() {
std::string line;
std::getline(file, line);
return line;
}
};
//
-------------------------------------------------------------
// 2. LogParser (Strategy Pattern Candidate)
//
-------------------------------------------------------------
// Responsibility: Extract errors from a log line.
// Pattern Used: Could apply **Strategy Pattern** here if we
// want to support multiple parsing strategies
// (e.g., parseError, parseWarning, parseInfo).
//
-------------------------------------------------------------
class LogParser {
public:
// Parse a line and return the error message if it
contains "ERROR".
std::string parseError(const std::string& line) {
if (line.find("ERROR") != std::string::npos) {
return line; // Can extract only the error
message portion if needed.
}
return "";
}
};

//
-------------------------------------------------------------
// 3. ErrorAggregator
//
-------------------------------------------------------------
// Responsibility: Keep count of each unique error message
and
// provide top N errors.
// Pattern Used: **Aggregator** logic, no formal GoF pattern,
// but follows SRP (only aggregation
responsibility).
//
-------------------------------------------------------------
class ErrorAggregator {
std::unordered_map<std::string, int> errorCount;
public:
// Increment count for a given error message.
void addError(const std::string& errorMsg) {
if (!errorMsg.empty()) errorCount[errorMsg]++;
}

// Get top N most frequent errors.


std::vector<std::pair<std::string,int>> getTopErrors(int
topN) {
std::vector<std::pair<std::string,int>>
entries(errorCount.begin(), errorCount.end());

std::sort(entries.begin(), entries.end(),
[](const auto& a, const auto& b) {
return a.second > b.second; // sort descending by
count
});

if (topN < entries.size())


entries.resize(topN);

return entries;
}
};

//
-------------------------------------------------------------
// 4. ErrorReporter (Strategy Pattern Candidate)
//
-------------------------------------------------------------
// Responsibility: Present error report to user.
// Pattern Used: **Strategy Pattern** could be applied if
// multiple reporting styles are supported
// (e.g., console, HTML, JSON, file output).
//
-------------------------------------------------------------
class ErrorReporter {
public:
void printTopErrors(const
std::vector<std::pair<std::string, int>>& topErrors) {
std::cout << "Top Errors:\n";
for (const auto& [msg, count] : topErrors) {
std::cout << "- " << msg << " (" << count << "
times)\n";
}
}
};

//
-------------------------------------------------------------
// 5. LogFilter (Optional Component)
//
-------------------------------------------------------------
// Responsibility: Filter logs by timestamp or other
criteria.
// Pattern Used: **Filter / Criteria Pattern** (structural
pattern).
//
-------------------------------------------------------------
class LogFilter {
public:
bool isValid(const std::string& line, const std::string&
minTimestamp) {
// Timestamp parsing and comparison logic goes here.
// For now, always return true.
return true;
}
};

//
-------------------------------------------------------------
// 🚀 Sample Main Flow (Single-threaded Example)
//
-------------------------------------------------------------
int main() {
LogReader reader("log.txt");
LogParser parser;
ErrorAggregator aggregator;
LogFilter filter; // Optional filter if needed.

// Read file line-by-line.


while (reader.hasNextLine()) {
std::string line = reader.getNextLine();

// Filter logs by timestamp (if needed).


if (!filter.isValid(line, "2025-08-10 00:00:00"))
continue;

// Parse only error lines.


std::string error = parser.parseError(line);

// Add error to aggregation.


aggregator.addError(error);
}

// Retrieve top 3 errors.


auto topErrors = aggregator.getTopErrors(3);

// Print the results.


ErrorReporter reporter;
reporter.printTopErrors(topErrors);

return 0;
}
🧠 Conceptual + Interview Questions

❓ Q1: How would you handle a 10 GB log le?

✅ Use ifstream to read line by line, avoid full memory load.

❓ Q2: How can you avoid memory pressure from many unique errors?

✅ Hash messages, compress them, store summaries, or drop very low-frequency ones after
threshold.

❓ Q3: How would you scale this to multiple log les in parallel?

✅ Use one thread per le. Share an ErrorAggregator with locking or use per-thread
aggregators and merge later.

❓ Q4: How can you plug in different log formats or error types?

✅ Use Strategy Pattern with ILogParser interface. Inject different parsers.

❓ Q5: What if timestamp parsing fails or logs are corrupted?

✅ Catch exceptions in parser. Log failures in separate invalid.log le. Continue


processing.

✅ Summary of Thought Process

Design Layer Justification


Modular Classes SRP and clean separation
Stream-based reading Scales for large files
Top-N using heap Efficient and optimal
Extensible
Add filters, formats, metrics later
architecture
For high-throughput real-world
Optional threading
use

📌 Final Thoughts
fi
fi
fi
fi
"What seems like a simple problem is actually a gateway to mastering clean design,
performance tuning, and real-world scalability."

So in interviews:

• ✅ Start with class design and justify each

• ✅ Talk through real-world challenges

• ✅ Mention extensibility and scalability

• ✅ Only go into code if asked

✅ System Summary: Real-Time Stock Board

A real-time stock board displays updated stock information (name, price, change %) on a
webpage. The backend must:

• Continuously fetch/update stock data

• Push updates to UI components

• Be designed to scale, use multithreading, and handle high-frequency updates

⚙ Mental Model Before Coding (Core Concepts)

1. Actors & Responsibilities:

◦ StockDataProvider → Feeds live stock data (simulate with random


generator or API)

◦ StockBoard (UI Component) → Displays data

◦ StockManager (Core) → Manages registered boards and noti es them of


updates

◦ ThreadPool / Dispatcher → Processes updates in the background

◦ Logger / Audit (optional) → For logging updates

2. Design Principles Used:

◦ Observer Pattern → Boards subscribe to updates


fi
◦ Singleton → Central stock manager

◦ Multithreading → Async updates, using std::thread or


std::async

◦ SOLID Principles (each component follows SRP, OCP, DIP)

📦 Class Diagram (Mental Visualization)

+------------------+
| StockDataProvider|
+--------+---------+
|
v (pushes data)
+------------------+
| StockManager | <-- Singleton
+--------+---------+
|
+---------------+----------------+
| | |
v v v
+------------+ +-------------+ +-------------+
| StockBoard | | StockBoard | | Logger (opt)|
+------------+ +-------------+ +-------------+

👨💻 C++ Class Framework (Backend-Only)

//
============================================================
// 1. Observer Interface
//
============================================================
// Responsibility: Define the contract for all observers (UI
components, loggers, alert systems).
// Pattern Used: **Observer Pattern** (Behavioral)
// - Observers register to a subject
(StockManager).
// - They get notified automatically when stock
prices update.
//
============================================================
class IStockObserver {
public:
virtual void update(const std::string& symbol, double
price) = 0;
virtual ~IStockObserver() = default;
};

//
============================================================
// 2. Subject (StockManager)
//
============================================================
// Responsibility: Maintain stock prices and notify observers
on changes.
// Pattern Used:
// - **Singleton**: Only one StockManager instance for the
whole system.
// - **Observer Pattern**: Manages list of observers and
notifies them.
//
============================================================
class StockManager {
private:
std::unordered_map<std::string, double> stockData;
std::vector<IStockObserver*> observers;
std::mutex mtx;

// Private constructor for Singleton


StockManager() = default;

public:
// Static accessor for Singleton instance
static StockManager& getInstance() {
static StockManager instance;
return instance;
}

// Disable copy and assignment for Singleton


StockManager(const StockManager&) = delete;
StockManager& operator=(const StockManager&) = delete;

// Register an observer
void registerObserver(IStockObserver* observer) {
std::lock_guard<std::mutex> lock(mtx);
observers.push_back(observer);
}

// Update stock price and notify observers


void updateStock(const std::string& symbol, double price)
{
{
std::lock_guard<std::mutex> lock(mtx);
stockData[symbol] = price;
}

notifyObservers(symbol, price);
}

private:
// Notify all observers asynchronously (non-blocking UI
updates)
void notifyObservers(const std::string& symbol, double
price) {
std::lock_guard<std::mutex> lock(mtx);
for (auto observer : observers) {
std::async(std::launch::async, [=]() {
observer->update(symbol, price);
});
}
}
};

//
============================================================
// 3. Concrete Observer (StockBoard)
//
============================================================
// Responsibility: Display stock price updates.
// Pattern Used: **Observer Pattern** (Concrete Observer
implementation)
//
============================================================
class StockBoard : public IStockObserver {
std::string boardName;

public:
explicit StockBoard(const std::string& name) :
boardName(name) {}

void update(const std::string& symbol, double price)


override {
std::cout << "[" << boardName << "] " << symbol << "
updated to " << price << std::endl;
}
};

//
============================================================
// 4. Stock Data Provider (Simulated External API)
//
============================================================
// Responsibility: Simulate live market data feed.
// Pattern Used: **Strategy Pattern Candidate** if we wanted
multiple data sources.
//
============================================================
class StockDataProvider {
public:
void start() {
std::vector<std::string> symbols = {"AAPL", "GOOG",
"TSLA"};
while (true) {
for (const auto& symbol : symbols) {
double newPrice = 100 + (rand() % 100); //
Random price

StockManager::getInstance().updateStock(symbol, newPrice);

std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
}
};

//
============================================================
// 5. Main Function (Wiring it all together)
//
============================================================
int main() {
// Create two stock boards (observers)
StockBoard board1("Board1");
StockBoard board2("Board2");

// Register observers to the stock manager


StockManager::getInstance().registerObserver(&board1);
StockManager::getInstance().registerObserver(&board2);

// Start data provider in a separate thread


StockDataProvider provider;
std::thread dataThread(&StockDataProvider::start,
&provider);

// Wait for data thread (simulates long-running server)


dataThread.join();

return 0;
}
🎯 Interview-Ready Questions

Conceptual

• Q: Why did you use Observer pattern here?

• Q: How would you scale this if 10K clients are connected?

• Q: Can this design support websocket-based updates?

Multithreading

• Q: Why use std::async for observer noti cation?

• Q: What thread-safety mechanisms are in place?

Scalability & Extensibility

• Q: How would you make the update frequency con gurable per observer?

• Q: How can you persist this stock data?

Edge Handling

• Q: What happens if one observer crashes?

• Q: How to handle stale clients?

🧠 How to Think Before Coding in Interviews

1. Clarify Core Use Cases

◦ Real-time data push, multiple clients, async updates.

2. Identify Actors

◦ Data provider, manager, UI/client, loggers, etc.

3. Choose Patterns

◦ Observer, Singleton, Threading

4. Design Interfaces First

◦ Think in contracts: IStockObserver, StockManager

5. Think Scalability
fi
fi
◦ Locking strategies, async updates, thread pools

6. Draw Sequence + Class Diagram in Mind

◦ "Who talks to whom, when, and how?"

🧩 You Can Extend This To...

• Add WebSocket or gRPC for real-time browser updates.

• Add logging using Logger class as a separate observer.

• Add priority queues if some stocks are more important.

✅ System Summary: Basic Amazon Website


You’re designing a minimal Amazon-like system that supports:

• User login/signup

• Browsing/searching products

• Viewing product details

• Adding items to a cart

• Placing an order

Bonus (for scaling & interview edge):

• Track orders, cancel/refund

• Admin: Add/remove products

⚙ Mental Model Before Coding

🧠 Think Like a User

• "I want to browse shoes"

• "Add it to my cart"

• "Login and checkout"

• "Track my order"
🧩 Actors and Interactions

Actor Role
Browses, logs in, adds to cart,
User
orders
Product Represents items for sale
Cart Holds selected items for the user
Order Final purchase containing cart items
Admin Can add/remove/update products
SearchServic
Enables product search
e

🏗 Class Design — Mental Map

+------------+
| User |
+------------+
|
+------+------+
| |
+---v---+ +----v----+
| Cart | | Order |
+---+---+ +----+-----+
| |
+---v---+ +---v-----+
|Product | |Payment |
+--------+ +---------+

👨💻 C++ Class Framework (Backend Only)


//
============================================================
// 1. Product
//
============================================================
// Responsibility: Represents a single product in the store.
// Pattern: **Data Model / Entity**
// - Could be created via **Factory Pattern** if
there are
// many types of products with different creation
logic.
//
============================================================
class Product {
int id;
std::string name;
std::string category;
double price;

public:
Product(int id, std::string name, std::string category,
double price)
: id(id), name(std::move(name)),
category(std::move(category)), price(price) {}

int getId() const { return id; }


std::string getName() const { return name; }
std::string getCategory() const { return category; }
double getPrice() const { return price; }
};

//
============================================================
// 2. Cart
//
============================================================
// Responsibility: Stores products selected by the user
before checkout.
// Pattern: **Composite-like structure** (manages a
collection of items).
//
============================================================
class Cart {
std::unordered_map<int, int> productIdToQuantity;

public:
void addProduct(int productId, int quantity) {
productIdToQuantity[productId] += quantity;
}

void removeProduct(int productId) {


productIdToQuantity.erase(productId);
}

const std::unordered_map<int, int>& getItems() const {


return productIdToQuantity;
}

void clear() {
productIdToQuantity.clear();
}
};

//
============================================================
// 3. User
//
============================================================
// Responsibility: Represents a customer in the system.
// Pattern: **Entity**
//
============================================================
class User {
int id;
std::string name;
std::string email;
Cart cart;

public:
User(int id, std::string name, std::string email)
: id(id), name(std::move(name)),
email(std::move(email)) {}

Cart& getCart() { return cart; }


int getId() const { return id; }
std::string getName() const { return name; }
std::string getEmail() const { return email; }
};

//
============================================================
// 4. Order
//
============================================================
// Responsibility: Represents a placed order with its items.
// Pattern: **Data Transfer Object (DTO)**
//
============================================================
class Order {
int orderId;
int userId;
std::vector<std::pair<int, int>> items; // (productId,
quantity)
double totalAmount;
std::string status;
public:
Order(int id, int uid, const std::unordered_map<int,
int>& cartItems, double total)
: orderId(id), userId(uid), totalAmount(total),
status("PLACED") {
for (const auto& item : cartItems) {
items.push_back(item);
}
}

void setStatus(const std::string& newStatus) {


status = newStatus;
}

std::string getStatus() const { return status; }


double getTotalAmount() const { return totalAmount; }
int getOrderId() const { return orderId; }
};

//
============================================================
// 5. StoreManager (Facade + Singleton-like Control)
//
============================================================
// Responsibility: Acts as the **Facade** for managing
products,
// users, carts, and orders. Encapsulates all business logic.
// Patterns:
// - **Facade**: Simplifies access to subsystems.
// - Could be turned into **Singleton** if we want a single
store
// instance globally accessible.
//
============================================================
class StoreManager {
std::unordered_map<int, Product> products;
std::unordered_map<int, User> users;
std::vector<Order> orders;
int nextOrderId = 1;

public:
void addProduct(const Product& p) {
products[p.getId()] = p;
}

void registerUser(const User& user) {


users[user.getId()] = user;
}
void addToCart(int userId, int productId, int quantity) {
if (products.find(productId) == products.end()) {
std::cout << "Product not found.\n";
return;
}
users[userId].getCart().addProduct(productId,
quantity);
}

void checkout(int userId) {


auto& cart = users[userId].getCart();
double total = 0;

for (auto [pid, qty] : cart.getItems()) {


total += products[pid].getPrice() * qty;
}

orders.emplace_back(nextOrderId++, userId,
cart.getItems(), total);
cart.clear();
std::cout << "Order placed! Total: $" << total <<
"\n";
}
};

//
============================================================
// 🧪 Main Example
//
============================================================
int main() {
StoreManager store;

// Setup store inventory


store.addProduct(Product(1, "Laptop", "Electronics",
1000));
store.addProduct(Product(2, "Shoes", "Fashion", 100));

// Register a new user


store.registerUser(User(101, "Alice",
"alice@example.com"));

// User adds items to cart


store.addToCart(101, 1, 1); // 1 Laptop
store.addToCart(101, 2, 2); // 2 Shoes

// Checkout
store.checkout(101);
return 0;
}

🎯 Interview-Facing Conceptual Questions

📌 Patterns & Principles

• Q: What design patterns did you use?

◦ A: Facade (StoreManager), Singleton-like access, SRP for each class

• Q: Can the system scale to millions of users?

◦ A: We'd use databases, sharding by userID, caching products with Redis,


async orders with a queue

• Q: How do you handle concurrency?

◦ A: Add thread-safe structures (mutex, atomic ops), especially for cart/order


updates

📌 Real-World Enhancements

• Track order status: Placed → Shipped → Delivered

• Add payment gateway simulation

• Add inventory/stock quantity check

• Add product search: by name/category (via SearchService)

🧠 System Design Thought Process Recap

Step Thought Process


User flow How do users interact? (browse, cart, order)
Entities User, Product, Cart, Order
Responsibility SRP for each class (User manages cart, not orders)
Design
Use Facade (StoreManager) to abstract workflows
Pattern
Can add payment, admin, reviews without breaking
Extensibility
flow
✅ 1. Scalability

“How would this system scale if 100 million users shopped concurrently?”

🔧 Challenges:

• Too many concurrent users

• Read-heavy load (product browsing)

• Write spikes (checkout, reviews)

💡 Design Insights:

• Horizontal Scaling: Microservices for user, product, cart, order

• Caching Layer: Use Redis/Memcached for product listings

• Load Balancer: Distribute traf c (NGINX, HAProxy, AWS ELB)

• CDN for Images: CloudFront for product images

🧠 Discussion Triggers:

• What should be cached vs always fetched?

• How do you invalidate stale product cache?

• How would you shard users or orders in DB?

✅ 2. Data Consistency

“What if two users try to buy the last item?”

🔧 Challenges:

• Cart inconsistency

• Double order

• Stale reads from cache

💡 Design Insights:
fi
• Pessimistic/Optimistic Locking in DB

• Eventual Consistency for product info, reviews

• Atomic operations with Redis Lua script for inventory

🧠 Discussion Triggers:

• When is strong consistency mandatory?

• Can two carts hold the same item?

• How would you model transaction rollback?

✅ 3. Database Design

“Show me your table schemas or data models.”

🔧 Core Tables:

• Users(id, name, email)

• Products(id, name, category, price, stock)

• Cart(user_id, product_id, quantity)

• Orders(id, user_id, status, total, created_at)

💡 Design Insights:

• Index frequently queried columns: category, name

• Use NoSQL (MongoDB/DynamoDB) for exible product attributes

• RDBMS for ACID compliance on orders

🧠 Discussion Triggers:

• How to store product variants (size/color)?

• When do you choose SQL vs NoSQL?

• How to scale order storage?

✅ 4. Search & Filtering


fl
“How do you support fast search over millions of products?”

🔧 Challenges:

• Full-text search (product name, description)

• Filtering by category, price range

💡 Design Insights:

• Elasticsearch or Solr for product search

• Precomputed lters and faceted search

🧠 Discussion Triggers:

• Can we build our own search?

• How would you support typo-tolerance in search?

✅ 5. High Availability & Fault Tolerance

“What happens if the product DB goes down?”

🔧 Challenges:

• Single point of failure

• Partial failures in microservices

💡 Design Insights:

• Replicated Databases (Primary-Replica)

• Circuit Breakers (Hystrix-style patterns)

• Graceful Degradation: show limited product info

🧠 Discussion Triggers:

• How do users continue shopping during failure?

• Do we retry or queue failed orders?

✅ 6. Concurrency & Threading


fi
“How does multithreading come into play in your backend?”

🔧 Challenges:

• Multiple users editing cart at once

• Backend threads for handling large loads

💡 Design Insights:

• Use Thread Pools for handling incoming HTTP requests

• Lock-free structures for high concurrency

• Asynchronous processing (std::async, thread in C++)

🧠 Discussion Triggers:

• Where should you use mutexes?

• Can async processing delay order con rmation?

✅ 7. Security

“How do you secure your application?”

🔧 Challenges:

• Data leakage (user/cart info)

• Fake orders (API misuse)

• Unauthorized access to admin actions

💡 Design Insights:

• Authentication: JWT or OAuth2

• Authorization: RBAC – User vs Admin

• Data Encryption: TLS for transit, AES at rest

• Rate Limiting to prevent abuse

🧠 Discussion Triggers:
fi
• How to detect fraudulent activity?

• What about SQL injection or XSS?

✅ 8. Session Management & Cart Persistence

“How do you persist cart for logged-out users?”

💡 Design Insights:

• Use Cookies/LocalStorage for anonymous cart

• Sync with user account on login

• Store session data in Redis

🧠 Discussion Triggers:

• What if user adds to cart on mobile and logs in from desktop?

• Should the cart auto-merge?

✅ 9. Asynchronous Processing

“Which parts would you make async?”

💡 Examples:

• Order con rmation email

• Invoice generation

• Stock decrement / noti cation

🧠 Tools:

• Message Queues (Kafka, RabbitMQ)

• Background workers (cron jobs, C++ threads or service workers)

✅ 10. Logging, Monitoring, Alerting

“How do you monitor and troubleshoot your live system?”


fi
fi
💡 Design Insights:

• Logs: user activity, orders, errors

• Metrics: average response time, active carts

• Alerts: order drop rate spike, search latency increase

🧠 Tools:

• ELK Stack (Elasticsearch, Logstash, Kibana)

• Prometheus + Grafana

• Custom alerts for order pipeline errors

🧠 Interview Conversation Flow You Should Practice


1. Clarify Requirements

"Are we designing just the backend or also front-end?"


"Should it support multiple product variants?"

2. List Core Features

Auth, browse, cart, order, admin, etc.

3. Design Class Model (C++)

Show SRP-compliant classes, use patterns like Facade, Observer, etc.

4. Talk About Challenges

Scale, concurrency, search, consistency

5. Suggest Enhancements

Payment system, recommendation engine, analytics

Distributed Systems with Examples

1. Distributed Systems Overview


What is a Distributed System?
A distributed system consists of independent machines interacting to achieve a
common goal. To the end-user, it appears as a single cohesive system.

Example: Social media platforms like Facebook operate on distributed systems


where servers globally work together to deliver content seamlessly.

Why Use Distributed Systems?


Centralised systems are limited in processing power, storage, and traf c handling.
Distributed systems overcome these limitations, offering improved reliability,
scalability, and maintainability.


2. Reliability
De nition:
Reliability is the system’s ability to function correctly even when components fail.

Key Concepts:
Faults: Deviations in speci c nodes (e.g., slow response in Facebook’s Activity
feed).
Failures: Complete system breakdown (e.g., if unhandled faults cascade, causing
Facebook to go of ine).

Causes of Faults & How to Avoid Them:


Hardware Faults: (e.g., disk crashes) → Use redundant power sources, replicate
data across servers.
Software Faults: (e.g., buggy code) → Perform comprehensive testing, monitor
systems actively.
Human Faults: (e.g., miscon gurations) → Implement access controls, meaningful
UI with con rmation prompts.

When is Sacri cing Reliability Acceptable?


Prototypes, low-budget projects, or when rapid development is crucial.


3. Scalability
De nition:
Scalability refers to a system’s capacity to handle increasing loads ef ciently.

What is Load?
Load is the demand placed on system resources (e.g., number of user requests).

Load Examples:
Web Server: Number of requests processed per second.
Chat Rooms: Concurrent users active simultaneously.

Measuring Load:
Mean Response Time: Average system response.
Median Response Time: Middle value to gauge typical performance.
fi
fi
fi
fi
fl
fi
fi
fi
fi
Percentile (e.g., 90th): 90% of requests are faster than this threshold.

Factors Affecting Scalability:


Architecture Design: Modularity, decoupling.
Resource Management: Load balancing, resource allocation.
Network Infrastructure: Bandwidth, latency.

Improving Scalability:
Load Balancers: Distribute traf c evenly.
Auto-Scaling: Adjust resources dynamically.
Caching & CDNs: Reduce data retrieval times.

Scaling Techniques:
Horizontal Scaling: Adding more machines.
Vertical Scaling: Enhancing existing machine capabilities.


4. Maintainability
De nition:
Maintainability is the ease of updating and managing the system over time.

Key Characteristics:
Operable: Easy to monitor and troubleshoot (e.g., clear logging systems).
Simple: Well-organised codebase, minimal complexity.
Evolvable: Modular design allows easy addition of new features.

Example: Cloud applications like Google Drive are highly maintainable, frequently
updated without affecting user experience.


Conclusion
Understanding reliability, scalability, and maintainability is crucial in designing
robust distributed systems that can handle large-scale operations ef ciently and
adapt to technological advancements.

Client Server Architecture

What is Client-Server Architecture?

Client-server architecture is a network architecture in which clients request


resources or services from a centralized or decentralized server.
fi
fi
fi
Components of Client-Server Architecture

These are some essential components that are necessary for every client-server
architecture, whether they are used for small purposes or for large-scale work:

• Client: The client is a device or computer that requests services or


resources from the server. The client can be a desktop computer, a laptop, a
mobile phone, or any other computing device.
◦ Thin Clients:
▪ Light Weight: They process the least amount of information.
▪ Heavily dependent on servers: These types of clients are
heavily dependent on the servers for processing.
▪ Example: Consider a web-based email service like Gmail.
When you access your email account through a web browser,
you are using a thin client. All of the email processing and
storage are handled by the Gmail servers, and your web
browser simply displays the information that is sent from the
server.
◦ Thick Clients:
▪ Bulky: Process large amounts of information.
▪ Relatively independent from servers: These types of clients are
relatively independent of the servers.
▪ Example: Consider video editing software like Adobe
Premiere. When you use Premiere to edit a video, you are
using a thick client. Premiere is a desktop application that runs
locally on your computer and uses the resources of your
computer to perform tasks like rendering and transcoding. The
server is only used to provide updates and licensing
information, but most of the processing happens locally on
your computer.
• Server: The server is a centralized computer or set of computers that
provide services or resources to the clients. The server typically manages
data storage, communication between clients, and processing of client
requests.
• Network: The network is the physical or virtual infrastructure that connects
the clients and the server. It can be a local area network (LAN), a wide area
network (WAN), or the Internet.
Usage of Ports in the Client-Server Architecture

Below is how ports are used in the client-server architecture:

• Ports are used to identify network services on a server.


• Each service listens on a speci c port, and clients connect to that port to
access the service.
• Servers can have multiple services running at the same time, each listening
on a di erent port.
◦ Ports are a standardized way of identifying services, which enables
di erent applications and operating systems to communicate with
each other over a network. There are well-known port numbers for
ff
ff
fi
common services, such as HTTP, FTP, SMTP, etc., which are
assigned by the IANA.
◦ Applications can also use non-standard port numbers for their
services. Port numbers are typically 16-bit unsigned integers, which
means there are 65,536 possible port numbers. However, not all of
them are available for use.
◦ Ports are used in both TCP and UDP protocols. TCP is a connection-
oriented protocol, which means it establishes a reliable, ordered, and
error-checked connection between two endpoints. UDP is a
connectionless protocol, which means it does not establish a
dedicated end-to-end connection between two endpoints.
Types of Client-Server Architecture

Client-server architecture is a popular model for designing distributed computing


systems, where one or more servers provide services to multiple clients over a
network. There are several di erent types of client-server architecture, each with
its own advantages and disadvantages.

1. Two-Tier Architecture

In this model, the client application communicates directly with the server, which
provides the required service. This is the simplest type of client-server architecture,
but it has some limitations.

• Advantages:
◦ Simple to implement
◦ Faster response times
◦ Lower Latency
• Disadvantages:
◦ Limited scalability
◦ Increased server load
◦ Lack of fault tolerance
• Real-life Examples:
◦ File and print server
◦ Web server
2. Three-Tier Architecture

In this model, the client application communicates with an application server,


which in turn communicates with the database server to retrieve or store data. This
type of architecture allows for greater scalability and fault tolerance.

• Advantages:
◦ Greater scalability
◦ Better fault tolerance
◦ Easier to maintain
• Disadvantages:
◦ Increased complexity
◦ Higher latency
◦ Increased cost
ff
• Real-life Examples:
◦ Online banking systems
◦ E-commerce websites
3. N-Tier Architecture:

In this model, the application is divided into multiple tiers or layers, each with a
speci c responsibility. This type of architecture allows for even greater scalability,
exibility, and fault tolerance.

• Advantages:
◦ Maximum scalability
◦ Maximum exibility
◦ Maximum fault tolerance
• Disadvantages:
◦ Increased complexity
◦ Higher latency
◦ Increased cost
• Real-life Examples:
◦ Large-scale enterprise systems
◦ Cloud computing platforms
Summary

The di erent types of client-server architecture o er various trade-o s between


simplicity, scalability, fault tolerance, and cost. Choosing the right architecture
depends on the speci c requirements of the application and the available
resources.

In today’s digital world, e cient communication between systems is crucial for


performance, scalability, and security. Understanding network protocols and
proxies is key to building robust systems. This article will guide you through the
basic concepts, important protocols, and proxies, explaining their roles in system
design with real-world examples.

What Are Network Protocols?

Network protocols are standardised rules that enable communication between


devices on a network. Imagine them as a common language that devices like
computers and servers use to exchange data reliably. Without these protocols,
devices would struggle to interpret and process data e ectively.
fl
fi
ff
fl
fi
ffi
ff
ff
ff
Why Do We Need Network Protocols?

Protocols are essential for several reasons:

Consistency: They ensure data is formatted and transmitted in a predictable way.


Interoperability: Devices from di erent manufacturers can communicate
seamlessly.
Reliability: Protocols help avoid data loss or corruption during transmission.
Security: Protocols like HTTPS and TLS secure data from unauthorised access.

Examples of Protocols in Action:

• Accessing a Website: When you type a URL in your browser, the HTTP protocol
requests the web page data. If the connection is secure, HTTPS encrypts the
data.
• Streaming Video: Protocols ensure data packets are delivered in the correct
order, even if the connection brie y drops.

OSI Model and TCP/IP Model

The OSI model and TCP/IP model are fundamental frameworks that de ne the
layers of network communication.

The OSI (Open Systems Interconnection) model and TCP/IP model describe how
di erent network protocols work across layers. Here’s an overview of the OSI
model:
ff
fl
ff
fi
Di erent protocols operate at each layer of the network. For instance, HTTP works
at the Application Layer, while TCP operates at the Transport Layer.

Key Network Protocols

Now, let’s explore some of the most commonly used network protocols in system
design:

1. HTTP and HTTPS:

- HTTP (Hypertext Transfer Protocol) is used to transfer data, such as web pages,
from a server to a client.

- HTTPS (Hypertext Transfer Protocol Secure) adds encryption for secure data
transmission, particularly when handling sensitive information like credit card
details.

Advantages:

- Simplicity: Easy to implement and widely supported.


- Security: HTTPS ensures encrypted communication, preventing data
interception.

Disadvantages:

- Stateless: HTTP doesn’t maintain user session data, requiring additional


mechanisms like cookies or tokens.
- Overhead in HTTPS: Encryption and decryption introduce processing overhead,
causing slight delays.

2. TCP (Transmission Control Protocol):

- TCP is a connection-oriented protocol that ensures reliable data transmission by


breaking data into packets and reassembling them at the destination.

- Real-life example: Email services like Gmail rely on TCP to deliver emails without
errors.

Advantages:

- Reliability: Guarantees data packets are delivered correctly.


- Error Checking: Ensures the integrity of transmitted data.

Disadvantages:
ff
- Slower Speed: The reliability mechanisms (like retransmitting lost packets)
introduce latency.

3. UDP (User Datagram Protocol):

UDP, a connectionless protocol, prioritises speed over reliability. It’s ideal for real-
time data delivery scenarios where accuracy isn’t paramount. For instance, online
gaming uses UDP to minimise lag, even if some packets are lost. Similarly, video
conferencing apps like Zoom may experience brief video freezes due to packet
loss.

• UDP’s advantages include low latency, which makes it suitable for time-sensitive
applications, and its broadcast capability, allowing simultaneous messages to
multiple recipients. However, it lacks a guarantee of delivery, meaning some
packets may be lost or arrive out of order.

WebSocket, on the other hand, enables real-time bidirectional communication


between clients and servers, unlike HTTP, which is unidirectional. Chat applications
like WhatsApp utilise WebSocket for seamless real-time messaging.

• WebSocket’s advantages include e ciency for real-time data, eliminating the


need for repeated requests, and its low latency, making it ideal for instant
interactions. However, it introduces increased complexity, especially when
managing WebSocket connections, particularly regarding connection lifecycles
and scalability. Additionally, WebSockets consume server resources due to their
persistent connections, which can become a concern when dealing with a large
number of clients.

Proxy servers act as intermediaries between clients and servers. Instead of


connecting directly to the server, clients send requests to the proxy, which
forwards them to the server.

Proxy servers come in various types, each with unique functions.

• Forward proxies act as intermediaries between clients and the internet,


forwarding client requests to the server on their behalf. This is commonly used in
corporate networks to restrict access to certain websites or monitor user activity.

• Reverse proxies, on the other hand, sit between external clients and internal
servers, forwarding client requests to the appropriate server. They are often
employed for load balancing, caching, and security purposes.

• Real-world examples of proxy servers include content delivery networks (CDNs),


such as Cloud are, which use reverse proxies to cache data and deliver content
from the server closest to the user, enhancing performance. Additionally, online
fl
ffi
stores like Amazon utilise reverse proxies to distribute user requests across
multiple servers, ensuring smooth operation during peak times.

• Forward proxies o er several advantages. They provide privacy by concealing


your IP address from websites you visit, safeguarding your online identity.
Forward proxies also enable access control, allowing you to block or allow
access to speci c websites or content based on prede ned rules. Furthermore,
forward proxies facilitate caching, speeding up browsing by storing frequently
accessed web content locally.

• Reverse proxies, in turn, o er several advantages. They excel at load balancing,


distributing tra c to prevent overloading. Reverse proxies also utilise caching to
expedite response times by storing frequently accessed data. Additionally,
reverse proxies enhance security by concealing internal servers from external
users, thereby fortifying network security.

• However, network design presents challenges associated with protocols and


proxies. One such challenge is latency, which arises from the additional hops
through proxies, potentially causing delays in communication.

• Managing multiple proxies requires careful planning due to their complexity. A


single point of failure, such as a proxy server going down, can disrupt
communication. To mitigate this risk, system designers should implement best
practices.

• One best practice is to use redundancy by employing multiple proxies, ensuring


high availability. Regularly analysing proxy logs provides valuable insights into
performance optimisation. Additionally, choosing the appropriate protocol is
crucial. TCP is ideal for reliability, while UDP is suitable for speed, depending on
the speci c use case.

In conclusion, network protocols and proxies are fundamental components of


system design. Understanding the di erent protocols, such as HTTP, TCP, and
UDP, and how proxy servers function, enables designers to create e cient,
scalable, and secure systems. Whether designing a real-time chat application or
optimising a CDN for global reach, the right combination of protocols and proxies
can make a signi cant di erence.

High-Level Design (HLD) Diagrams - Notes & Visual Representation

De nition

HLD diagrams provide a bird’s-eye view of a system, showcasing its architecture,


major components, and their interactions.
fi
fi
ffi
fi
fi
ff
ff
ff
ff
fi
ffi
Purpose

✅ Simpli es system architecture representation ✅ Improves communication


among stakeholders ✅ Serves as a reference for development teams

Example:

An HLD diagram for an e-commerce application may include:

User interface

API layer

Database

Third-party integrations

Importance of HLD Diagrams

✅ Clarity – Represents complex systems concisely ✅ Collaboration – Helps


teams understand and cooperate better ✅ Transition – Bridges design and
implementation

Core Components

🔹 Modules/Components – Authentication, Payment Gateways, etc. 🔹 Interfaces


– APIs, messaging protocols 🔹 Data Flow – Information exchange across
components 🔹 External Systems – Third-party services 🔹 Infrastructure Details
– Servers, networks, databases 🔹 Actors – Users or external systems interacting
with the application

Types of HLD Diagrams

1. Component Diagrams

De nition: Show how system components are arranged and interrelated.

🔹 Key Notations:

Components: Rectangles labeled for distinct functionalities

Interfaces: Circles & half-circles (provided/required)


fi
fi
Relationships: Solid lines (association) & dashed arrows (dependency)

Ports: Small squares indicating interaction points

📌 Example Component Diagram:

2. Sequence Diagrams

De nition: Show interactions between objects or components over time. 🎯


Purpose: Highlight message sequence & timing.

🔹 Notations:

Actors: Stick gure notation

Lifelines: Dashed vertical lines representing object existence

Messages: Arrows (solid for synchronous, dashed for asynchronous)

Activation Bars: Represent processing duration

📌 Example Sequence Diagram:


fi
fi
3. Deployment Diagrams

De nition: Show physical infrastructure on which software components are


deployed.

🔹 Key Elements:

Nodes: Physical/virtual environments

Components: Software modules deployed on nodes

Artifacts: Files associated with components

Dependencies: Relationships between nodes & components

📌 Example Deployment Diagram:


fi
Steps to Create HLD Diagrams

1⃣ Identify Scope & Requirements – De ne system boundaries


2⃣ List Major Components – Determine core functionalities
3⃣ De ne Interfaces – Specify connections & interactions
4⃣ Visualize Relationships – Establish component dependencies
5⃣ Include Infrastructure Details – Servers, networks, databases
6⃣ Draw the Diagram – Use tools like Lucidchart, Visio, or Draw.io
7⃣ Review & Re ne – Validate with stakeholders

Best Practices

✅ Simplicity – Keep diagrams concise & clear ✅ Consistency – Use standard


symbols & naming conventions ✅ Modular Design – Divide system into
manageable sections ✅ Color Coding – Group related components & provide
legends ✅ Stakeholder Validation – Regular reviews for alignment

Simplifying Complexity in HLD Diagrams

📌 Abstract Details – Focus on high-level components 📌 Iterative Re nement –


Improve diagrams step by step 📌 Use Scenarios – Represent critical user
journeys
fi
fi
fi
fi
Conclusion

HLD diagrams are essential tools for system design. 🔹 They simplify complexity,
improve collaboration, and provide a bridge between design & implementation. 🔹
Following best practices ensures clarity, consistency, and e ective stakeholder
communication.

Domain Name System (DNS)

Introduction to DNS

The Domain Name System (DNS) is responsible for translating human-readable


domain names into IP addresses, enabling computers to locate and communicate
with each other on the Internet.

What is DNS?

✅ DNS converts domain names (e.g., example.com) into numerical IP addresses


(e.g., 192.168.1.1). ✅ It is a fundamental component of Internet infrastructure that
simpli es navigation for users.

Where Does DNS Operate?

DNS functions in a client-server architecture: 🔹 The client (web browser, mail


server, or device) requests a domain name resolution. 🔹 The DNS server
responds with the matching IP address.

Importance of DNS in Client-Server Architecture

🔹 Simpli es Internet access – Eliminates the need to remember IP addresses. 🔹


Enables Load Balancing – Distributes tra c among multiple servers. 🔹 Provides
Fault Tolerance – Ensures availability by redirecting requests in case of failure.

DNS Resolution Process - Steps

1⃣ Client Query: The user’s device queries a local DNS resolver for an IP address.
2⃣ Cache Check: If cached, the resolver returns the IP address; if not, it forwards
the query.
fi
fi
ffi
ff
3⃣ Root DNS Server: Responds with the Top-Level Domain (TLD) server address
(.com, .org, etc.).
4⃣ TLD Server Lookup: The TLD server directs the query to the authoritative DNS
server for the domain.
5⃣ Authoritative DNS Response: Returns the correct IP address of the requested
domain.
6⃣ Final Connection: The client uses the retrieved IP address to connect to the
web server.

Advantages of Using DNS

✅ Human-Friendly Access – Users can remember names instead of numeric IPs.


✅ Load Balancing – Distributes tra c to optimize performance. ✅ Fault
Tolerance – Redirects tra c if a server fails. ✅ Simpli es Website Migration –
Allows moving domains without a ecting users.

Where is the DNS File Located?

🔹 The DNS le resides on the DNS server. 🔹 It contains records that de ne


domain name resolutions. 🔹 Can be modi ed manually or through a DNS
management tool to add/update domains.

Request Routing in DNS

Introduction to Request Routing

Request routing in the Domain Name System (DNS) ensures that human-readable
domain names (e.g., www.example.com) are mapped to numerical IP addresses
that computers use to locate web servers. The DNS system is distributed and
hierarchical, with multiple levels of servers managing di erent parts of the domain
name resolution process.

Process of Request Routing

Step-by-Step Overview

1⃣ User Request: 🔹 A user enters a domain name in their web browser. 🔹 The
browser sends a DNS query to a local DNS resolver, typically provided by the ISP
(Internet Service Provider).
fi
ffi
ff
ffi
fi
ff
fi
fi
2⃣ Local Cache Check: 🔹 The resolver checks its cache for the requested
domain’s IP address. 🔹 If found, it immediately returns the IP address to the
user’s browser. 🔹 If not found, the resolver forwards the request to a Root DNS
Server.

3⃣ Root DNS Server Query: 🔹 The Root DNS Server does not store IP addresses
but redirects the request to the appropriate Top-Level Domain (TLD) Server based
on the domain extension (e.g., .com, .org, .net).

4⃣ Top-Level DNS Server Lookup: 🔹 The TLD Server processes the request and
forwards it to the Authoritative DNS Server for the domain.

5⃣ Authoritative DNS Server Response: 🔹 The Authoritative DNS Server contains


DNS records for the requested domain and returns the correct IP address to the
resolver.

6⃣ Final Connection: 🔹 The DNS resolver sends the IP address back to the
user's web browser, which then establishes a connection to the website’s server.

📌 Diagram Representation:
Logical Components of DNS

🔹 Root DNS Server – Directs requests to the appropriate TLD server. 🔹 Top-
Level DNS Server – Manages speci c domain extensions like .com, .org, .net. 🔹
Authoritative DNS Server – Provides the nal IP address for the requested domain.
🔹 DNS Resolver – The software component that processes DNS requests and
returns results to clients (e.g., browsers). 🔹 DNS Cache – A local storage that
maintains recent IP addresses to speed up future queries and reduce DNS server
load.

Summary

✔DNS request routing ensures fast and reliable domain resolution. ✔ It allows
users to access websites without needing to remember IP addresses. ✔ The
hierarchical structure distributes query load across multiple servers, improving
performance.

DNS Caching

What is DNS Caching?

🔹 DNS caching is a process that accelerates DNS resolution by storing previously


resolved queries, reducing the need for repeated lookups. 🔹 Without caching,
DNS resolution would be required for each request, potentially causing delays and
network congestion.

📌 Purpose: ✅ Enhances speed – Faster domain resolution ✅ Reduces network


load – Fewer requests to DNS servers ✅ Improves reliability – Ensures service
availability during outages

Where is the Caching Layer Located?

DNS caching can occur at di erent levels in the infrastructure:

1⃣ Client-Side Caching – Stored within web browsers or OS caches, allowing


quick access to frequently visited sites. 2⃣ Local DNS Resolver – Provided by
Internet Service Providers (ISPs), stores responses to minimize redundant queries.
3⃣ Authoritative DNS Server – May cache frequently requested records to reduce
processing load.
ff
fi
fi
📌 Diagram Representation:

Advantages of Using Caching Layers

✅ Faster Response Times – Caching allows instant resolution for frequently


requested domains. ✅ Optimized Network Performance – Reduces DNS tra c by
limiting repeated queries. ✅ Increased Reliability – Prevents downtime issues by
serving cached data during network disruptions.

Disadvantages of Using Caching Layers

❌ Risk of Stale Data – Improper cache management may store outdated records,
causing failed connections. ❌ Increased Memory Usage – Requires additional
system resources to store cached DNS records.

What are DNS Records?

DNS records store and distribute information about domain names and their
associated IP addresses.

Common DNS Record Types:

🔹 A Record – Maps a domain name to an IPv4 address. 🔹 CNAME Record –


Associates an alias with a canonical domain name. 🔹 MX Record – Speci es mail
servers for email delivery. 🔹 NS Record – Identi es authoritative name servers for
a domain. 🔹 TXT Record – Stores text-based metadata for security/
authentication.
fi
fi
ffi
Conclusion

✔ DNS caching is crucial for network e ciency and faster internet access. ✔
Multiple caching layers contribute to performance optimization, redundancy, and
load balancing. ✔ Understanding DNS records enhances security, performance,
and domain resolution e ectiveness.

DNS in Action: Route53

Amazon Route 53 is a highly available and scalable cloud Domain Name System
(DNS) service. It allows customization of DNS routing policies to reduce latency. In
simple terms, cloud computing refers to storing and accessing data and programs
on remote servers hosted on the internet, rather than on a local computer’s hard
drive or server. It is also known as Internet-based computing.

Features of Cloud Computing

Below are the features of cloud computing:

• No Up-Front Investment: Cloud computing eliminates the need for


signi cant initial capital expenditure.
• Lower Operating Costs: It reduces operational costs by o ering pay-as-you-
go pricing models.
• Highly Scalable: Resources can be scaled up or down based on demand.
• Easy Access: Data and applications can be accessed from anywhere with
an internet connection.
• Reducing Business Risks and Maintenance Expenses: Cloud computing
minimizes risks and maintenance costs.
• No Need to Guess the Capacity: Resources can be adjusted according to
actual usage, eliminating the need for capacity planning.
• Flexible: O ers various services that can be adapted to di erent needs.
Amazon Web Services (AWS) is a subsidiary of Amazon.com that provides on-
demand cloud computing platforms to individuals, companies, and governments,
based on a paid subscription model.
Amazon Route 53

Amazon Route 53 is a highly available and scalable cloud DNS web service. It is
designed for developers and corporations to route end users to internet
applications by translating human-readable names like www.geeksforgeeks.org
into numeric IP addresses like 192.0.1.1 that computers use to connect. Amazon
Route 53 cannot be used to connect an on-premises network with the AWS Cloud.
fi
ff
ff
ffi
ff
ff
Functions of Route53

Below are the functions of route53:

• Domain Name Registration: Route 53 helps register domain names for


websites.
• User Connection: Connects users to websites based on domain names.
• Health Monitoring: Automatically routes users to healthy resources if any
failures are detected.
Route 53 is cost-e ective, secure, scalable, exible, highly available, and reliable.

Methodologies Related to Route 53

Below are the methodologies related to Route53:

• Records: Objects in a hosted zone that determine how to route internet


tra c for a domain name.
• Hosted Zone: Created when a domain name is registered, containing
records for routing tra c for the domain and subdomains.
• DNS Query: A request for information sent from a DNS client to a DNS
server.
• Alias Records: Route internet tra c to AWS resources like S3 buckets and
CloudFront, created at the top node of the DNS namespace.
• Name Servers: Translate domain names into IP addresses to route internet
tra c to resources.
• DNS Failover: Routes tra c from unhealthy resources to healthy ones.
Routing Policies

Below are some routing policies:

• Simple Routing Policy: Routes tra c to a single resource, with multiple


values possible in the same record.
ffi
ffi
ff
ffi
ffi
ffi
ffi
fl
• Failover Routing Policy: Routes tra c from unhealthy resources to healthy
ones.
• Geolocation Routing Policy: Routes tra c based on the geographic location
of the user, providing location-speci c content.
• Geoproximity Routing Policy: Routes tra c based on the geographical
location of the user and the type of content, allowing tra c shifting between
locations.
• Latency Routing Policy: Routes requests to the AWS region with the lowest
latency, improving performance for users.
• Multivalue Routing Policy: Returns multiple values in response to DNS
queries, checking resource health before returning values.
• Weighted Routing Policy: Routes tra c to multiple resources based on user-
de ned proportions.
Bene ts of Route53

Below are the bene ts of Route53:

• Highly Reliable: Built on AWS’s reliable infrastructure, ensuring consistent


routing of end users to web applications.
• Scalable: Automatically scales resources to handle large tra c and queries
without user intervention.
• Easy to Use: User-friendly con guration for DNS settings, answering queries
within minutes.
• Health Check: Monitors application health and redirects users to healthy
resources if issues are detected.
• Flexible: Allows for the selection of di erent routing policies as needed.
• Simple: Manages global tra c with various routing types.
• Cost-E ective: Charges are based on the services used.
• Secure: Integration with IAM ensures access is secured by authorizing only
permitted users.
• Mapped with Various AWS Services: Can map domain names to AWS
resources such as EC2 instances and S3 buckets.
fi
fi
ff
fi
ffi
fi
ffi
fi
ffi
ff
ffi
ffi
ffi
ffi
Introduction to Load Balancers

Imagine you're at a crowded restaurant with one cashier and a huge line of hungry
customers. If everyone has to order from that single cashier, it will take forever,
right? But if the restaurant adds multiple cashiers, and a manager e ciently directs
each customer to the next available cashier, the process will be much smoother.
🔹 That manager is like a load balancer! It ensures requests (orders) don’t pile up
on one cashier (server) and instead get evenly distributed, keeping things fast and
e cient.
🚦 What is a Load Balancer?
A load balancer is a tool that manages incoming requests and distributes them
across multiple servers to prevent overload and ensure smooth performance.
ffi
ffi
👉 Key Functions: ✅ Routes requests e ciently (like a tra c cop 🚦 ). ✅
Prevents any single server from getting overwhelmed. ✅ Can be physical
(hardware) or virtual (software).

📍 Where Are Load Balancers Typically Placed?


💡 Think of a busy city with multiple intersections and tra c signals. Load
balancers act like tra c controllers at di erent points:
1⃣ Between users and servers → Ensures users are directed to the fastest
available server.
2⃣ Between servers and application/job servers → Manages internal processing
requests.
3⃣ Between application servers and cache servers → Keeps frequently accessed
data available.
4⃣ Between cache servers and database servers → Ensures databases don’t get
overloaded.
ffi
ff
ffi
ffi
ffi
🚀 Bene ts of Load Balancers (Why Are They Important?)
💡 Load balancers make applications faster, more reliable, and scalable!
✅ High Availability – Prevents downtime by rerouting tra c to working servers.
✅ Faster Responses – Reduces delays by e ciently distributing requests.
✅ Flexible Scaling – Can add/remove servers based on demand.
✅ Prevents Overload – Stops any single server from becoming a bottleneck.
✅ Improves Performance – Ensures applications run smoothly.
✅ Predicts Issues – Smart load balancers detect bottlenecks early.
✅ Security Protection – Helps defend against cyber attacks (e.g., DDoS).
✅ Simpli es Management – Combines multiple servers into one manageable
system.
💡 Final Thought: Load balancers are like tra c controllers ensuring smooth
movement of requests across servers. Without them, applications would struggle
with delays, downtime, and poor performance.

Types of Load Balancers

Load balancing is like e ciently distributing tasks among workers. Here’s how it
can be done:
🚀 Three Ways to Achieve Load Balancing

1⃣ Software Load Balancers in Clients


🖥 Where is the load balancing logic? → Inside the client application (e.g., mobile
app). 📌 How does it work?
The client app gets a list of available servers.
It connects to the rst server in the list.
If the chosen server fails (after a few retries), it moves to the next one. 💡 Pros:
Cheap and easy to implement. 🔻 Cons: Limited exibility—clients decide which
server to use.

2⃣ Software Load Balancers in Services


⚙ Where is the load balancing logic? → In a software service installed on a
machine (Windows/Linux). 📌 How does it work?
Receives incoming requests and routes them based on de ned rules. 💡 Pros: ✅
No need for extra hardware—just install and con gure. ✅ More exible—can
customize routing rules. 🔻 Cons: Needs proper setup and maintenance.
fi
fi
fi
ffi
ffi
ffi
fi
fl
ffi
fi
fl
3⃣ Hardware Load Balancers
🛠 Where is the load balancing logic? → In a physical device placed in the
network. 📌 How does it work?
Directs tra c e ciently across multiple servers.
Performs network address translation (NAT) to manage incoming requests. 💡
Pros: ✅ Can handle huge amounts of tra c. ✅ Performs health checks to ensure
servers are working. 🔻 Cons: Expensive to purchase and con gure. 🔻 Used
mainly as an entry point in large networks, while software load balancers handle
internal tra c.

🔄 Di erent Categories of Load Balancers

1⃣ Layer 4 (L4) Load Balancer – Network Level Load Balancing


🌐 Where is it used? OSI Model’s Transport Layer (TCP/UDP). 📌 How does it
work?
Routes tra c based on source/destination IP and port numbers.
Performs NAT, but doesn’t look into the actual content of packets. 💡 Best for:
Maximizing network e ciency by distributing tra c across IP addresses.

2⃣ Layer 7 (L7) Load Balancer – Application Level Load Balancing


📌 Where is it used? OSI Model’s Application Layer (HTTP/HTTPS). 🔍 How does
it work?
ff
ffi
ffi
ffi
ffi
ffi
ffi
ffi
fi
Makes routing decisions based on actual content (headers, cookies, URLs). 💡
Best for: Advanced tra c control (e.g., directing users to speci c servers based on
the requested page).

3⃣ Global Server Load Balancing (GSLB) – Across Data Centers


🌎 Where is it used? In cloud-based or multi-location networks. 📌 How does it
work?
Routes requests across multiple data centers to reduce latency. 💡 Best for: ✅
Ensuring fast access for users in di erent locations. ✅ Balancing tra c between
geographically dispersed servers. ✅ Delivering applications with higher reliability
and consistency.

💡 Final Thought: Load balancers come in di erent forms—software-based (on


clients or services) and hardware-based. They operate at di erent levels (L4, L7,
GSLB) to ensure smooth and e cient tra c ow across networks.

💡 Why Scale a Load Balancer?


📌 If tra c increases beyond what a single server can handle, scaling is required
to keep applications fast, available, and reliable. 📌 Without scaling, the system
may slow down or even crash during high tra c spikes.

✅ Scaling a load balancer helps with:


Improved availability – More servers mean less downtime.
Better performance – Faster response times.
Handling tra c spikes – Prevents overload during peak demand.
Reducing downtime – Keeps the system operational even if some servers fail.

🛠 Ways to Scale a Load Balancer


1⃣ Vertical Scaling (Scaling Up)
How? Add more power (CPU, memory, bandwidth) to a single load balancer
instance.
Pros: Simple, no extra con guration needed.
Cons: Has limits—you can only upgrade a machine so much before it reaches
capacity.
2⃣ Horizontal Scaling (Scaling Out)
How? Add more instances of load balancers to distribute tra c across multiple
servers.
Pros: Greater redundancy, can handle huge tra c loads.
Cons: Requires extra setup for managing multiple instances properly.
3⃣ DNS Round Robin
How? Assign multiple IP addresses for a domain.
ffi
ffi
ffi
fi
ffi
ff
ffi
fl
ffi
ff
ffi
ff
ffi
fi
ffi
Each IP corresponds to a load balancer cluster that directs tra c e ciently.
Pros: Easy to scale horizontally by adding more servers.
Cons: Doesn’t guarantee load balancing e ciency in real-time.

🔄 Responsibilities of a Load Balancer


✅ Routing requests – Ensures e cient request distribution. ✅ Keeping metadata
– Stores information about requests and servers. ✅ Tracking server health –
Checks if servers are working properly.

💡 Managing metadata can get complex, which is why another system is


introduced: Registry Service.
🔍 Responsibilities of Registry Service
✅ Captures metadata – Stores information about services. ✅ Performs health
checks – Monitors server conditions. ✅ Manages healing of servers – Redirects
tra c when servers fail. ✅ Supports autoscaling – Expands resources
automatically when needed.
💡 Example: Zookeeper – A popular registry service used for managing distributed
applications.

💡 Final Thought: Scaling a load balancer ensures applications remain fast,


responsive, and available even as tra c grows. Whether through vertical scaling,
horizontal scaling, or DNS Round Robin, the goal is to keep the system balanced
and e cient.
ffi
ffi
ffi
ffi
ffi
ffi
ffi
Load Balancing Algorithms

Load balancers optimize tra c distribution, ensuring that websites and


applications run smoothly. Di erent algorithms serve di erent purposes,
depending on the load and type of requests.

🚦 1⃣ Round Robin – Basic Rotation Method


📌 How it works:
Requests are sent to servers one after another, in a circular order.
Works well for equal-capacity servers handling similar workloads.
💡 Real-World Example: 🔹 Customer Support Call Centers – Each incoming call
is assigned to the next available agent in sequence.

⚖ 2⃣ Weighted Round Robin – Smarter Distribution


📌 How it works:
Servers are assigned weights based on their processing power.
Higher-capacity servers get more requests.
💡 Real-World Example: 🔹 Cloud Hosting Services (AWS, Google Cloud, Azure) –
Cloud providers allocate tra c to servers based on their capacity to process
workloads e ciently.

🔗 3⃣ Hashing Algorithms – Consistent Routing


📌 How it works:
Uses a hashing function to direct requests to a speci c server.
Ensures session persistence, meaning users always land on the same server.
💡 Real-World Example: 🔹 Online Banking Systems – A user logging into an
online bank consistently gets redirected to the same server for security and
seamless experience. 🔹 E-commerce Websites (Amazon, Flipkart) – Ensures that
customers browsing a website remain connected to the same backend server for
faster shopping.

📉 4⃣ Least Bandwidth – Tra c-Based Balancing


📌 How it works:
Routes requests to the server with the lowest current load (bandwidth usage).
Helps avoid overloading servers with high tra c demand.
💡 Real-World Example: 🔹 Video Streaming Platforms (YouTube, Net ix, Disney+)
– These platforms direct users to the least busy streaming server to avoid bu ering
issues.

🛠 Choosing the Right Algorithm (Industry Use Cases!)


✅ Round Robin & Weighted Round Robin → Used in customer support, cloud
hosting, general server management. ✅ Hashing Algorithm → Used in banking,
ffi
ffi
ffi
ff
ffi
ffi
fi
ff
fl
ff
secure login systems, e-commerce websites. ✅ Least Bandwidth → Used in
video streaming, gaming platforms, live events.

💡 Final Thought: Load balancing keeps networks e cient, scalable, and high-
performing. Companies like Net ix, Amazon, and Google rely on smart balancing
techniques to provide seamless experiences to millions of users worldwide.

Types of Scaling

Scaling helps systems adapt to increasing workloads by adding resources. It


ensures applications remain fast, responsive, and reliable.
🚀 What is Scaling?
📌 Scaling is expanding computing capacity to meet demand. ✅ Helps handle
tra c spikes. ✅ Improves system performance & e ciency. ✅ Ensures
uninterrupted availability.

🔄 Types of Scaling

1⃣ Horizontal Scaling (Scaling Out) – Adding More Machines


📌 How it works?
Increases system capacity by adding more servers to the network.
Distributes workload across multiple machines.
💡 Real-World Example: 🔹 Google Search Engine – Uses thousands of
interconnected servers worldwide to handle massive search tra c.
✅ Pros: ✔ Easy to upgrade. ✔ Lower cost—can use multiple cheap machines
instead of one expensive one. ✔ O ers exibility & scalability (unlimited server
addition). ✔ Database upgrades are simple—just add another node.
🔻 Cons:
Requires more coordination between servers.
Can increase complexity in managing data consistency.

2⃣ Vertical Scaling (Scaling Up) – Upgrading One Machine


📌 How it works?
Keeps the same infrastructure but adds computing power (CPU, RAM, storage).
A single, more powerful machine takes on more workload.
💡 Real-World Example: 🔹 Traditional Database Servers – Banks upgrade server
CPUs and RAM to handle more transactions without changing infrastructure.
✅ Pros: ✔ Lower cost for space, cooling, and power. ✔ Easy to use and manage.
✔ No need to recon gure code—it runs the same way but faster.
🔻 Cons:
ffi
fi
fl
ff
fl
ffi
ffi
ffi
Limited scalability—there’s a max upgrade capacity.
If the main machine fails, the system can crash.

💡 Final Thought: Horizontal scaling is better for large-scale, cloud-based


applications, while vertical scaling is ideal for high-performance databases and
simpler setups. Companies often combine both to optimize their systems.

Databases

A database is an organized collection of data that allows easy storage, retrieval,


and management. Instead of manually handling data, databases provide an
e cient structure to automate these processes.

🚀 Why Are Databases Important?


✅ Stores data in a structured manner for quick access. ✅ Prevents redundant
tasks by providing abstraction layers (handling storage, retrieval, and query
processing). ✅ Enables multiple users to access and manipulate data securely.
ffi
🔄 Advantages of Using a Database
1⃣ Reducing Data Redundancy
Problem: Duplicate data causes inconsistencies and errors.
Solution: Databases use foreign keys to remove unnecessary duplication.
Real-World Example: Bank databases ensure customer records are stored once,
avoiding repetition.
2⃣ Improving Data Security
Problem: Organizations face cybersecurity threats.
Solution: Databases restrict unauthorized access and encrypt sensitive data.
Real-World Example: Online banking ensures only authenticated users can access
nancial records.
3⃣ Ensuring Data Backup
Problem: Hardware/software failures lead to data loss.
Solution: Databases use replication techniques to store real-time copies.
Real-World Example: Cloud databases (Google Drive, AWS) create backups
automatically.
4⃣ Enabling Data Sharing
Bene t: Di erent teams can collaborate while accessing the same database.
Example: Multiple employees in an e-commerce company manage product listings
simultaneously.
5⃣ Maintaining Data Consistency
Bene t: A single database ensures updated values are re ected everywhere.
Example: Flight booking systems update availability instantly across all platforms.

🔻 Disadvantages of Databases
❌ Compromised Data Security Risks – Centralized databases may be vulnerable
to cyberattacks. ❌ Complex Setup & Maintenance – Requires trained personnel
(DB administrators). ❌ High Cost of Data Conversion – Migrating data from old
systems can be expensive.

🛠 Types of Databases
1⃣ Relational Databases (RDBMS) – Structured & Organized
Stores data in tables (rows & columns).
Uses primary keys & foreign keys for relationships.
Example: MySQL, PostgreSQL, Microsoft SQL Server.
2⃣ Non-Relational Databases (NoSQL) – Flexible & Scalable
Doesn’t use tables—stores data in documents, key-value pairs, graphs, etc.
Designed for cloud-based applications.
Example: MongoDB (document-based), Redis (key-value store), Neo4j (graph-
based).
fi
fi
fi
ff
fl
💡 Final Thought: Databases power modern applications by managing structured
and unstructured data e ciently. Businesses, banks, e-commerce, and social
media platforms rely on databases to function smoothly.

Relational Database
A relational database stores data in tables where records (rows) are connected
through unique keys. It follows the relational model, making data organization
intuitive and structured.

🛠 Key Features of a Relational Database


✅ Stores data in rows & columns (structured format). ✅ Uses unique keys to
establish relationships. ✅ Supports SQL (Structured Query Language) for data
management. ✅ ACID Compliance ensures reliable transactions.

🔄 ACID Properties (Guaranteeing Data Integrity)


🔹 Atomicity – A transaction is all or nothing (no partial updates). 🔹 Consistency
– Ensures database rules & constraints aren't violated. 🔹 Isolation – Transactions
run independently, preventing interference. 🔹 Durability – Data remains safe &
unchanged after a transaction completes.

💡 Real-World Example: Bank Transactions – When transferring money, the


database ensures funds are deducted from one account only if they are
successfully added to the recipient’s account.
ffi
📊 Example of a Relational Database Table (Banking System)

✔Each user has one or more accounts (one-to-many relationship). ✔ Some


databases store data row-wise (R1, R2, R3, etc.).

💾 Common Relational Databases


SQL Server
Oracle
MySQL
PostgreSQL
Aurora

📂 Common Datatypes in Relational Databases


Storing Time
✅ Datetime – Stores date & time (YYYY-MM-DD hh:mm:ss). ✅ Number – Stores
numeric values.
Storing Long Text
✅ Varchar – Stores xed-length text (letters, numbers, special characters). ✅
Text – Stores large text values (up to 65,535 characters). ✅ Blob – Stores binary
large objects (images, audio, video). ✅ JSON – Stores structured JSON data with
indexing.

💡 Final Thought: Relational databases power many critical applications like


banking, e-commerce, and enterprise systems. Their structured format, ACID
compliance, and SQL-based queries make them ideal for handling complex,
reliable transactions.
fi
Non-Relational Databases

🚀 What is a Non-Relational Database?


A non-relational database (NoSQL) stores data without structured relationships,
making it more exible. Unlike traditional relational databases that use tables, non-
relational databases organize data di erently, using documents, key-value pairs,
graphs, and more.

✅ More scalable – Easily handles growing data volumes. ✅ Supports


unstructured & semi-structured data – Great for diverse datasets. ✅ Eventually
consistent – Provides high availability.
💡 Real-World Example: Social media platforms like Instagram, Facebook, and
Twitter use NoSQL databases to store user posts, comments, and interactions
without complex table relationships.

🛠 Types of Non-Relational Databases

1⃣ Key-Value Database – Fast & Simple


📌 How it works? Stores data as key-value pairs, similar to a dictionary. 💡 Use
Cases: ✅ Stores session details for users. ✅ Tracks user preferences in apps.
🔹 Examples: Memcache, DynamoDB.

2⃣ Document Database – Flexible & Scalable


📌 How it works? Stores data in JSON, BSON, or XML format. 💡 Use Cases: ✅
Used in e-commerce platforms for product catalogs. ✅ Stores user-generated
content (blogs, reviews). 🔹 Examples: MongoDB, DocumentDB.

3⃣ Text-Search Database – Smart Searching


📌 How it works? Optimized for full-text search queries (complex searches with
Boolean operators). 💡 Use Cases: ✅ Log analysis in cybersecurity. ✅ Search
engines (Google, Bing). 🔹 Examples: Elastic Search, Apache Solr.

4⃣ Graph Database – Relationship-Focused


📌 How it works? Stores data in nodes and relationships (instead of tables). 💡
Use Cases: ✅ Social media recommendations (friend connections). ✅ Fraud
detection (linking suspicious transactions). 🔹 Examples: Neo4J, Neptune.

5⃣ Time-Series Database – Organized by Time


fl
ff
📌 How it works? Stores time-stamped data (pairs of time and values). 💡 Use
Cases: ✅ IoT-based applications (sensor data tracking). ✅ Financial stock
market analysis. 🔹 Examples: In uxDB, KDB+.

6⃣ Immutable Ledger Database – Secure & Unchangeable


📌 How it works? Stores records that cannot be altered (uses hashing). 💡 Use
Cases: ✅ Financial transactions (secure banking ledgers). ✅ Blockchain & land
records. 🔹 Examples: Hyperledger Fabric, QLDB.

7⃣ Columnar Database – Optimized for Analytics


📌 How it works? Stores data in columns instead of rows for fast queries. 💡 Use
Cases: ✅ Big data processing (business intelligence, analytics). ✅ High-speed
reporting systems. 🔹 Examples: RedShift, Snow ake.

🔄 When to Use a Relational Database?


✔ If data structure is predictable ( xed format). ✔ If relationships between entities
matter (e.g., banking, e-commerce). ✔ If the project requires complex queries (SQL
syntax). ✔ If historical support, security, and consistency are essential.
💡 Example: ERP Systems, Inventory Management – Need strict data relationships
for accurate reporting.
fl
fi
fl
📌 When to Use a Non-Relational Database?
✔ If data structure needs exibility (frequent modi cations). ✔ If horizontal
scalability is required (cloud environments). ✔ If handling high tra c & real-time
data (social media, big data analytics). ✔ If Sharding or Data Denormalization is
needed for faster performance.
💡 Example: IoT Applications, Streaming Services (Net ix, YouTube) – Handle
massive data ow with rapid storage & retrieval.

🛠 Key Di erences Between Relational & Non-Relational Databases

💡 Final Thought: ✔ Relational databases excel in structured, consistent data with


strict relationships. ✔ Non-relational databases shine in exibility, scalability, and
handling diverse data types.

Introduction to Database Replication

Database replication ensures that copies of data exist across multiple systems,
improving availability, performance, and fault tolerance.
🚀 What is Replication?
📌 Replication = Copying & Maintaining Data Across Multiple Systems ✅ Ensures
high availability of data. ✅ Helps systems recover after failures or network
outages. ✅ Supports distributed computing for improved e ciency.
ff
fl
fl
fi
fl
fl
ffi
ffi
💡 Example: 🔹 Cloud Storage (Google Drive, Dropbox, AWS S3) – Files are
replicated across multiple locations, so if one fails, users can still access their data.

📌 Why Do We Need Replication?


✔ High Availability – Data remains accessible even if a system fails. ✔ Scalability –
Expands storage & computing capacity across multiple servers. ✔ Load Balancing
– Distributes requests e ciently, reducing congestion.
💡 Example: 🔹 Online Shopping Websites (Amazon, Flipkart) – Replication
ensures product databases stay available even during heavy tra c (e.g., Black
Friday sales).

🛠 Advantages of Using Replication


✅ Improved Performance – Faster data access. ✅ Increased Availability –
Prevents downtime. ✅ Reduced Network Tra c – Local access prevents
overload.
💡 Example: 🔹 Stock Market Systems – Live data updates replicated across
multiple servers, ensuring real-time tracking.

🔻 Challenges of Using Replication


❌ Data Consistency Issues – Con icts may arise if multiple servers update data
simultaneously. ❌ Replication Lag – Updates may take time to reach all replicas.
❌ Complexity – Managing multiple replicas requires extra con guration.
💡 Example: 🔹 Social Media Platforms (Facebook, Instagram) – Replication lag
might cause delays in updating likes/comments across multiple regions.

🔄 Modes of Replication

1⃣ Synchronous Replication – Instant & Consistent


📌 How it works?
All copies are updated instantly, ensuring perfect consistency.
More reliable, but slower due to real-time updates.
💡 Use Case: 🔹 Banking Systems (HDFC, ICICI, SBI) – Transactions require
instant replication to maintain accurate balances.

2⃣ Asynchronous Replication – Faster but Flexible


📌 How it works?
Updates are delayed – replication happens over time.
Faster performance, but there’s a risk of temporary inconsistencies.
💡 Use Case: 🔹 Video Streaming Platforms (Net ix, YouTube) – Videos are
replicated gradually across multiple servers for better scalability.
ffi
fl
ffi
fl
fi
ffi
💡 Final Thought: Replication is key for ensuring reliable, scalable, and high-
performing databases. It is widely used in banking, social media, cloud computing,
and e-commerce to avoid data loss and system downtime.

Types of Database Replication

Database replication ensures multiple copies of data exist across di erent


systems, improving availability, fault tolerance, and performance.

🚀 What is Database Replication?


📌 Replication = Copying & Maintaining Data Across Multiple Nodes ✅ Helps
prevent data loss in case of failures. ✅ Supports distributed computing for better
performance. ✅ Ensures data redundancy across multiple locations.
💡 Example: 🔹 Cloud-Based Services (Google Drive, AWS S3, Microsoft Azure) –
Files are replicated across di erent regions to ensure accessibility during outages.

🔄 Leader-Follower-Based Replication
🔹 How it works? ✔ One leader node handles all write operations. ✔ Follower
nodes replicate the leader’s data and handle read queries. ✔ Ensures data
consistency across all nodes.
💡 Example: 🔹 Banking Systems (ICICI, HDFC, SBI) – The main database (leader)
processes transactions while backup replicas (followers) maintain accurate
nancial data.

🛠 Replication Log – Tracking Changes E ciently


📌 Log-based replication uses database log les to record updates and replicate
changes. ✅ E cient for stable database structures that don’t change frequently.
✅ Keeps detailed records of transactions.
💡 Example: 🔹 Stock Market Databases – Track real-time trades & price changes
using log-based replication.

📌 Handling Node Outages in Replication


✔ Automatic Failover – If the primary node fails, a standby node takes over without
manual intervention. ✔ Manual Failover – An administrator manually promotes a
standby node if auto-failover isn’t feasible.
💡 Example: 🔹 Online Banking Systems – If a primary transaction server fails, a
backup server ensures seamless banking operations.
fi
ffi
ff
ffi
fi
ff
🛠 Di erent Data Replication Schemes

1⃣ Full Database Replication – Complete Copy on All Servers


📌 How it works? Entire database is replicated across multiple locations for
maximum redundancy. 💡 Example: 🔹 E-commerce Platforms (Amazon, Flipkart)
– Servers in Europe, Asia, and America store full copies for global availability.

2⃣ Partial Replication – Only Relevant Data Is Replicated


📌 How it works? Some parts of the database are distributed based on location
relevance. 💡 Example: 🔹 Insurance & Financial Systems – Indian customer data
stays in India, European customer data stays in Europe, but headquarters
maintains a master record.
ff
🛠 Primary Selection in Replication
✔ Consensus Protocols Decide the Primary Node ✔ Common algorithms: ✅ Raft
Consensus Algorithm ✅ Paxos Algorithm ✅ Zab Protocol
💡 Example: 🔹 Distributed Banking Systems – Ensures one primary node
handles writing while replicas sync data accurately.

💡 Final Thought: Replication is crucial for ensuring high availability, fault


tolerance, and performance in large-scale systems like banking, e-commerce,
social media, and cloud computing.

Multi-Leader Replication Topology

Multi-leader replication ensures that multiple data centers have their own leader,
and these leaders replicate changes asynchronously to other leaders. This setup
helps with higher availability, fault tolerance, and better data consistency across
distributed systems.
🔹 Con ict Resolution in Multi-Leader Replication

✔Since multiple leaders can handle write operations, con icts can arise when the
same record is updated in di erent data centers.
✔Some common con ict resolution strategies include:
✅ Last Write Wins (LWW) – The most recent update is accepted.
✅ Versioning – Tracks multiple versions of data and resolves con icts manually
or automatically.
✅ Operational Transformation (OT) – Applies changes in a way that maintains
consistency.
💡 Example: 🔹 Google Docs – Multi-user editing relies on operational
transformation to resolve simultaneous changes in di erent locations.

🔹 Network Latency Considerations


✔Asynchronous replication can cause temporary inconsistencies due to network
delays between data centers.
✔Strategies to minimize latency:
✅ Compression – Reducing data size before transmission.
✅ Proximity-based replication – Prioritizing replication to geographically closer
data centers rst.
💡 Example: 🔹 Global payment systems (Visa, Mastercard) – Transactions need
real-time synchronization to avoid latency-related issues.

🔹 Use Cases for Multi-Leader Replication


✔Financial Trading Systems – Ensures traders in di erent time zones get
synchronized stock market data.
✔Gaming Platforms – Multiplayer online games replicate user actions across
servers to avoid lag.
✔Healthcare Records – Synchronizes patient records across hospitals and regions
to ensure accurate medical history tracking.

🚀 What is Cassandra?
✔ A highly scalable NoSQL database developed by Apache. ✔ Uses peer-to-peer
distributed architecture (no single point of failure). ✔ All nodes work independently
yet remain interconnected.
💡 Example: Net ix, Facebook, and Twitter use Cassandra for managing large-
scale real-time data.

🔄 Data Replication in Cassandra


✔ Certain nodes act as replicas to provide data backup. ✔ If stale data is
detected, Cassandra returns the most recent value. ✔ Read Repair updates
outdated data in the background.
fl
fi
fl
fl
ff
ff
ff
fl
fl
💡 Example: 🔹 Global E-Commerce Platforms (Amazon, Flipkart) – Ensure
inventory consistency across di erent regions.

🛠 Accessing Cassandra (CQL & Keyspace)


✔Users interact with Cassandra using CQL (Cassandra Query Language). ✔
Keyspace is the top-level container that holds tables & replication settings. ✔
Replication Factor – Determines how many copies exist across machines.

📌 Sharding – Dividing Data for Better Management


✔ Sharding means splitting large data sets into smaller partitions to reduce
database load. ✔ It is complex and should be used only when necessary.
💡 Alternatives Before Sharding: ✅ Purging – Removing outdated or unnecessary
data. ✅ Data Archiving/Tiering – Storing infrequently used data separately. ✅
Logical Data Separation – Organizing data into di erent databases e ciently.

🔄 Di erent Types of Multi-Leader Replication Topologies

1⃣ All-to-All Topology
✔ Every leader replicates data to all other leaders in the system. ✔ Ensures full
redundancy, but can be complex to manage. ✔ Best for high availability across
distributed systems.
ff
ff
ff
ffi
💡 Example: Global nancial systems ensure all branches sync their transaction
records.

2⃣ Circular Topology
✔ Each leader replicates data to the next leader in a circular fashion. ✔ Less data
movement, but failure in one node can disrupt the chain. ✔ Used for moderate
replication needs.
💡 Example: Multi-region warehouse logistics, where data ows sequentially from
one center to another.
fi
fl
3⃣ Star Topology
✔ One central leader replicates data to multiple secondary leaders. ✔ Reduces
replication complexity but introduces a single point of failure at the central leader.
💡 Example: Content Delivery Networks (CDNs) use a main hub to push content
across multiple global locations.

💡 Final Thought: Multi-leader replication is widely used in distributed cloud


systems, ensuring high availability, fast access, and fault tolerance in applications
like banking, e-commerce, and social media platforms.

Leaderless Replication Topology

Leaderless replication is a fault-tolerant, highly scalable replication method where


all nodes are equal and can simultaneously receive write operations. This
eliminates the need for a designated leader, ensuring high availability and low
latency in distributed databases.

🚀 How Does Leaderless Replication Work?

✔Write Operations: Updates happen on multiple nodes at the same time, using a
consensus algorithm (Paxos or Raft).
✔Con ict Resolution: If multiple write operations occur on di erent nodes,
consensus algorithms resolve con icts to ensure consistency.
✔ Data Consistency: If a node fails, the rest continue operations without
disruption, maintaining availability and reliability.
fl
fl
ff
💡 Example: 🔹 Cloud-based databases (Cassandra, Riak) – Ensure high
availability across multiple data centers, even during failures.

🔄 Read & Write Quorums


✔ In leaderless replication, read and write operations use quorums to ensure
consistency. ✔ A quorum requires a majority of nodes to agree before con rming
the operation. ✔ Helps in balancing availability vs. consistency in distributed
databases.
💡 Example: 🔹 Amazon DynamoDB uses Quorum-based writes to ensure strong
consistency across multiple nodes.

📌 Anti-Entropy Mechanisms
✔ Since leaderless replication relies on eventual consistency, databases use anti-
entropy techniques to x inconsistencies over time. ✔ Merkle Trees are often used
to detect and resolve di erences across nodes.
💡 Example: 🔹 Cassandra & Riak use Merkle Trees to identify out-of-sync data
and repair inconsistencies.

🛠 Trade-O s of Leaderless Replication


✅ High Availability: No single point of failure. ✅ Scalability: Can handle large
distributed environments. ❌ Eventual Consistency: Some temporary
inconsistencies may occur. ❌ Complex Con ict Resolution: Requires extra
mechanisms to synchronize con icting writes.

🛠 Advantages of Leaderless Replication


✅ Scalability: Multiple nodes handle writes, making it ideal for large datasets.
✅ Fault Tolerance: No single point of failure—if a node crashes, others handle
requests.
✅ Low Latency: Distributed architecture ensures fast data retrieval.

💡 Use Case:
✔Social Media Platforms (Facebook, Instagram) – Handle huge volumes of user-
generated content.
✔E-Commerce Websites (Amazon, Flipkart) – Ensure real-time inventory updates
across multiple locations.

📌 Real-Life Examples of Leaderless Replication


1⃣ Cassandra (Apache NoSQL Database)
📌 How it works?
Handles massive amounts of structured data across multiple cloud regions.
Uses the Gossip Protocol to sync updates across nodes.
ff
fi
ff
fl
fl
fi
💡 Example: 🔹 Net ix & Facebook use Cassandra for high-speed data replication
across global servers.
2⃣ Riak (NoSQL Database)
📌 How it works?
Designed for large volumes of unstructured data.
Uses Vector Clocks for data consistency across multiple servers.
💡 Example: 🔹 IoT & real-time analytics platforms use Riak for quick data access
& fault tolerance.

💡 Conclusion
Leaderless replication powers modern distributed databases, ensuring high
availability, scalability, and fault tolerance. It is widely used in social media, cloud
services, nancial systems, and big data applications.

📚 Introduction to Indexing in Databases


Indexing is a data structure technique used in databases to improve query performance
and minimize disk I/O. It allows faster data retrieval without scanning every row of a table.

🔍 What is an Index?
An index maps one or more column values (search keys) to their corresponding row
locations via pointers.

• Search Key: Field(s) used to sort or lter queries.

• Data Reference: Points to the exact data location in storage.

• Note: The actual table data may or may not be physically sorted.

🧠 Indexing Attributes

Attribute Description
Access Types Value-based search, range queries
Access Time Time to retrieve a specific data element
Insertion Time Time to insert and update the index
fi
fl
fi
Time to delete a record and update the
Deletion Time
index
Space
Extra storage space used by the index
Overhead

🗂 File Organization Mechanisms in Indexing

1⃣ Sequential File Organization

🔸 Dense Index

• One index record for every search key value.

• Contains the key and a pointer to the data.

🔸 Sparse Index

• Index for only selected key values.

• Points to blocks, not speci c rows.

• Lookup may need a sequential scan within a block.

• Lookup complexity: log₂(n) + 1

2⃣ Hash File Organization

• Distributes values across buckets using a hash function.

• Supports constant-time lookups but not range queries.

🧱 Types of Indexing

1. Clustered Indexing

• Physically orders the data le by the index key.

• Supports faster joins and grouped lookups.

• De ned on a non-key or composite column sometimes.

• Example: Grouping students by semester.


fi
fi
fi
🔹 Primary Indexing (Special Case)

• Clustered index on a primary key (unique & sorted).

• Creates an ef cient sequential le.

2. Non-Clustered (Secondary) Indexing

• Maintains pointers to actual data, not sorted.

• Data is accessed indirectly via leaf-level references.

• Similar to a book’s table of contents.

• Always uses dense format.

3. Multilevel Indexing

• Reduces index size by hierarchically organizing indexes.

• Inner blocks point to lower-level blocks down to data blocks.

• Enhances performance for large datasets.

🧮 Common Index Data Structures

📐 B-Tree

• Balanced tree with all leaf nodes at the same level.

• Nodes contain keys and pointers.

• Great for minimizing disk reads.

📏 B+ Tree

• All data is in leaf nodes, internal nodes only help traverse.

• Ef cient range queries and better fan-out.

• Often used in modern DBMS.

📚 Explain Plan (Query Optimization)


• The Query Optimizer uses indexes to nd the most ef cient execution plan.
fi
fi
fi
fi
fi
• Techniques like index-only scans, covering indexes, and partial indexes are used
to improve query speed.

🧰 Indexing Strategies & Features

Feature/Strategy Description
Covering Index Index that contains all columns used in a query
Index-Only Scans Query served entirely from index, no table scan needed
Partial Index Indexes only a filtered subset of records
Index Occurs due to non-contiguous blocks; solved via
Fragmentation rebuilds
Index Maintenance Includes creation, deletion, reorganization

✅ Advantages of Indexing
• Speeds up queries and lookups.

• Reduces disk I/O.

• Enables ef cient sorting and ltering.

• Supports data integrity (e.g., unique constraints).

• Improves concurrency by avoiding full-table locks.

⚠ Disadvantages of Indexing
• Increased storage due to extra index les.

• Slower write operations (INSERT/UPDATE/DELETE).

• Maintenance overhead (especially with frequent changes).

🛒 Real-Life Use Case: E-Commerce


• Indexes on columns like brand, price, category allow users to lter millions of
products in milliseconds.
fi
fi
fi
fi
• Without indexes, a product search would require scanning every row—massively
inef cient.

🧾 Introduction to Queueing Systems


A queueing system models how entities (like tasks, requests, or customers) arrive at a
service point, wait if necessary, and get processed by one or more servers.

Used in:

• Cloud-based messaging systems

• Hospital patient management

• Network packet routing

• Customer service call centers

⚙ Core Components of a Queueing System


Componen
Role
t
Producer Generates tasks or messages (e.g., placing an order)
Consumer Processes those tasks (e.g., order fulfillment)
Acts as the middleware, queueing and routing messages (e.g., RabbitMQ,
Broker
Kafka)

📌 Think about it: If your app crashes when too many users log in, is it an issue with the
producer, the consumer, or the broker?

🧪 Types of Queueing Architectures


fi
Model Description
Single Server, Single
One worker, one task line. e.g., Small clinic check-in counter.
Queue
Multiple Servers, Single
Multiple workers pull from one line. e.g., Airport check-in.
Queue
Single Server, Multiple One worker checks different queues. e.g., Bank clerk handling
Queues deposits, loans, queries.
Multiple Servers, Multiple workers with individual queues. e.g., Fast food
Multiple Queues counters.

🚨 Issues in Queueing Systems

1⃣ Producers Outpace Consumers

If producers generate tasks faster than consumers can process them:

• 🎯 Drop Some Messages (if non-critical)

• 📦 Add a Larger Buffer (but resource-intensive)

• 👥 Increase Number of Consumers

• 🔄 Apply Back Pressure (slow producer via broker feedback)

🧠 Re ection: What happens in a food delivery app if customers place 1000 orders but only
100 delivery partners are active?

2⃣ Broker (Node) Crashes

The broker may crash due to overload or infrastructure failure. To avoid this:

• 📌 Durability Guarantee: Write messages to disk

• 🧬 High Availability: Use broker clusters with auto-failover

3⃣ Head-of-Line Blocking

Occurs when a message at the front of the queue repeatedly fails to process. Solutions:

• ✅ Retry Cap with fallback


fl
• ☠ Dead Letter Queue (DLQ) to park problematic messages

🔍 Think on this: If a single invalid transaction blocks all others, what is more important—
data consistency or system responsiveness?

📬 Types of Message Brokers

📨 1. Queue-Based Brokers

• Each message is processed by only one consumer.

• Ef cient for task distribution or load balancing.

• Examples: AWS SQS (Simple Queue Service), Kafka Queues

🧪 Use case: Background job processing (e.g., image resizing, order dispatch)
fi
📢 2. Topic-Based Brokers

• One message is broadcast to all subscribers.

• Enables parallel and decoupled processing.

• Examples: Kafka Topics, AWS SNS

🧪 Use case: Event-based systems where multiple services need to react to the same trigger
(e.g., send email, update dashboard, log analytics)

🧠 Review Questions

1. What are the trade-offs between dropping messages and applying back pressure?

2. How would you decide between using a Queue or a Topic model?

3. What might happen if your dead letter queue lls up?


fi
🧠 Caching in System Design – Interview-Centric Guide

✅ What is Caching?

Caching is a performance optimization technique where frequently accessed data is


temporarily stored in fast-access memory (e.g., RAM) to reduce retrieval time and backend
load.

Analogy:
Think of keeping your favorite tools on your desk instead of fetching them from storage
every time — that’s caching in action.

🎯 Why is Caching Important?

Benefit Description
🚀 Performance Serves data faster, reducing latency

📉 Backend Reduces repeated DB/API hits


Offload
💰 Cost Efficiency Saves compute cycles and bandwidth
Helps handle high request loads
⚖ Scalability efficiently

🔁 How Caching Works (Flow)

1. Client Request

2. Check Cache

3. Cache Hit → return data instantly

4. Cache Miss → fetch from DB/API

5. Update Cache

6. Return to Client

Question: What happens if the cached data is outdated or corrupted?

🧹 Cache Eviction & Expiry


Term Description
Removes old entries when memory is full (e.g., LRU,
Eviction
LFU)
Expiry
Automatically invalidates entries after a set time
(TTL)

Question: What eviction strategy would you use for frequently changing data?

🔧 Types of Caching

1. Client-Side Caching

• Cached on user devices (e.g., browser, mobile)

• Ideal for static assets: HTML, JS, CSS

💡 Example: Browser caching for images, speeding up site reloads

Question: What risks does client-side caching pose for real-time data?

2. Server-Side Caching

• Cache stored on application server or a shared cache layer

• Faster DB queries, computation results, etc.

Subtypes:

a. Application Server Cache (Local In-Memory)

• Fastest access, good for single-server apps

• Problem in distributed setup: Each server holds its own isolated cache → leads to
cache inconsistency

b. Global Cache

• Centralized cache layer shared across all servers

• Ensures consistency, but can become a bottleneck

🔧 Tech: Redis, Memcached

Question: How would you scale a centralized cache without losing availability?
3. Distributed Cache

• Cache is partitioned and spread across nodes

• Uses consistent hashing to route data

• High availability, scalable

🔧 Tech: Redis Cluster, Hazelcast, Couchbase

Question: How does consistent hashing help in scaling distributed caches?

4. CDN (Content Delivery Network)

• Edge servers cache static les close to users geographically

• Reduces latency, accelerates content delivery

🔧 Tech: Cloud are, Akamai, AWS CloudFront

Question: Can CDNs cache dynamic content? If yes, how would you handle invalidation?

🛠 Caching Strategies (Patterns)

Strategy How it Works Trade-off


Cache-Aside App checks cache first; DB fetch on Flexible, but needs manual
(Lazy) miss sync
Cleaner API, but cache-
Read-Through Cache auto-fetches from DB on miss
dependent
Writes go to both DB and cache
Write-Through Consistent, but slower
immediately
Write-Back Fast, but risk of data loss if
Writes go to cache first, async to DB
(Behind) crash

Question: Which write strategy ts real-time analytics vs. nancial transactions?

💡 Real-World Caching Examples

🖥 Hardware Level

• CPU Caches (L1/L2/L3): Speed up processor operations


fl
fi
fi
fi
• GPU Caches: Optimize rendering

🌐 Service Level

• API Caching: Prevent repeated expensive calls

• DNS Caching: Speed up domain resolution

• Database Caching: Cache query results

🌍 Application Level

• Browser Cache: Locally cache site resources

• Scienti c Simulations: Cache intermediate results

Question: How would you cache high-frequency search queries in a search engine?

⚠ Challenges in Caching

Challenge Description
Keeping cache in sync with DB is
❌ Cache Invalidation tricky
Limited space → requires smart
📉 Memory Limits
eviction
🔄 Stale Data TTL tuning is crucial

🔧 Complex Adds system design overhead


Integration

Question: How do you handle eventual consistency in caching systems?

🧠 Interview-Ready Summary
Caching is a key performance enhancement tool in system design. It reduces latency,
handles scale, and conserves backend resources by storing frequently accessed data in faster
mediums like memory or edge servers. From local in-memory caches to globally distributed
CDN systems, the right caching strategy depends on data volatility, consistency needs, and
access patterns. Effective use of eviction policies, TTLs, and strategy selection (like Cache-
Aside or Write-Through) are essential to balance performance with accuracy.
fi
🚀 Cache Invalidation vs. Cache Eviction
in System Design
When designing high-performance systems, caching is critical—but without proper
invalidation and eviction strategies, it can lead to stale data or memory overload.

🔁 What is Cache Invalidation?


De nition: Ensures that cached data is refreshed or removed when the original source
(like a database) is updated.

Why It Matters:
Imagine a product price changes in DB, but the user still sees the old price from cache = ❌
stale data.

✅ When to Use:

• Consistency is critical (e.g., stock prices, order status)

• Data updates frequently

🔧 Cache Invalidation Strategies

Strategy How it Works Use Case


Time-Based
Data auto-expires after a fixed time Blogs, rarely updated info
(TTL)
Write to DB and cache
Write-Through Ensures strong consistency
simultaneously
Lazy Trade-off between speed and
On read: if data is stale, refresh it
Invalidation accuracy

🧠 Interview Tip:

How would you handle invalidation for real-time dashboards or analytics systems?

🧹 What is Cache Eviction?


De nition: The process of removing data from cache to free up space when it's full.
fi
fi
Why It Matters:
Caches are memory-bound. Without eviction, performance degrades or OOM (Out of
Memory) errors occur.

✅ When to Use:

• Memory is limited

• Data is no longer relevant or rarely accessed

🚦 Cache Eviction Policies

Policy How it Works Best Use Case


LRU (Least Recently Removes least recently General-purpose; common default
Used) accessed item
LFU (Least Frequently
Removes least used item Items with long-term popularity
Used)
FIFO (First In, First Removes oldest item
Simple, but may evict useful data
Out) regardless of use
RR (Random Very simple, used in low-
Evicts random item
Replacement) complexity systems

🧠 Interview Tip:

Why might LFU be less effective than LRU for bursty traf c patterns?

🔁 Invalidation vs. Eviction — At a Glance

Aspect Cache Invalidation Cache Eviction


Goal Keep cache in sync with source Free up memory in cache
Data change in source (DB/
Trigger Memory full or policy-driven
API)
Focus Data freshness Space efficiency
Who controls Often cache engine (e.g.,
Often developer/application
it Redis)

📊 Real-World Scenario: E-Commerce Product Info

Feature Approach
fi
Product TTL-based invalidation (content rarely
description changes)
Product price Write-through or lazy invalidation
Product reviews Lazy + TTL
Memory handling LRU eviction policy

🧠 Common Interview Questions (with framing hints)


1. Q: How do you keep cache consistent when a user updates their pro le?

◦ A: Write-through or immediate invalidation

2. Q: What happens if cache gets overloaded?

◦ A: Eviction kicks in, e.g., LRU to free memory

3. Q: How do you decide the TTL for a cache entry?

◦ A: Based on update frequency and tolerance for staleness

4. Q: Can invalidation and eviction happen together?

◦ A: Yes. A cache may evict due to memory pressure even if TTL hasn't
expired

🌍 Content Delivery Network (CDN)


🚀 What is a CDN?
A Content Delivery Network (CDN) is a geographically distributed network of proxy
servers and data centers. Its goal is to deliver web content to users quickly and reliably,
regardless of location.

CDNs act as an intermediary layer between the origin server and the client, reducing
latency, improving speed, and lowering bandwidth consumption.
fi
📦 Core Components of a CDN

Component Role
Edge Servers Caches content close to users for fast access
POP (Point of
Location that hosts multiple edge servers
Presence)
Origin Server Stores original version of the content
Handles routing, caching, and cache eviction/invalidation
CDN Software
strategies
Backbone Network High-speed links between POPs and the origin server

🖼 CDN Architecture (Imagine this Diagram):

🔁 How a CDN Works – Step by Step


1. User Request → e.g., image, video, webpage.

2. DNS Redirection → DNS routes request to closest POP (based on proximity and
load).

3. Cache Check:

◦ ✅ Cache Hit → Content served instantly from edge.

◦ ❌ Cache Miss → Fetched from origin and stored in edge cache.

4. Content Delivery → Quick, reliable delivery.

5. Content Cached → For future requests from nearby users.


🧠 Types of CDN

Type Description Use Case


Precise control; e.g., scheduled
Push CDN Manual upload of content to edge nodes
releases
Pull CDN CDN fetches content on-demand from Simpler to use; dynamic content
origin
Private
Owned and managed by the business itself Netflix Open Connect
CDN
Public Provided by third parties (e.g., Cloudflare,
SaaS, media, ecommerce
CDN Akamai)

⚙ CDN Caching Techniques


• Time-to-Live (TTL) for cache entries

• Cache Invalidation (manual or automated purge)

• Versioning URLs (file_v1.js → file_v2.js to avoid stale cache)

• ETags and Last-Modi ed headers

🛡 Bene ts of Using a CDN

Problem Solved How CDN Helps


High Latency Edge delivery reduces round-trip time
Traffic Spikes Load distributed among edge servers
Bandwidth
Reduces traffic to origin servers
Costs
CDNs provide built-in protection and
DDoS Attacks
firewalls

🌐 Real-World Use Case: Net ix Open Connect


• Challenge: Deliver high-de nition video globally with low latency.

• Solution: Net ix built Open Connect, a private CDN.


fi
fl
fi
fi
fl
• They also leverage public CDNs where necessary.

• Bene t: Lower cost per GB and improved control.

📉 Pros and Cons of CDN

✅ Pros:

• Faster content delivery

• Lower origin server load

• Scalable under heavy traf c

• Built-in security (DDoS protection, WAF)

❌ Cons:

• Reliance on third-party services

• Compliance/legal issues (data locality)

• Costly for large-scale custom use

🧠 Interview-Ready Questions
1. Q: How does a CDN improve performance?

◦ A: By reducing physical distance and caching data close to users.

2. Q: How do CDNs handle dynamic vs. static content?

◦ A: Static is cached; dynamic can be delivered via edge computing or partial


caching.

3. Q: What if an edge server has stale data?

◦ A: CDN uses cache invalidation via TTL, purge APIs, or cache-busting


URLs.

4. Q: How can you make sure a user always gets fresh content?

◦ A: Set low TTL, use query strings/versioning, or force purge.

5. Q: Why did Net ix build a private CDN?

◦ A: To reduce costs, gain control, and deliver consistent high-quality video.


fi
fl
fi
💡 CDN in System Design Interviews
When designing scalable web systems (e.g., e-commerce, video streaming, news portals),
always mention:

• “We will use a CDN to cache static assets (images, JS, CSS) and possibly pre-
rendered pages to reduce latency and of oad traf c from the backend.”

✅ Conclusion
CDNs are critical infrastructure for global content delivery. Understanding how they
cache, route, and optimize traf c enables you to design high-performance, scalable
systems—exactly what interviewers want to see in a system design round.

🧱 System Design Overview


System design is the blueprint for building scalable, reliable, and ef cient software/
hardware systems. It focuses on de ning a system’s structure, components, and interactions
before coding begins.

✅ Primary Objectives of System Design

• Practicality – Solve real-world problems effectively.

• Accuracy – Align with functional and non-functional requirements.

• Completeness – Cover every essential scenario.

• Ef ciency – Optimize for high throughput and low latency.

• Reliability – Tolerate failures gracefully.

• Optimization – Use minimal space/time without compromising capability.

• Scalability – Handle increasing load without rearchitecture.


fi
fi
fi
fl
fi
fi
🔑 Key Concepts of System Design

Concept Explanation
Requirement
Understand what the system is solving for.
Identification
Choose architecture, interfaces, algorithms, and
Component Planning
storage.
User-Friendliness Build intuitive and accessible systems.
Performance & Resilience Minimize errors/downtime. Maximize speed.
Constraint Awareness Include budget, hardware, and regulatory limitations.
🛒 Example: Small E-Commerce Website
This real-world breakdown demonstrates a practical application of the design framework:

🔹 Functional Requirements

• Browse catalog

• Add to cart and place order

• Track orders

• Handle payments and refunds

🔹 Non-Functional Requirements

• Scalability (high concurrent users)

• Fast response times

• Strong security

🔹 High-Level Architecture

• Frontend: UI via React/Angular

• Backend: Microservices for checkout, ful llment, noti cations

• Database: MySQL/PostgreSQL

• API: REST or GraphQL

• Cloud Deployment: AWS/GCP

📋 System Design Framework in Interviews


System design interviews are open-ended — you're expected to lead the discussion. Here's
the general approach:

🔄 Requirement Gathering

Step Purpose
fi
fi
Clarify the Problem
“Are we designing the complete app or just a component?”
Scope
Confirm Focus Areas “Do you want to emphasize performance, scaling, or security?”
“I’m assuming we need near real-time updates — does that sound
Validate Assumptions
right?”
Avoid Rushed
Think before committing to a design decision.
Responses

🧠 Sample Interaction

I: Let’s design Facebook. Y: Which part — feed, noti cations, chat? I: Let’s focus on the
feed. Y: Should we include images and videos or just text?

📊 Essential Requirement Questions for Any Design


These questions guide the direction of your design:

Area Sample Interview Questions


Scale & Load What are the expected DAUs, QPS, or storage footprint?
Should it serve a specific market or be globally
Region
distributed?
Growth Expectations Do we anticipate 10x usage in the next year?
Latency What’s acceptable — sub-second or eventual results?
Availability vs.
Is the system CAP-critical? Which tradeoff is okay?
Consistency
MVP vs. Full Feature Set What’s the core product vs. add-ons?
Legal Compliance Will GDPR or HIPAA apply? Any PII/PHI handling?

🧩 Interview-Style Questions You Could Be Asked


1. How would you handle rapid user growth without rearchitecting the system?

2. What tradeoffs are involved in using a monolith vs. microservices?

3. If a service fails in your design, how is availability ensured?

4. What caching strategy would you use to reduce latency?

5. How would you redesign your system for of ine- rst capability?
fl
fi
fi
🧱 High-Level Design (HLD)
What it is: A birds-eye system representation that outlines core components, their
interactions, and the ow of data—before implementation details.

✨ Key Inclusions:

• System architecture (components, communication protocols)

• Database design (schema patterns, read/write ow)

• Module roles and relationships (e.g., user-service, payment-service)

• Tech stack choices (cloud, DB, message queues)

• Interfaces (internal APIs, external integration, frontends)

🧠 Interview Insight: You're expected to be modular, scalable, and fail-safe by design.


Think how things talk to each other and why.

📄 High-Level Design Document (HLD)


A macro-level design artifact meant for developers, architects, and sometimes
stakeholders to understand:

Section Purpose
Architecture
Visual representation of systems & data flow
Diagram
Explains what each microservice/module
Component Roles
does
Tech Stack Decisions behind DBs, queues, caching
Interfaces/APIs Contracts for inter-component interaction
Constraints Scale, security, compliance notes

🧠 Re ection: What’s the latency or consistency tradeoff across key components?

📏 Back-of-the-Envelope Estimation (BoE)


fl
fl
fl
Used to roughly validate scale and performance needs. Should answer: Can this
architecture survive the expected load?

Example: IoT Car Monitoring

• 1M cars, 1 event/10s → 100K req/s

• Each request: 25 bytes → 2.5 MB/s ingestion

• 1 year = ~79 TB storage

• 5 years ≈ 500 TB

💡 Pro-tip for interviews: Always state assumptions clearly. E.g., “Assuming each event is
25 bytes…”
🧮 Quick BoE Checklist
• QPS estimate (throughput)

• Storage over time (granularity, data model)

• Latency/response goals

• Read/write ratio & cache hit ratio

• Traf c growth curve

🧠 Question Prompt: What happens if daily active cars double? Can we horizontally scale
to keep ingestion latency <50 ms?

🔧 Detail Design Phase


Focuses on how each module works under the hood—algorithms, communication patterns,
edge cases, infra layout.

🔍 Deep Dive Areas

• Database structure: indexes, replication strategy, partitioning

• Load balancers: strategies, SSL termination


fi
• Queues: how retries/dead letters are handled

• APIs: idempotency, timeout handling

• Security: Auth/AuthZ, HTTPS, PII protection

🔁 Iterative exploration, driven by interviewer or use-case.

🧠 Starter Question: How would analytics handle 100K writes per second—via stream
processing or batched ETL?

✅ Wrapping Up System Design Interviews


• 🧠 Identify likely bottlenecks (write DB saturation, cache invalidation)

• 🧊 Consider cold-start scenarios (system recovery, deployment)

• 🔀 Leave room for scale evolution (e.g., moving from queues to pub/sub)

• 📉 Mention observability (metrics, logging, alerting)

📌 Emphasize tradeoffs → no design is awless, and you're aware of that.

⏲ Recommended Time Distribution


Time
Phase
(mins)
Requirement
5–10
Gathering
High-Level Design 10–15
Deep Dive / Detail 15–25
Wrap-Up 3–5

📈 System: Online Stock Trading Platform

🧰 Functional Requirements

• Browse stock listings & search by symbol


fl
• Real-time stock price updates

• Buy/sell stocks (with order types: market, limit, etc.)

• Portfolio & order history view

• Optional: Price alerts, watchlists, analytics

🔐 Non-Functional Requirements

• Low latency (especially for placing trades)

• High availability

• Strong consistency for transactions

• Auditability & compliance logging

• Secure data transmission

🧮 Quick Back-of-the-Envelope
Assume:

• 10M users total; ~500K daily active

• Peak trade placement: 20K/sec

• Quote subscription: 1M clients polling/streaming stock prices

• Each order: 200 bytes avg → Ingestion: 4MB/sec + quote streaming = very high
load

🧱 High-Level Architecture

┌────────────┐
┌──────────────▶ API GATEWAY │◀────────────┐
│ └────┬───────┘ │
▼ ▼ ▼
[ Mobile/Web Clients ] [ Auth Service ]
[ WebSocket Gateway ]
│ │
┌─────────────┼───────────────────────┤
▼ ▼ ▼
[ Order Service ] ───────┬──────────▶ [Quote
Stream Service]
│ │
▼ ▼
[ Order Matching Engine ] [ Portfolio Service ]
│ │
▼ ▼
[ Broker API ] [ User DB, Orders DB ]
[ Redis Cache (Portfolio) ]

─────▶ [ Audit Log DB / Kafka ]

🗂 Key Component Roles

Component Responsibilities
API Gateway Auth, rate limiting, routing
Auth Service JWT/OAuth-based user login, MFA
WebSocket Gateway Push quote updates and order status
Order Service Validate & submit trade orders
Matching Engine Forward to broker; simulate sandbox trades
Portfolio Service Manages user holdings & history
Quote Stream
Fetch from market data vendors
Service
PostgreSQL (orders), MongoDB (user prefs), Redis (fast
Data Layer
reads)
Kafka/Log Store Compliance logs, audit trails

📚 HLD Notes
• Trade Orders must go through strong validation, atomic writes, and compliance
audit.

• Use Kafka for event ow: order placed → matched → update portfolio → notify
client.

• Real-time quotes best delivered via WebSockets or gRPC streams, not polling.
fl
• Redis caches: user portfolios, quote snapshots, hot stocks.

• Support rate limiting & backpressure to prevent overload.

🤔 Interview Follow-Up Prompts


1. How would you prevent race conditions in simultaneous buy/sell requests?

2. If one broker API fails mid-trade, how is the system kept consistent?

3. How would you scale quote delivery for 1M WebSocket users?

4. How can you extend this to options trading, or after-hours trading?

🧱 Monolithic Architecture
• Single codebase → tightly coupled modules

• Single deployment unit → affects all components

• Synchronous calls dominate internal communication

• Straightforward to build, great for small to mid-sized apps

• Struggles with scaling, fault isolation, and tech evolution


🧠 Interview Triggers:

• “How would you introduce CI/CD in a monolithic app with long build times?”

• “Can we scale only the checkout system of a monolith during a festive sale?”

🧩 Microservices Architecture — Essentials


• Application decomposed into independent services

• Each owns its data, logic, and can be scaled/deployed independently

• Uses APIs or messaging queues to communicate

• Offers exibility, fault isolation, language diversity

• But introduces complexity, latency, and operational overhead


fl
🧠 Interview Triggers:

• “How would you detect partial failures in a distributed microservices system?”

• “Describe how eventual consistency would work in order placement.”

🔁 At a Glance: Comparison Summary

Feature Monolithic Microservices


Deployment All or nothing Individual services
Scaling Entire app Per service (horizontal)
Failure Scope Can bring whole app down Contained to a single service
Lower latency (no network
Performance Higher latency (over network/API)
hop)
Codebase Shared, unified Multiple, isolated repositories
Tech Stack Single stack Polyglot-friendly
Development Slower until tooling/processes
High (initially)
Speed mature

🔧 Migration Blueprint: Monolith ➡ Microservices


1. Domain modeling (DDD) → identify logical service boundaries

2. Extract non-critical services rst (e.g., analytics, email)

3. Build API contracts to bridge old + new systems

4. Migrate stateful components carefully (e.g., user pro le, cart)

5. Use strangler pattern to incrementally replace monolith modules

🔦 Real-World Situational Questions


• "How would you ensure ACID-like properties in a microservices-based checkout
work ow?"

• "What’s a hybrid architecture where a monolith and microservices can co-exist?"


fl
fi
fi
• "If real-time performance is critical, would you still go for microservices?"

⚙ Serverless Architecture – Essentials

📌 Core Concepts

1. Event-Driven Functions are invoked in response to triggers like HTTP calls,


database updates, le uploads, etc.

2. Stateless Execution Each function runs independently with no memory of prior


invocations.

3. Automatic Scaling Functions scale seamlessly with incoming traf c—zero manual
intervention needed.

4. Pay-as-You-Go Billing is based on actual compute time used, not provisioned


capacity.

🧠 Conceptual Questions

• What happens if a function needs to maintain user session state?

• How do retries or failures propagate in serverless work ows (e.g., AWS Step
Functions)?

• How would you secure endpoints exposed by serverless functions?

🆚 Architecture Comparison

Feature Monolithic Microservices Serverless


Deployment
Entire app Individual services Individual functions
Unit
Scalability Whole app Per service Per function (auto)
Depends on Usage-based (millisecond
Cost Model Fixed infra
service load billing)
Ops
High Medium Very low
Overhead
Best Use Simple apps, internal Complex modular Event-driven apps, bursty traffic
Cases tools apps patterns
fi
fl
fi
🎯 When Serverless Shines

• Real-time le/image processing

• Backend for mobile/web apps

• Scheduled tasks (cron-like)

• APIs with inconsistent traf c

🔍 Common Interview Prompts

1. Design a system to process and watermark millions of uploaded images per day
using serverless.

2. How would you implement rate-limiting in a serverless API Gateway + Lambda


setup?

3. What are cold starts in serverless? How can you mitigate them?

4. How would you log and monitor a distributed serverless work ow with multiple
chained functions?

⚡ Circuit Breaker Pattern

🔍 What Is It?

A fault-tolerance pattern used in distributed systems to detect service failures and


temporarily stop requests to prevent overwhelming already-failing components—just like a
fuse in an electrical system.

🔁 How It Works

State Behavior Example


Close All requests pass; system monitors Normal flow between your app and a
d failure rate third-party payment gateway
Requests are blocked temporarily after Gateway is unresponsive—circuit opens
Open
failure threshold is reached to prevent flooding it
Half- Limited trial requests allowed to test if After a cool-down, app sends 5 requests
Open service has recovered —if 4 succeed, circuit closes
fi
fi
fl
⚙ Key Parameters

• Failure Threshold: e.g., 5 failures out of 10 requests

• Open Timeout: e.g., stay open for 30 seconds before trying again

• Sampling Window: Track failures within a time window (like last 60s)

🎯 Real-World Use Cases

1. Net ix (Hystrix):

◦ Before moving to Resilience4j, Net ix used Hystrix to isolate failures in its


recommendation engine.

◦ If the personalization microservice went down, fallback defaults were served.

2. Airbnb:

◦ Implements circuit breakers between its internal inventory service and


booking pipeline.

◦ When availability checks failed, a fallback cache showed popular listings


instead.

3. E-commerce Flash Sales:


fl
fl
◦ During peak load (e.g., Black Friday), if payment or inventory APIs degrade,
the breaker opens temporarily to maintain checkout responsiveness.

✅ Bene ts

• Protects Upstream Systems from overload

• Improves UX by failing fast and optionally serving fallbacks

• Helps Recovery by throttling aggressive retry storms

⚠ Drawbacks

• Hard to tune thresholds for diverse traf c patterns

• Adds latency due to state-checking and fallback logic

• Fallback complexity can be non-trivial (what’s safe, meaningful to return?)

🧰 Common Tools

• Resilience4j (modern, Java)

• Polly (.NET)

• Hystrix (deprecated but conceptually useful)

🧠 Interview-Style Questions

1. How does a circuit breaker differ from retries or rate limiting?

2. Where would you place a circuit breaker in a stock-trading platform?

3. If a fallback consistently succeeds, should the circuit ever close?

4. How would you visualize circuit breaker metrics in production?


fi
fi
🚦 CQRS (Command Query Responsibility Segregation)

🔍 De nition

CQRS is a design pattern that separates reads (queries) from writes (commands) by using
dedicated models for each. It’s especially useful in scalable, complex, and distributed
systems.

⚙ Core Principle

• Commands: Change system state → create, update, delete

• Queries: Retrieve data only, no side effects

> 🧠 Why this matters: As apps grow, write and read concerns diverge. CQRS isolates them
for exibility, scaling, and better system hygiene.

🧱 Basic Architecture

Commands often trigger events → those events asynchronously update the read model.
fl
fi
💡 Advantages

Benefit Example
Scalability Scale read replicas differently from write cluster
Optimize read DB for projections,
Performance Tuning
denormalization
Flexible Data Models Separate schemas for write vs. read paths
Event Sourcing
Great fit for audit logs and rollback scenarios
Ready

🧠 Real-World Examples

• Amazon Cart System: Write-heavy for cart updates, read-heavy for product listings

• Financial Ledger: Commands store immutable events (e.g., "deposit $100"), reads
calculate derived balances

• Gaming Leaderboards: Frequent in-game writes, leaderboard reads optimized and


cached

📉 Challenges

• Eventual Consistency: Query model may lag behind the latest updates

• Sync Overhead: Keeping read model updated reliably in real time

• Increased Complexity: Two models = more testing, orchestration

• Debugging: Harder to trace across async events if not well-logged

📚 Implementation Tips

• Start with a monolith-compatible CQRS module, then split as needed

• Event Bus (e.g., Kafka) for write → read projections

• Use frameworks like Resilience4j, Axon (Java), Marten (C#)

• Monitor lag and stale reads using metrics like replication delay, event queue depth
❓ Interview-Style Questions

1. How would you ensure consistency between command and query models in real-time
order tracking?

2. Where in a social platform would CQRS bring the most value?

3. What happens if the event handler for updating the read DB fails silently? How
would you detect and recover?

4. Is CQRS overkill for CRUD apps? When should you not use it

🔁 Event Sourcing

🧠 Core Idea

Instead of directly updating a database, capture every change as a distinct event. The
current state is derived by replaying all these events in order.

> 📌 Think of it as version control for your data.


🧱 How It Works

1. Command → “Transfer $500”

2. Event → MoneyTransferred(accountA, accountB, $500)

3. Store → Event is saved in an append-only event store

4. Rebuild State → Replay all events to compute latest state (e.g., account balance)

✅ Why Use It? (Bene ts)

• Audit Trail: Full history of what happened and when

• Time Travel Debugging: Rewind app to past states by replaying events

• Flexibility: Rebuild state in new ways (e.g., migrate projection logic)

• Scalability: Append-only writes are fast and partition-friendly

• CQRS-friendly: Pairs well with separate read/write models

🌍 Real-World Examples

• Banking → Every transaction is an event; balance is derived

• E-commerce → Track all cart changes and order status updates

• Git → Commits = events; current le state = result of replay

• Gaming → Player actions, inventory, achievements logged as events

🧪 Challenges

Challenge Interview Talking Point


Event Versioning How do you handle changes to event structure over time?
Complex Queries Reading from raw events is non-trivial → use projections
Storage Growth Archive old events; snapshot frequently
Eventual
Reads may lag behind writes; suitable for async UX
Consistency
fi
fi
One corrupt event can poison state → validation is
Failure Recovery
critical

💬 Conceptual & Technical Questions

• What if an event fails to publish after a command succeeds?

• How would you replay only a subset of events for partial projection rebuild?

• What’s the difference between Event Sourcing and Change Data Capture (CDC)?

• How do you ensure idempotency when replaying events during recovery?

🚀 When to Use It

✅ Great for:

• High auditability (banking, health, logistics)

• Complex business work ows

• Distributed systems with eventual consistency

❌ Avoid for:

• Simple CRUD apps without strong history/audit requirements

• Real-time analytics if you can't tolerate delay in projections

🧳 Sidecar Pattern

🔍 What It Is

The Sidecar Pattern is when a helper service runs next to (but separate from) the main
application — like a “sidekick.” Both share the same lifecycle and often communicate over
localhost (HTTP/gRPC). The sidecar handles cross-cutting concerns, allowing your core
app to focus on business logic.

> 🧠 Think of it as of oading chores (logging, metrics, security) to a smart neighbor.


fl
fl
⚙ Key Characteristics

• Decoupled Responsibilities: Sidecars manage auxiliary tasks (logging, proxying,


etc.)

• Language/Framework Independence: Can be implemented in any language

• Lifecycle Coupling: Starts/stops with the main app

• Common Communication Pattern: HTTP/gRPC over localhost

🧩 Where It's Used (Real-World Examples)

Sidecar Tool Function Used In


Traffic control, service
Envoy Proxy Istio, Kubernetes service networking
mesh
Fluentd/ Log collection & Logging pipelines to Elasticsearch, S3,
Logstash forwarding etc.
Datadog Agent Metrics & traces Observability with Datadog
Redis Sidecar Caching layer helper Performance boost in e-commerce apps

🚀 Common Use Cases

• Logging & Monitoring (e.g., forward metrics/logs to Prometheus or ELK)

• Service Discovery (registering with Consul, Eureka)


• Rate Limiting & Authentication (enforce policies at the network edge)

• Request Proxying (centralized SSL termination, retries, circuit breakers)

🧠 Interview-Style Questions

1. Why might you use a sidecar for service discovery instead of embedding it in your
app?

2. How does the Sidecar Pattern improve observability across a microservices eet?

3. What are the tradeoffs between using a sidecar and a shared library for logging/
security?

4. How would you deploy and scale sidecars in Kubernetes?

✅ Bene ts

• Separation of Concerns: Business logic stays lean

• Cross-Language Reusability: One sidecar can serve many services

• Uniformity: Enforces consistent behaviors across services

• Scalability: Sidecars can scale independently if decoupled properly

⚠ Challenges

• Operational Overhead: More processes/pods = more resource usage

• Debugging Complexity: Requires visibility across both containers

• Coordination: Must ensure tight coupling in deployment & health checks

⚡ Event-Driven Architecture (EDA)

🧠 Core Idea

EDA is a design paradigm where components communicate by producing and reacting


to events, instead of direct API calls. It's asynchronous, loosely coupled, and highly
scalable.
fi
fl
> 📌 Think: “Something happened” → “Whoever’s interested will react.”

🧱 Key Components

Component Role
Event Producer Emits events (e.g., “order placed”)
Routes events to subscribers (e.g., Kafka, RabbitMQ,
Event Broker
SNS)
Event
Reacts to events (e.g., update inventory, send email)
Consumer

🛠 Real-World Example: E-Commerce Checkout Flow

1. User places an order → OrderPlaced event emitted

2. Inventory service reduces stock

3. Payment service charges the customer

4. Noti cation service emails a receipt Each service reacts independently, without
being tightly coupled.

✅ Bene ts
fi
fi
• Loose Coupling: Services don't know about each other’s internals

• Scalability: Consumers scale independently

• Resilience: Failures in one service don’t block others

• Real-Time Processing: Enables quick reactions to events (e.g., fraud detection)

🔍 Event Processing Styles

Pattern Example
Simple Event
Motion sensor → light turns on
Processing
Event Stream Social media clickstream updates engagement
Processing dashboards
Event Sourcing Bank account rebuilt from deposit/withdrawal events

⚠ Challenges

• Debugging Complexity → Hard to trace event ow across systems

• Event Duplication → Consumers must be idempotent

• Out-of-Order Events → Require ordering strategies or retries

• Tooling Overhead → Brokers and consumers need monitoring/maintenance

🧠 Interview-Ready Questions

• How would you ensure idempotency in event consumers?

• Compare EDA vs. RESTful synchronous APIs — when would you prefer EDA?

• How does EDA improve scalability in a stock price streaming service?

• What issues arise when producers ood brokers with high-velocity events?
fl
fl
⚖ CAP Theorem

🧠 Core Idea

In any distributed system, you can only guarantee two out of three properties at a time:

• Consistency (C): Every read re ects the latest write

• Availability (A): Every request gets a (non-error) response

• Partition Tolerance (P): The system continues to operate despite network failures

> 📌 You can’t have all three. Pick two based on your use case.
fl
🔁 Real-World Analogy

Imagine two friends (servers) taking notes for a task reminder service. If they can’t talk
(network partition), you must choose:

• Wait for con rmation (Consistency) → but user waits (no Availability)

• Respond anyway (Availability) → but risk outdated info (no Consistency)

🧩 Trade-Offs in Practice

Mod
Guarantees Sacrifices Example Use Case
el
Consistency + Partition
CP Availability Banking, ticketing (e.g., MongoDB)
Tolerance
Availability + Partition
AP Consistency Social media, DNS (e.g., Cassandra)
Tolerance
Partition Single-node DBs (e.g., MySQL in
CA Consistency + Availability
Tolerance one DC)

✅ When to Choose What

• CP → When correctness matters more than uptime (e.g., nancial systems)

• AP → When uptime matters more than perfect accuracy (e.g., news feeds)

• CA → Only feasible in non-distributed or tightly coupled systems

🧠 Interview Questions to Expect

• Why can’t we achieve all three properties in a distributed system?

• How would you design a stock trading app under CAP constraints?

• What happens to writes during a partition in a CP system?

• Can you name a real-world system that prioritizes availability over consistency?

⚠ Limitations of CAP

• Doesn’t account for latency or durability


fi
fi
• Doesn’t re ect real-world hybrid strategies (e.g., tunable consistency)

• Modern systems like Google Spanner and CRDTs aim to soften the trade-offs

🔁 Consistent Hashing

🧠 Core Idea

Consistent hashing is a technique to distribute data across nodes in a way that minimizes
remapping when nodes are added or removed. It’s a key enabler of scalable, fault-tolerant
distributed systems.

> 📌 Think of it as placing both data and servers on a circular ring using the same hash
function.

🧱 How It Works

1. Hash Ring: Imagine a circle (0–2³²). Hash both servers and keys onto this ring.

2. Data Assignment: A key is stored on the rst server clockwise from its hash.

3. Node Addition: Only keys between the new node and its predecessor are remapped.

4. Node Removal: Only keys assigned to the removed node are reassigned.
fl
fi
🌍 Real-World Examples

System Use of Consistent Hashing


Distributes partitions across nodes with minimal
Cassandra
reshuffling
Amazon
Ensures high availability and partition tolerance
DynamoDB
CDNs (e.g.,
Distribute cached content across edge servers
Akamai)
Load Balancers Route client sessions to the same backend consistently

✅ Bene ts

• Scalable: Add/remove nodes with minimal disruption

• Fault-Tolerant: Node failure affects only a small portion of keys

• Load Balanced: With virtual nodes, data is evenly spread

• Ef cient: Avoids full rehashing like in modulo-based hashing

⚠ Challenges

• Hotspots: Without virtual nodes, some servers may get overloaded

• Hash Function Choice: Poor distribution leads to imbalance

• Complexity: Slightly more complex than modulo hashing to implement

🧠 Interview Questions

• Why is consistent hashing better than modulo hashing in dynamic clusters?

• How would you implement virtual nodes to improve load distribution?

• What happens when two nodes hash to the same position?

• How does consistent hashing help in cache invalidation or session stickiness?


fi
fi
🚀 Scaling from Zero to Millions – Core Principles

🧠 What Is Scalability?

The ability of a system to handle increased load by adding resources without degrading
performance.

• Vertical Scaling: Add more power (CPU/RAM) to a single machine

• Horizontal Scaling: Add more machines to distribute load (preferred for large-scale
systems)

🧱 Key Strategies by Layer

1. Infrastructure & Traf c Management

• Cloud- rst: Use AWS/GCP/Azure for elasticity and global reach

• Auto-scaling: Dynamically adjust compute based on traf c

• Load Balancing: Distribute traf c across servers (e.g., NGINX, ELB)

• CDNs: Cache static content closer to users (e.g., Cloud are, Akamai)

2. Database Scaling

• Read Replicas: Master-slave setup for read-heavy workloads

• Sharding: Partition data across nodes (e.g., user_id % N)

• Caching: Use Redis/Memcached to reduce DB load

• Indexing: Optimize queries on high-traf c elds

3. Architecture Evolution

Stage Architecture Notes


MVP Monolith Fast to build, hard to scale later
Growt Microservice Independent scaling, better fault
h s isolation
Scale Event-Driven Async, decoupled, resilient
Burst Serverless Auto-scale, pay-per-use, great for spikes
fi
fi
fi
fi
fi
fl
fi
🌍 Real-World Examples
• Instagram: Started as a monolith, migrated to microservices with sharded Postgres
and Redis

• Net ix: Uses microservices + event-driven + sidecars (Envoy) for observability

• Amazon: Embraced microservices + eventual consistency + DynamoDB for global


scale

🧠 Interview-Style Questions
• How would you scale a read-heavy social feed to 10M DAUs?

• What trade-offs do you face when sharding a user table?

• How would you handle a sudden 10x traf c spike during a product launch?

• When would you choose serverless over containers?

✅ Best Practices
• Start simple, scale smart: Don’t over-engineer early

• Monitor everything: Use Prometheus, Grafana, Datadog

• Automate deployments: CI/CD pipelines, blue-green or canary releases

• Design for failure: Use retries, circuit breakers, graceful degradation

• Test at scale: Load test with tools like k6, JMeter, or Locust

✅ System Design Interview: Design a Rate


Limiter
This walkthrough is structured step-by-step like an interview with questions, answers,
decisions, trade-offs, and justi cations. All your detailed data is organically incorporated
into the ow.

🔹 1. Understanding the Problem


fl
fl
fi
fi
🎯 Interviewer: "Design a rate limiter."

Your response:

"Sure, just to clarify:

• Are we limiting by IP, user, or API key?

• What’s the threshold (e.g., 100 req/min)?

• Do we care about burst traf c?

• Should this work in a distributed system?"

💡 What is a Rate Limiter?


A rate limiter controls the number of requests a user/client can make to a system over a
period of time.

✅ Example:
If your order service handles 1,000 RPS, but gets 10,000 RPS during sales — a rate limiter
drops excess requests, shielding backend services.

🔹 2. Use Cases of Rate Limiting


• Time Window Limiting: Keep usage predictable — useful for billing and stability.

• Revenue Management: Tiered API access for free/premium plans.

• Controlling Spikes: Avoid overloads during ash events.

• Preventing Abuse: Stop bots, DoS attacks, or aggressive clients.

❓ Interview Question:
"How do you inform clients about rate limits?"

✅ Return HTTP 429 (Too Many Requests) with headers:

• RateLimit-Limit: total allowed

• RateLimit-Remaining: left in window

• Retry-After: wait time before retry


fi
fl
🔹 3. Types of Throttling

Type Description
Hard Throttling Strictly reject any request beyond the limit.
Drop requests but suggest retry with Retry-
Soft Throttling
After.
Elastic
Allow burst traffic if system has spare capacity.
Throttling

For critical APIs → hard or soft.


For exible services → elastic works well.

🔹 4. Where to Place the Rate Limiter

Location Pros Cons


Reduces server
Client-side Can be bypassed
load
Server-side Full control Tight coupling
Middleware/API Scalable, Requires distributed
Gateway decoupled coordination
fl
✅ Best choice for most: API Gateway or Dedicated Middleware

❓ Interview Question:
"How do you ensure high availability?"

✅ Use:

• Redis clustering and replication.

• Retry mechanisms.

• Stateless limiter logic (store in Redis, not memory).

• Graceful fallback modes.

🔹 5. Choosing the Right Algorithm

Algorithm Pros Cons


Allows bursts, simple to Needs careful refill
Token Bucket
tune tuning
Leaky Bucket Smooths traffic Drops excess requests
Fixed Window Counter Easy to implement Bursts at window edge
Sliding Window Log High accuracy High memory usage
Sliding Window Balanced, used by
Slight inaccuracy
Counter Cloudflare

✅ Choose Token Bucket for:

• Burst-friendly APIs

• Good enough accuracy

• Low overhead
🔹 6.

Architecture Design
+------------+
Client ------> | API Gateway| ----> Backend
+------------+
|
v
+----------------+
| Rate Limiter |
| (Token Bucket) |
+----------------+
|
v
[Redis Store]

🔹 7. Redis as the Storage Layer


Why Redis?
• In-memory → fast O(1) reads/writes

• Supports INCR, EXPIRE, ZADD, ZREMRANGEBYSCORE

• Lua scripts enable atomic logic

❓ Interview Question:
"How to handle race conditions in rate limiting?"

✅ Use Lua scripts in Redis:

• Atomic check-and-update in 1 transaction

• Prevents two nodes from allowing the same request simultaneously

🔹 8. C++ Pseudocode (Token Bucket)

class TokenBucket {
private:
int maxTokens;
double refillRate; // tokens per second
double currentTokens;
long long lastRefillTime;

long long currentTimeMillis() {


return
std::chrono::duration_cast<std::chrono::milliseconds>(

std::chrono::steady_clock::now().time_since_epoch()).coun
t();
}

void refill() {
long long now = currentTimeMillis();
double tokensToAdd = ((now - lastRefillTime) /
1000.0) * refillRate;
currentTokens = std::min(maxTokens * 1.0,
currentTokens + tokensToAdd);
lastRefillTime = now;
}
public:
TokenBucket(int capacity, double rate)
: maxTokens(capacity), refillRate(rate),
currentTokens(capacity) {
lastRefillTime = currentTimeMillis();
}

bool allowRequest() {
refill();
if (currentTokens >= 1.0) {
currentTokens -= 1.0;
return true;
}
return false;
}
};

🔹 9. Scalability Concerns
• Redis bottleneck → use clustering, sharding

• High RPS per user → use time-sharded keys

• Geographical spread → replicate Redis near edge locations

🔹 10. Error Handling & Resilience


• Redis down? → fallback to approximate local memory limit

• Log all 429 responses

• Monitor Redis latency and failures

❓ Interview Question:
"What if Redis goes down?"

✅ Options:

• Fallback to in-process limits (approximate)


• Return fail-open/closed based on policy

• Use retry + exponential backoff

🔹 11. Tiered API Plan Support

Plan Limit
Free 60 req/min
Premiu 1000 req/
m min

Design:

• Store limits per user type in DB or con g

• Redis key → rate:<plan>:<user_id>

🔹 12. Monitoring
• Export metrics:

◦ Throttled requests

◦ Redis errors

◦ Avg token consumption

• Use: Prometheus + Grafana

🔚 Conclusion – Summary for Interview


"To summarize, I’d design the rate limiter as middleware in the API gateway, using a Token
Bucket algorithm implemented over Redis for fast shared storage. I’d handle race
conditions with Lua scripts and use proper HTTP 429 responses with headers. The design
supports tiered limits, burst traf c, and scalable, distributed operation with Redis
replication. Monitoring and fallback paths ensure resilience."
fi
fi

You might also like