lunes, 7 de mayo de 2018

Abstract Factory design pattern with both interfaces and enumerations approaches. Examples in some popular languages.

The Abstract Factory design pattern is a creational pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes. Below is a Java implementation of the Abstract Factory design pattern.

Implementation of Abstract Factory Pattern

  1. Abstract Factory Interface: Defines methods for creating abstract products.
  2. Concrete Factories: Implement the creation of specific families of products.
  3. Abstract Product Interfaces: Define the types of products.
  4. Concrete Products: Implement the abstract product interfaces.
  5. Client Code: Uses the factory and products through their interfaces.

 



Example 1: Abstract Factory with Interfaces in Java

Below is an example implementation of the Abstract Factory pattern using interfaces in a banking context for creating different types of loans.

// Abstract Factory
interface LoanFactory {
    Loan createLoan();
}

// Abstract Product
interface Loan {
    void getDetails();
}

// Concrete Products
class MortgageLoan implements Loan {
    @Override
    public void getDetails() {
        System.out.println("This is a Mortgage Loan.");
    }
}

class PersonalLoan implements Loan {
    @Override
    public void getDetails() {
        System.out.println("This is a Personal Loan.");
    }
}

class AutoLoan implements Loan {
    @Override
    public void getDetails() {
        System.out.println("This is an Auto Loan.");
    }
}

// Concrete Factories
class MortgageLoanFactory implements LoanFactory {
    @Override
    public Loan createLoan() {
        return new MortgageLoan();
    }
}

class PersonalLoanFactory implements LoanFactory {
    @Override
    public Loan createLoan() {
        return new PersonalLoan();
    }
}

class AutoLoanFactory implements LoanFactory {
    @Override
    public Loan createLoan() {
        return new AutoLoan();
    }
}

// Client
public class BankingApplication {
    public static void main(String[] args) {
        LoanFactory mortgageFactory = new MortgageLoanFactory();
        Loan mortgageLoan = mortgageFactory.createLoan();
        mortgageLoan.getDetails();

        LoanFactory personalFactory = new PersonalLoanFactory();
        Loan personalLoan = personalFactory.createLoan();
        personalLoan.getDetails();

        LoanFactory autoFactory = new AutoLoanFactory();
        Loan autoLoan = autoFactory.createLoan();
        autoLoan.getDetails();
    }
}

Example 2: Abstract Factory with Enumerations in Java

This example uses enums to manage the loan creation logic. Each enum instance acts as a factory.

// Abstract Product
abstract class Loan {
    abstract void getDetails();
}

// Concrete Products
class MortgageLoan extends Loan {
    @Override
    void getDetails() {
        System.out.println("This is a Mortgage Loan.");
    }
}

class PersonalLoan extends Loan {
    @Override
    void getDetails() {
        System.out.println("This is a Personal Loan.");
    }
}

class AutoLoan extends Loan {
    @Override
    void getDetails() {
        System.out.println("This is an Auto Loan.");
    }
}

// Abstract Factory using Enum
enum LoanFactory {
    MORTGAGE {
        @Override
        public Loan createLoan() {
            return new MortgageLoan();
        }
    },
    PERSONAL {
        @Override
        public Loan createLoan() {
            return new PersonalLoan();
        }
    },
    AUTO {
        @Override
        public Loan createLoan() {
            return new AutoLoan();
        }
    };

    public abstract Loan createLoan();
}

// Client
public class BankingApplicationWithEnum {
    public static void main(String[] args) {
        Loan mortgageLoan = LoanFactory.MORTGAGE.createLoan();
        mortgageLoan.getDetails();

        Loan personalLoan = LoanFactory.PERSONAL.createLoan();
        personalLoan.getDetails();

        Loan autoLoan = LoanFactory.AUTO.createLoan();
        autoLoan.getDetails();
    }
}

Key Points:

  1. Using Interfaces:

    • Provides flexibility and extensibility by allowing additional types of loans and factories without modifying existing code.
    • Clear separation of concerns with individual factory classes for each loan type.
  2. Using Enumerations:

    • Simplifies factory management as enum constants inherently represent different factories.
    • Reduces the overhead of creating multiple factory classes but is less flexible for large-scale systems.

  

Advantages and disadvantages of using


Advantages

  1. Encapsulation of Object Creation:

    • Hides the details of how objects are created and assembled, making code cleaner and easier to manage.
  2. Consistency:

    • Ensures that products created by the factory are compatible with each other (e.g., objects from the same family).
  3. Scalability:

    • Adding new product families is straightforward. You can add a new factory and corresponding products without altering existing code.
  4. Flexibility:

    • Decouples client code from concrete implementations, allowing easy swapping of product families by simply changing the factory instance.
  5. Promotes Dependency Inversion Principle:

    • Clients depend on abstractions (interfaces or abstract classes) rather than concrete classes, enhancing testability and maintainability.
  6. Centralized Creation Logic:

    • All object creation is handled in one place, making it easier to update or modify.

Disadvantages

  1. Complexity:

    • Introduces additional layers of abstraction and requires more classes and interfaces, which can make the system more complex.
  2. Difficult to Extend Individual Products:

    • Modifying or adding a single product in a family may require changes in all the factories or creating a new product family.
  3. Overhead for Small Systems:

    • In systems with limited object types or families, the pattern can be overkill and unnecessarily increase codebase size.
  4. Coupling Between Factories and Products:

    • Factories are tightly coupled to the specific product hierarchies they create, which can limit flexibility in some cases.
  5. Rigid Structure:

    • While adding a new family is straightforward, adding support for a new product across existing families can be cumbersome as it may require changes to all factories.

When to Use Abstract Factory

  • Use the Abstract Factory pattern when:

    • The system needs to work with multiple families of related objects.
    • You want to isolate the creation process of objects from their usage.
    • Ensuring compatibility among objects is a priority.
    • There’s a need to change the product family dynamically at runtime.
  • Avoid using the pattern when:

    • The system has simple requirements with few types of objects.
    • The overhead of creating factories and products outweighs the benefits.

 


Example 1: Abstract Factory with Interfaces in C#

Below is the implementation of the Abstract Factory pattern using interfaces in C#.

// Abstract Factory
public interface ILoanFactory
{
    ILoan CreateLoan();
}

// Abstract Product
public interface ILoan
{
    void GetDetails();
}

// Concrete Products
public class MortgageLoan : ILoan
{
    public void GetDetails()
    {
        Console.WriteLine("This is a Mortgage Loan.");
    }
}

public class PersonalLoan : ILoan
{
    public void GetDetails()
    {
        Console.WriteLine("This is a Personal Loan.");
    }
}

public class AutoLoan : ILoan
{
    public void GetDetails()
    {
        Console.WriteLine("This is an Auto Loan.");
    }
}

// Concrete Factories
public class MortgageLoanFactory : ILoanFactory
{
    public ILoan CreateLoan()
    {
        return new MortgageLoan();
    }
}

public class PersonalLoanFactory : ILoanFactory
{
    public ILoan CreateLoan()
    {
        return new PersonalLoan();
    }
}

public class AutoLoanFactory : ILoanFactory
{
    public ILoan CreateLoan()
    {
        return new AutoLoan();
    }
}

// Client
public class BankingApplication
{
    public static void Main(string[] args)
    {
        ILoanFactory mortgageFactory = new MortgageLoanFactory();
        ILoan mortgageLoan = mortgageFactory.CreateLoan();
        mortgageLoan.GetDetails();

        ILoanFactory personalFactory = new PersonalLoanFactory();
        ILoan personalLoan = personalFactory.CreateLoan();
        personalLoan.GetDetails();

        ILoanFactory autoFactory = new AutoLoanFactory();
        ILoan autoLoan = autoFactory.CreateLoan();
        autoLoan.GetDetails();
    }
}

Example 2: Abstract Factory with Enumerations in C#

This example uses enumerations to manage loan creation logic in C#.

// Abstract Product
public abstract class Loan
{
    public abstract void GetDetails();
}

// Concrete Products
public class MortgageLoan : Loan
{
    public override void GetDetails()
    {
        Console.WriteLine("This is a Mortgage Loan.");
    }
}

public class PersonalLoan : Loan
{
    public override void GetDetails()
    {
        Console.WriteLine("This is a Personal Loan.");
    }
}

public class AutoLoan : Loan
{
    public override void GetDetails()
    {
        Console.WriteLine("This is an Auto Loan.");
    }
}

// Abstract Factory using Enum
public enum LoanFactory
{
    MORTGAGE,
    PERSONAL,
    AUTO
}

public static class LoanFactoryExtensions
{
    public static Loan CreateLoan(this LoanFactory factory)
    {
        return factory switch
        {
            LoanFactory.MORTGAGE => new MortgageLoan(),
            LoanFactory.PERSONAL => new PersonalLoan(),
            LoanFactory.AUTO => new AutoLoan(),
            _ => throw new ArgumentException("Invalid Loan Type")
        };
    }
}

// Client
public class BankingApplicationWithEnum
{
    public static void Main(string[] args)
    {
        Loan mortgageLoan = LoanFactory.MORTGAGE.CreateLoan();
        mortgageLoan.GetDetails();

        Loan personalLoan = LoanFactory.PERSONAL.CreateLoan();
        personalLoan.GetDetails();

        Loan autoLoan = LoanFactory.AUTO.CreateLoan();
        autoLoan.GetDetails();
    }
}

Key Points for Both Examples:

  1. Using Interfaces:

    • Provides a clean separation of concerns.
    • Adding new loan types requires creating new concrete product and factory classes.
  2. Using Enumerations:

    • Simplifies the logic by using a single enum to encapsulate all factory behavior.
    • Extensions on the enum provide centralized logic for creating different loan types.
    • Less flexible if individual factories or additional customization is required.

Both examples effectively demonstrate the Abstract Factory pattern in a C# context!

 

 

 

miércoles, 2 de mayo de 2018

Resilience Patterns common adv disadv

 

In software design, resilience patterns are strategies or techniques to ensure systems can recover gracefully and continue functioning despite failures, disruptions, or unexpected events. These patterns are especially relevant in distributed systems, cloud environments, and large-scale applications where failures are inevitable.

Here are common resilience patterns in software:


1. Retry Pattern

  • Description: Automatically retry a failed operation with a delay or exponential backoff to account for transient failures.
  • Use Case: Network timeouts, temporary unavailability of services.
  • Example:
    • A payment service retries a transaction after a brief pause if the upstream payment gateway fails to respond.
  • Tools: Libraries like Polly (C#), Resilience4j (Java).

2. Circuit Breaker Pattern

  • Description: Prevent repeated attempts to perform an operation that is likely to fail. When failures cross a threshold, the circuit "opens," blocking further calls for a cooldown period.
  • Use Case: Avoid cascading failures due to an unavailable or slow service.
  • Example:
    • If a database connection fails consistently, the application stops retrying for a set period and quickly responds with fallback logic.
  • Tools: Hystrix (Netflix), Resilience4j (Java), Polly (C#).

3. Fallback Pattern

  • Description: Provide an alternative response or behavior when an operation fails.
  • Use Case: Mitigating the impact of failure.
  • Example:
    • If a recommendation service fails, return static or cached recommendations instead.
  • Tools: Integrated into circuit breaker tools like Hystrix or Resilience4j.

4. Timeout Pattern

  • Description: Set a maximum duration for an operation to complete; if it exceeds that duration, abort the operation.
  • Use Case: Prevent a system from waiting indefinitely for a slow or unresponsive service.
  • Example:
    • An API call to a third-party service times out after 5 seconds, returning an error or fallback response.

5. Bulkhead Pattern

  • Description: Isolate critical components or resources into separate "pools" to prevent failure in one area from affecting others.
  • Use Case: Protect a system from resource exhaustion caused by a single failure.
  • Example:
    • If a particular service consumes too many threads or connections, it is isolated in its own thread pool to prevent affecting the entire application.
  • Tools: Thread pools, containerization (e.g., Kubernetes).

6. Rate Limiting and Throttling

  • Description: Limit the number of requests or operations that can occur in a given time period.
  • Use Case: Protect against excessive load, abuse, or denial-of-service attacks.
  • Example:
    • An API limits clients to 100 requests per minute to prevent server overload.
  • Tools: API Gateways, Redis (rate limiting), cloud-based tools like AWS API Gateway.

7. Idempotency Pattern

  • Description: Ensure that an operation can be performed multiple times without changing the result.
  • Use Case: Retry logic where multiple attempts may occur.
  • Example:
    • Retrying a payment request should not result in multiple charges.
  • Tools: Use unique identifiers (e.g., idempotency keys) to track operations.

8. Failover Pattern

  • Description: Switch to a backup system or component when the primary one fails.
  • Use Case: Ensure high availability and continuity.
  • Example:
    • If the primary database server fails, traffic is routed to a replica or standby database.
  • Tools: Load balancers, cloud failover mechanisms.

9. Data Replication Pattern

  • Description: Maintain copies of data across different nodes or regions to improve availability and resilience.
  • Use Case: Recover from hardware failures or data loss.
  • Example:
    • Data replication in distributed databases like Cassandra or PostgreSQL replicas.
  • Tools: Cloud databases (e.g., AWS RDS Multi-AZ), distributed storage (e.g., HDFS).

10. Chaos Engineering

  • Description: Introduce controlled failures to test and improve system resilience proactively.
  • Use Case: Validate a system’s ability to handle failures before they occur in production.
  • Example:
    • Simulating server failures or network latency using tools like Gremlin or Netflix Chaos Monkey.

11. Compensating Transaction Pattern

  • Description: Perform a rollback or corrective operation if a transaction across services fails.
  • Use Case: Ensuring consistency in distributed systems.
  • Example:
    • If booking a hotel succeeds but booking a flight fails, cancel the hotel reservation.

12. Message Queue and Event-Driven Architecture

  • Description: Use message queues to decouple services and ensure messages are retried or persisted during failures.
  • Use Case: Decouple systems to avoid cascading failures.
  • Example:
    • Using Kafka or RabbitMQ to persist messages during an outage and replay them later.

Why Use Resilience Patterns?

Resilience patterns are critical for:

  • Ensuring high availability in distributed systems.
  • Minimizing downtime caused by hardware, software, or network failures.
  • Improving user experience by handling failures gracefully.
  • Protecting system resources and preventing cascading failures.

By implementing these patterns, software systems can recover faster, adapt to transient issues, and continue delivering reliable performance even under adverse conditions.

 



Advantages and Disadvantages

Here’s a detailed explanation of the advantages and disadvantages of each resilience pattern:

 

1. Retry Pattern

  • Description: Automatically retry a failed operation after a delay or with an exponential backoff.

Advantages:

  • Simple to implement for transient failures like timeouts or temporary unavailability.
  • Improves reliability by overcoming temporary issues without user intervention.
  • Allows seamless recovery without manual intervention.

Disadvantages:

  • May cause overload if retries are not controlled (e.g., excessive retries can overwhelm a failing service).
  • Wastes resources when a failure is not transient (e.g., permanent failure cases).
  • Can cause longer delays if retries are too aggressive.

Best Use Case:

  • For transient failures (e.g., temporary network glitches, database deadlocks).

2. Circuit Breaker Pattern

  • Description: Stops repeated attempts to perform a failing operation. After failures reach a threshold, the circuit "opens" to block further calls temporarily.

Advantages:

  • Prevents cascading failures by quickly detecting and halting requests to faulty services.
  • Improves system stability under failure conditions.
  • Reduces unnecessary load on failing services.

Disadvantages:

  • Circuit recovery (from open to closed) can cause temporary "thundering herd" issues when many requests retry simultaneously.
  • Requires tuning thresholds and cooldown periods, which can be complex.
  • False positives may occur, causing healthy services to be temporarily blocked.

Best Use Case:

  • For dependent services that may fail or slow down, such as external APIs or databases.

3. Fallback Pattern

  • Description: Provides an alternative response or behavior when an operation fails.

Advantages:

  • Improves user experience by providing a degraded response instead of a failure.
  • Prevents complete system breakdown in case of failures.
  • Supports graceful degradation under partial failure scenarios.

Disadvantages:

  • Fallback logic might not always meet functional requirements (e.g., stale or inaccurate data).
  • Adds extra implementation effort to provide meaningful fallback mechanisms.
  • May mask underlying issues if fallback is used excessively.

Best Use Case:

  • When alternative data or cached responses can substitute for a failing service.

4. Timeout Pattern

  • Description: Sets a maximum time for an operation to complete, aborting it if it exceeds the limit.

Advantages:

  • Prevents the system from hanging indefinitely due to unresponsive components.
  • Frees up resources by abandoning slow operations.
  • Avoids cascading failures caused by blocked threads or connections.

Disadvantages:

  • Requires careful timeout tuning; too short may trigger false timeouts, too long may delay recovery.
  • May lead to incomplete operations or partial failures.
  • Combined with retries, it can increase load if not managed properly.

Best Use Case:

  • For network calls, database queries, or external service requests that might become unresponsive.

5. Bulkhead Pattern

  • Description: Isolates resources into separate pools to prevent one failure from impacting the entire system.

Advantages:

  • Limits the blast radius of a failure, ensuring other components remain unaffected.
  • Protects system stability by isolating resource-hungry services.
  • Useful for handling varying levels of load across services.

Disadvantages:

  • Resource isolation adds complexity (e.g., configuring multiple thread pools or containers).
  • May lead to underutilized resources if isolation is overprovisioned.
  • Incorrect configuration can still cause bottlenecks or resource exhaustion.

Best Use Case:

  • For resource-constrained systems with critical components that must remain unaffected by failures in others.

6. Rate Limiting and Throttling

  • Description: Limits the number of requests processed within a given time to prevent system overload.

Advantages:

  • Prevents overloading and potential denial of service.
  • Ensures fair usage of system resources.
  • Improves system stability during traffic spikes.

Disadvantages:

  • May block legitimate requests if limits are too strict.
  • Introduces additional latency for throttled requests.
  • Requires monitoring and dynamic configuration to adjust for varying loads.

Best Use Case:

  • For APIs or services with high load or risk of abuse.

7. Idempotency Pattern

  • Description: Ensures that an operation can be executed multiple times without side effects.

Advantages:

  • Safeguards against unintended duplicate operations caused by retries.
  • Ensures consistency in distributed systems.
  • Reduces the risk of data corruption or inconsistency.

Disadvantages:

  • Requires tracking of request states (e.g., using unique identifiers like idempotency keys).
  • Adds complexity to design and increases storage overhead.
  • Not all operations are naturally idempotent (e.g., payment processing).

Best Use Case:

  • For retry mechanisms where duplicates could cause issues (e.g., billing or payment APIs).

8. Failover Pattern

  • Description: Switches to a backup component or system when the primary one fails.

Advantages:

  • Ensures high availability and reduces downtime.
  • Provides seamless failover with minimal user impact.
  • Redundant systems improve fault tolerance.

Disadvantages:

  • Requires additional infrastructure and redundancy (increased cost).
  • Data synchronization between primary and backup can be challenging.
  • Failover mechanisms can introduce latency during switching.

Best Use Case:

  • For critical systems requiring high availability, such as databases or load-balanced services.

9. Data Replication Pattern

  • Description: Maintains copies of data across multiple nodes or regions.

Advantages:

  • Improves availability and resilience against hardware or node failures.
  • Enhances read performance by serving data from multiple locations.
  • Protects against data loss.

Disadvantages:

  • Increases data storage and synchronization costs.
  • May introduce eventual consistency issues in distributed systems.
  • Complex to manage across multiple nodes or regions.

Best Use Case:

  • For databases or distributed systems requiring fault tolerance and high availability.

10. Chaos Engineering

  • Description: Intentionally injects failures to test system resilience.

Advantages:

  • Proactively identifies weaknesses and improves fault tolerance.
  • Helps teams prepare for real-world failure scenarios.
  • Strengthens confidence in system resilience.

Disadvantages:

  • Requires careful planning to avoid unintended system disruptions.
  • Can cause real outages if experiments are not properly controlled.
  • Adds operational overhead for conducting and monitoring tests.

Best Use Case:

  • For large-scale systems where failures are inevitable, such as cloud-native applications.

11. Compensating Transaction Pattern

  • Description: Rolls back or corrects changes made by failed transactions.

Advantages:

  • Ensures data consistency across distributed systems.
  • Mitigates the impact of failures in multi-step workflows.
  • Supports "eventual consistency" in distributed systems.

Disadvantages:

  • Increases implementation complexity.
  • Requires careful design to handle rollback scenarios correctly.
  • May introduce latency when handling failures.

Best Use Case:

  • For distributed systems or microservices requiring transactional integrity.

12. Message Queue and Event-Driven Architecture

  • Description: Decouples services using message queues to persist and retry failed messages.

Advantages:

  • Increases system reliability by enabling asynchronous processing.
  • Ensures messages are not lost during failures.
  • Decouples systems to avoid cascading failures.

Disadvantages:

  • Adds latency compared to synchronous communication.
  • Requires infrastructure like message brokers (e.g., Kafka, RabbitMQ).
  • Potential message duplication or reordering must be handled.

Best Use Case:

  • For systems requiring asynchronous communication or retries, such as event-driven applications.

By carefully choosing the right resilience pattern for each failure scenario, systems can achieve high availability, stability, and reliability.

 

 

lunes, 2 de abril de 2018

Some of the most used and most common design patterns.

 

Here’s a list of the most used and most common design patterns, categorized by their type and popularity in software development:


1. Creational Patterns

Focus on object creation mechanisms, providing flexibility and efficiency.

  1. Singleton

    • Ensures a class has only one instance and provides a global point of access to it.

    • Commonly used for logging, configuration, and managing shared resources.

  2. Factory Method

    • Defines an interface for creating objects but lets subclasses decide which class to instantiate.

    • Commonly used in frameworks for creating objects without specifying exact classes.

  3. Abstract Factory

    • Provides an interface to create families of related or dependent objects without specifying their concrete classes.

    • Used in GUI libraries (e.g., different themes for UI components).

  4. Builder

    • Constructs complex objects step-by-step, separating the construction from representation.

    • Useful for creating immutable objects like StringBuilder or for object hierarchies.

  5. Prototype

    • Creates new objects by cloning an existing object (prototypes).

    • Used when object creation is costly, e.g., in game engines or document editors. 


       


2. Structural Patterns

Deal with composing classes or objects into larger structures while keeping them flexible and efficient.

  1. Adapter

    • Allows incompatible interfaces to work together by translating one interface to another.

    • Commonly used in legacy system integrations.

  2. Decorator

    • Adds functionality to an object dynamically without altering its structure.

    • Common in user interface components (e.g., adding scrollbars or borders).

  3. Facade

    • Provides a simplified interface to a complex subsystem.

    • Often used in APIs and libraries to hide implementation details.

  4. Composite

    • Composes objects into tree structures to represent part-whole hierarchies.

    • Frequently used in GUIs and file system representations.

  5. Proxy

    • Provides a surrogate or placeholder for another object to control access to it.

    • Used for lazy initialization, access control, and logging.

  6. Bridge

    • Decouples an abstraction from its implementation so that both can vary independently.

    • Common in graphical rendering libraries.

  7. Flyweight

    • Reduces memory usage by sharing as much data as possible with similar objects.

    • Used in caching mechanisms or text editors for character storage.


3. Behavioral Patterns

Concerned with the communication and interaction between objects.

  1. Strategy

    • Encapsulates algorithms and allows them to be interchangeable at runtime.

    • Common in payment processing systems and sorting algorithms.

  2. Observer

    • Defines a dependency between objects so that when one changes state, its dependents are notified.

    • Commonly used in event-driven programming and GUIs.

  3. Command

    • Encapsulates a request as an object, allowing parameterization and queuing of requests.

    • Used in undo/redo systems and job scheduling.

  4. State

    • Allows an object to change its behavior when its state changes.

    • Frequently used in workflows or state machines.

  5. Template Method

    • Defines the skeleton of an algorithm in a base class, allowing subclasses to override specific steps.

    • Common in frameworks and libraries.

  6. Chain of Responsibility

    • Passes a request along a chain of handlers until it is handled.

    • Used in logging systems or middleware pipelines.

  7. Mediator

    • Centralizes communication between objects to reduce dependencies.

    • Common in chat applications or GUIs.

  8. Iterator

    • Provides a way to access elements of a collection sequentially without exposing its underlying structure.

    • Used in collections and data structures.

  9. Visitor

    • Adds new operations to objects without modifying their classes.

    • Used in compilers or processing hierarchical structures.

  10. Memento

    • Captures an object’s state to restore it later.

    • Commonly used in undo functionality.


Additional Modern Patterns

Beyond the classical Gang of Four patterns, these modern patterns are also widely used:

  1. Dependency Injection (DI)

    • Provides dependencies to objects from an external source, improving modularity.

    • A cornerstone of frameworks like Spring or .NET Core.

  2. Model-View-Controller (MVC)

    • Separates an application into three components: Model (data), View (UI), and Controller (logic).

    • Dominant in web development frameworks like Ruby on Rails and ASP.NET MVC.

  3. Model-View-ViewModel (MVVM)

    • Variant of MVC, primarily used in UI frameworks like Angular or WPF.

  4. Repository

    • Abstracts access to data sources, providing a clean separation between the application and data layer.

    • Common in DDD (Domain-Driven Design).

  5. CQRS (Command Query Responsibility Segregation)

    • Separates read and write operations into different models.

    • Used in scalable architectures.

  6. Event Sourcing

    • Represents the state of an object as a sequence of events.

    • Common in microservices and systems requiring strong audit trails.


Most Used Patterns in Practice

  1. Singleton

  2. Factory Method

  3. Strategy

  4. Observer

  5. MVC/MVVM

  6. Dependency Injection

  7. Repository

  8. Adapter

  9. Decorator

  10. Command

These patterns are versatile, widely applicable, and form the backbone of most modern software systems.