Mastering Design Patterns in C# and ASP.NET Core
Part 4.6 – Memento Design Pattern
Series: Design Patterns in C# and ASP.NET Core
Pattern Category: Behavioral Design Pattern
Difficulty: ⭐⭐⭐☆☆ Intermediate
Prerequisites: C#, OOP, Classes, Interfaces, Encapsulation, Dependency Injection, ASP.NET Core
Table of Contents
Introduction
What is the Memento Design Pattern?
Why Do We Need the Memento Pattern?
The Problem with Restoring Object State
Memento Pattern Solution
Originator, Memento, and Caretaker
UML Class Diagram
How the Memento Pattern Works
Complete C# Console Application
Undo/Redo Example
Immutable Memento
ASP.NET Core Implementation
Banking Transaction Example
Document Version History Example
Configuration Snapshot Example
Database Transaction and Memento
Memento vs Command
Memento vs Prototype
Memento vs Snapshot
Advantages
Disadvantages
Best Practices
Common Mistakes
Real-World Enterprise Scenarios
Interview Questions
Key Takeaways
Conclusion
Coming Up Next
1. Introduction
Applications frequently need the ability to save an object's state and restore it later.
For example:
A text editor needs Undo.
A drawing application needs previous versions.
A configuration system may need rollback.
A workflow engine may need to restore a previous state.
A game needs save points.
A banking application may need to preserve transaction state.
An application may need to maintain document versions.
Consider a document editor:
Document State
↓
Version 1
↓
Version 2
↓
Version 3
↓
Undo
↓
Restore Version 2
How can we save the previous state without exposing the internal implementation of the object?
This is where the Memento Design Pattern becomes useful.
2. What is the Memento Design Pattern?
The Memento Design Pattern is a behavioral design pattern that allows an object to capture and restore its previous state without exposing its internal implementation details.
The key idea is:
Capture an object's state at a particular point in time so that it can be restored later.
A simplified representation is:
Object
↓
Create Snapshot
↓
Memento
↓
Store
↓
Restore Later
The object whose state is saved is called the Originator.
The saved state is represented by a Memento.
The object that stores the mementos is called the Caretaker.
3. Why Do We Need the Memento Pattern?
Suppose we have:
public class Document
{
public string Title { get; set; }
public string Content { get; set; }
public string Font { get; set; }
}
Now imagine that we want to implement:
Edit
Edit
Edit
Undo
Undo
Redo
One approach might be to expose all properties publicly and allow another class to copy them.
But this can lead to problems.
The caretaker may become tightly coupled to the internal structure of Document.
For example:
var title = document.Title;
var content = document.Content;
var font = document.Font;
Later, suppose we add:
public string BackgroundColor { get; set; }
Now every state-saving component may need to change.
This violates encapsulation.
The Memento Pattern provides a better abstraction.
4. The Problem with Restoring Object State
Consider a complex object:
Order
├── Customer
├── Items
├── Payment
├── Shipping
├── Discount
└── Status
Saving and restoring the complete state manually can become complicated.
We don't want external classes to understand every internal detail.
Ideally:
Caretaker
|
| "Save"
↓
Originator
|
↓
Memento
The caretaker stores the snapshot but does not need to understand its internal details.
5. Memento Pattern Solution
The Memento Pattern introduces three major components:
Originator
|
| creates
↓
Memento
↑
|
Caretaker
Originator
The object whose state needs to be saved.
Memento
Stores the state of the Originator.
Caretaker
Stores and manages Mementos.
6. Originator, Memento, and Caretaker
6.1 Originator
The Originator:
Maintains current state.
Creates a Memento.
Restores state from a Memento.
Example:
public class Document
{
public string Content { get; private set; }
public Memento Save()
{
return new Memento(Content);
}
public void Restore(Memento memento)
{
Content = memento.Content;
}
}
6.2 Memento
The Memento stores a snapshot.
public class Memento
{
public string Content { get; }
public Memento(string content)
{
Content = content;
}
}
6.3 Caretaker
The Caretaker stores snapshots.
public class History
{
private readonly Stack<Memento> _history = new();
public void Save(Memento memento)
{
_history.Push(memento);
}
public Memento Undo()
{
return _history.Pop();
}
}
The important point is that the Caretaker doesn't modify the document's internal state directly.
7. UML Class Diagram
The classic UML structure looks like this:
+------------------+
| Originator |
+------------------+
| - state |
+------------------+
| + Save() |
| + Restore() |
+--------+---------+
|
| creates
↓
+------------------+
| Memento |
+------------------+
| - state |
+------------------+
| + State |
+------------------+
↑
|
| stores
+--------+---------+
| Caretaker |
+------------------+
| - history |
+------------------+
| + Save() |
| + Undo() |
+------------------+
8. How the Memento Pattern Works
Suppose a document has the following states.
State 1
Content = "Hello"
Save it:
Document
↓
Memento 1
Change it:
State 2
Content = "Hello World"
Save again:
Document
↓
Memento 2
Change again:
State 3
Content = "Hello World!"
Now the history contains:
History
├── Memento 2
└── Memento 1
If the user chooses Undo:
Current State
↓
Restore Memento 2
↓
"Hello World"
9. Complete C# Console Application
Let's create a simple document editor.
Step 1 – Memento
public class DocumentMemento
{
public string Content { get; }
public DocumentMemento(string content)
{
Content = content;
}
}
Step 2 – Originator
public class Document
{
public string Content { get; private set; } = string.Empty;
public void Write(string text)
{
Content += text;
}
public DocumentMemento Save()
{
return new DocumentMemento(Content);
}
public void Restore(DocumentMemento memento)
{
Content = memento.Content;
}
public void Show()
{
Console.WriteLine($"Document: {Content}");
}
}
Step 3 – Caretaker
public class DocumentHistory
{
private readonly Stack<DocumentMemento> _history = new();
public void Save(DocumentMemento memento)
{
_history.Push(memento);
}
public DocumentMemento? Undo()
{
if (_history.Count == 0)
{
return null;
}
return _history.Pop();
}
}
Step 4 – Program
var document = new Document();
var history = new DocumentHistory();
document.Write("Hello");
history.Save(document.Save());
document.Write(" World");
history.Save(document.Save());
document.Write("!");
document.Show();
var previousState = history.Undo();
if (previousState != null)
{
document.Restore(previousState);
}
document.Show();
previousState = history.Undo();
if (previousState != null)
{
document.Restore(previousState);
}
document.Show();
Output:
Document: Hello World!
Document: Hello World
Document: Hello
The flow is:
Write "Hello"
↓
Save
↓
Write " World"
↓
Save
↓
Write "!"
↓
Undo
↓
"Hello World"
↓
Undo
↓
"Hello"
10. Implementing Undo/Redo
A single stack is sufficient for Undo.
For both Undo and Redo, we typically need two stacks:
Undo Stack
Redo Stack
Conceptually:
Current State
|
+---------+---------+
| |
Undo Stack Redo Stack
When the user changes the document:
Current
↓
Undo Stack
When Undo is performed:
Undo Stack
↓
Current
↓
Redo Stack
When Redo is performed:
Redo Stack
↓
Current
↓
Undo Stack
Complete Undo/Redo Example
public class DocumentHistory
{
private readonly Stack<DocumentMemento> _undo = new();
private readonly Stack<DocumentMemento> _redo = new();
public void Save(DocumentMemento memento)
{
_undo.Push(memento);
_redo.Clear();
}
public DocumentMemento? Undo(
DocumentMemento current)
{
if (_undo.Count == 0)
{
return null;
}
_redo.Push(current);
return _undo.Pop();
}
public DocumentMemento? Redo(
DocumentMemento current)
{
if (_redo.Count == 0)
{
return null;
}
_undo.Push(current);
return _redo.Pop();
}
}
The important concept is:
New Change
↓
Clear Redo History
This is standard undo/redo behavior.
11. Immutable Memento
A Memento should ideally be immutable.
For example:
public sealed class DocumentMemento
{
public string Content { get; }
public DocumentMemento(string content)
{
Content = content;
}
}
External code cannot change:
memento.Content
after construction.
This makes the snapshot safer and more predictable.
12. Using C# Records for Memento
Modern C# provides a convenient option using record.
public record DocumentMemento(
string Content);
The Originator can create it:
public DocumentMemento Save()
{
return new DocumentMemento(Content);
}
This is concise and works well when the snapshot is primarily data.
13. ASP.NET Core Implementation
Let's build a simplified document versioning API.
Suppose the API provides:
POST /api/documents
PUT /api/documents/{id}
POST /api/documents/{id}/undo
GET /api/documents/{id}
We can use a Memento-like snapshot model.
14. Document Model
public class Document
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
}
15. Document Snapshot
public record DocumentSnapshot(
int Id,
string Title,
string Content);
This snapshot represents a point-in-time state.
16. Document Service
public interface IDocumentService
{
Document Get(int id);
void Update(
int id,
string title,
string content);
void Undo(int id);
}
Implementation:
public class DocumentService : IDocumentService
{
private readonly Dictionary<int, Document> _documents = new();
private readonly Dictionary<
int,
Stack<DocumentSnapshot>> _history = new();
public Document Get(int id)
{
return _documents[id];
}
public void Update(
int id,
string title,
string content)
{
var document = _documents[id];
if (!_history.ContainsKey(id))
{
_history[id] =
new Stack<DocumentSnapshot>();
}
_history[id].Push(
new DocumentSnapshot(
document.Id,
document.Title,
document.Content));
document.Title = title;
document.Content = content;
}
public void Undo(int id)
{
if (!_history.ContainsKey(id) ||
_history[id].Count == 0)
{
return;
}
var snapshot = _history[id].Pop();
var document = _documents[id];
document.Title = snapshot.Title;
document.Content = snapshot.Content;
}
}
This is a simplified in-memory example.
In a production system, snapshots would generally need appropriate persistence and concurrency handling.
17. ASP.NET Core Controller
[ApiController]
[Route("api/documents")]
public class DocumentsController : ControllerBase
{
private readonly IDocumentService _service;
public DocumentsController(
IDocumentService service)
{
_service = service;
}
[HttpGet("{id}")]
public IActionResult Get(int id)
{
var document = _service.Get(id);
return Ok(document);
}
[HttpPost("{id}/undo")]
public IActionResult Undo(int id)
{
_service.Undo(id);
return Ok(new
{
Message = "Document restored successfully."
});
}
}
The controller doesn't need to know how snapshots are managed.
18. Register the Service
In Program.cs:
builder.Services.AddSingleton<IDocumentService,
DocumentService>();
For a real database-backed implementation, the lifetime and persistence strategy should be designed according to the application's requirements rather than blindly using Singleton.
19. Banking Transaction Example
The Memento Pattern can be useful when an application needs to capture a business object's state before performing a change.
Consider:
Bank Account
|
+-- Balance
+-- Status
+-- Account Type
Before a complex operation:
Account State
↓
Create Snapshot
↓
Perform Operation
If the operation needs to be reversed at the application level:
Snapshot
↓
Restore
For example:
public record AccountMemento(
decimal Balance,
string Status);
Originator:
public class BankAccount
{
public decimal Balance { get; private set; }
public string Status { get; private set; }
= "Active";
public AccountMemento Save()
{
return new AccountMemento(
Balance,
Status);
}
public void Restore(
AccountMemento memento)
{
Balance = memento.Balance;
Status = memento.Status;
}
public void Deposit(decimal amount)
{
Balance += amount;
}
}
The pattern can preserve application-level state.
However, it is important to distinguish this from a database transaction.
20. Memento vs Database Transaction
These concepts are not interchangeable.
Memento
Used to preserve and restore object/application state.
Object
↓
Snapshot
↓
Restore
Database Transaction
Used to ensure atomic database operations.
BEGIN TRANSACTION
↓
UPDATE
↓
INSERT
↓
COMMIT
If something fails:
ROLLBACK
Therefore:
Use database transactions for database atomicity and Memento when you need application-level state snapshots.
21. Document Version History
One of the best real-world examples is document versioning.
Suppose:
Document
Version 1
Version 2
Version 3
Version 4
The application can store snapshots:
Version 1 → Snapshot
Version 2 → Snapshot
Version 3 → Snapshot
Version 4 → Current
The user can then restore an earlier version.
This concept appears in many applications involving:
Documents
Configuration
Templates
Content management
Workflow definitions
22. Configuration Snapshot Example
Imagine an application configuration:
public class ApplicationSettings
{
public int MaxRetries { get; set; }
public int TimeoutSeconds { get; set; }
public bool EnableFeatureX { get; set; }
}
Before changing configuration:
Current Configuration
↓
Create Snapshot
↓
Apply New Configuration
If the new configuration causes problems:
Snapshot
↓
Restore
This is useful in administrative systems where users can experiment with configuration changes.
23. Memento and Serialization
Sometimes snapshots are persisted as:
JSON
XML
Binary
Database Records
For example:
public record ConfigurationMemento(
int MaxRetries,
int TimeoutSeconds,
bool EnableFeatureX);
The snapshot could be serialized into a persistence store.
However, if the state is large, storing complete snapshots for every change can become expensive.
That leads to an important design consideration:
Memento trades memory/storage for simpler state restoration.
24. Large Object State and Memory Considerations
Suppose an application creates:
1,000 snapshots
and each snapshot is:
5 MB
The storage requirement could become significant.
Therefore, Memento should be carefully designed for large objects.
Possible alternatives include:
Store only changed properties.
Store version identifiers.
Use database versioning.
Use event sourcing.
Store compressed snapshots.
Limit history size.
25. Memento vs Command
These patterns are frequently used together.
Command
Represents an operation:
DeleteDocumentCommand
UpdateOrderCommand
ChangeAddressCommand
Memento
Stores state:
Before State
After State
They can work together for Undo.
Command
↓
Execute
↓
Memento
↓
Undo
For example:
UpdateDocumentCommand
↓
Save Memento
↓
Update Document
↓
Execute
Undo:
Memento
↓
Restore
26. Memento vs Prototype
Both involve copying state, but they have different purposes.
| Memento | Prototype |
|---|---|
| Saves state for later restoration | Creates new objects by cloning |
| Primarily supports undo/rollback | Primarily supports object creation |
| Behavioral pattern | Creational pattern |
| Focuses on state history | Focuses on object creation |
Memento:
Object
↓
Snapshot
↓
Restore
Prototype:
Existing Object
↓
Clone
↓
New Object
27. Memento vs Snapshot
The word "snapshot" is often used informally for the state stored by a Memento.
However, the design pattern provides a broader structure:
Originator
↓
Memento
↓
Caretaker
A snapshot can simply mean a saved representation of state.
Memento is specifically about encapsulating an object's state so it can be restored later without exposing its internals.
28. Advantages of Memento Pattern
1. Preserves Encapsulation
The Caretaker doesn't need to know the internal implementation of the Originator.
2. Supports Undo
One of the most common applications.
Edit
↓
Save
↓
Edit
↓
Undo
3. Supports Version History
Multiple states can be stored.
Version 1
Version 2
Version 3
Version 4
4. Simplifies State Restoration
The Originator itself knows how to restore its state.
5. Works Well with Immutable Snapshots
C# records and immutable objects make Memento implementations safer.
6. Separates State Management
The Caretaker can manage history without understanding business internals.
29. Disadvantages of Memento Pattern
1. Memory Usage
Multiple snapshots can consume significant memory.
2. Storage Requirements
Persistent version history can require substantial database storage.
3. Performance
Creating snapshots of large objects can be expensive.
4. Complex Object Graphs
If an object contains nested objects:
Order
├── Customer
├── Items
│ ├── Product
│ └── Product
└── Payment
creating a safe snapshot may require careful deep-copy logic.
5. Version Compatibility
If snapshots are persisted for a long time, changes to the object model can make older snapshots difficult to restore.
30. Best Practices
1. Prefer Immutable Mementos
Use:
public record DocumentMemento(
string Content);
where appropriate.
2. Keep Memento Creation Inside the Originator
Prefer:
document.Save();
instead of allowing external classes to manually copy every field.
3. Don't Expose Internal State Unnecessarily
The Caretaker should not need to manipulate the Originator's fields.
4. Limit History Size
For example:
private const int MaxHistory = 50;
This prevents unbounded memory growth.
5. Consider Deep Copy Carefully
If the state contains mutable objects, simply copying references can create unexpected behavior.
6. Use Database Versioning for Persistent History
If version history must survive application restarts, storing snapshots in memory is insufficient.
7. Consider Differential Snapshots
Instead of saving the entire object every time:
Version 1 → Full Snapshot
Version 2 → Changes only
Version 3 → Changes only
This can reduce storage requirements.
31. Common Mistakes
Mistake 1 – Using Shallow Copies Accidentally
Consider:
public class Order
{
public List<OrderItem> Items { get; set; }
}
Copying the Order object without copying the list and its elements can leave multiple versions sharing the same mutable objects.
For state restoration, this can cause bugs.
Mistake 2 – Storing Mutable Mementos
A memento should represent a stable point-in-time state.
Prefer immutable state.
Mistake 3 – Unlimited History
This can eventually consume a large amount of memory.
Mistake 4 – Using Memento Instead of Transactions
Memento does not replace database transactions.
Mistake 5 – Saving Huge Objects Too Frequently
For example:
10 MB object
+
1000 snapshots
=
10 GB
This is a simplified example, but it illustrates why snapshot size and history limits matter.
Mistake 6 – Exposing All State to the Caretaker
If the caretaker understands every field, encapsulation has effectively been lost.
32. Real-World Enterprise Scenarios
The Memento Pattern can be useful for:
Document Management
Document
↓
Version
↓
Restore
Configuration Management
Configuration
↓
Snapshot
↓
Apply Change
↓
Rollback
Workflow Systems
Workflow State
↓
Snapshot
↓
Continue
↓
Restore
Game Development
Game State
↓
Save Point
↓
Continue
Drawing Applications
Canvas
↓
Snapshot
↓
Edit
↓
Undo
Form Editors
Form State
↓
Save
↓
Modify
↓
Undo
Business Applications
Business Object
↓
Snapshot
↓
Business Operation
↓
Restore
33. Memento in Enterprise Architecture
A simplified architecture might look like:
API
|
↓
Application
|
↓
Domain Object
|
+-----+-----+
| |
Save Restore
| |
↓ ↓
Memento ←→ History
|
↓
Persistence Store
This approach can be particularly useful when an application requires explicit state/version management.
34. Memento with Dependency Injection
Memento itself doesn't require dependency injection.
However, in ASP.NET Core, a history or snapshot repository can be injected.
For example:
public interface IDocumentHistoryRepository
{
Task SaveAsync(
int documentId,
DocumentSnapshot snapshot);
Task<DocumentSnapshot?> GetPreviousAsync(
int documentId);
}
Implementation:
public class DocumentHistoryRepository
: IDocumentHistoryRepository
{
public Task SaveAsync(
int documentId,
DocumentSnapshot snapshot)
{
// Save snapshot to database
return Task.CompletedTask;
}
public Task<DocumentSnapshot?> GetPreviousAsync(
int documentId)
{
// Retrieve previous snapshot
return Task.FromResult<DocumentSnapshot?>(
null);
}
}
The application service can then use the repository.
35. Memento and Clean Architecture
Memento can fit naturally into a Clean Architecture solution.
For example:
MyApplication
│
├── API
│
├── Application
│ ├── Commands
│ ├── Queries
│ └── Services
│
├── Domain
│ ├── Entities
│ ├── ValueObjects
│ └── Mementos
│
└── Infrastructure
├── Persistence
└── Repositories
The exact placement depends on whether the snapshot is considered domain state, application state, or infrastructure persistence.
36. Interview Questions
Beginner
1. What is the Memento Design Pattern?
It is a behavioral pattern that allows an object to save and restore its previous state without exposing its internal implementation.
2. What are the three major components?
Originator
Memento
Caretaker
3. What is an Originator?
The object whose state needs to be saved and restored.
4. What is a Memento?
An object that stores a snapshot of the Originator's state.
5. What is a Caretaker?
An object responsible for storing and managing Mementos.
Intermediate
6. Where is Memento commonly used?
Examples include:
Undo/Redo
Document versioning
Configuration rollback
Game save points
Workflow state restoration
7. How does Memento preserve encapsulation?
The Originator creates and restores its own state. The Caretaker stores the Memento without needing to know the internal representation of the Originator.
8. What is the relationship between Memento and Undo?
Memento provides the saved state required to restore the object to an earlier state.
9. What is the difference between Memento and Prototype?
Memento preserves state for restoration.
Prototype clones an existing object to create another object.
10. Can Memento support Redo?
Yes.
A common implementation uses:
Undo Stack
Redo Stack
37. Advanced Interview Questions
11. What are the disadvantages of Memento?
The primary concerns are:
Memory usage
Storage requirements
Snapshot creation cost
Deep-copy complexity
Version compatibility
12. Does Memento replace database transactions?
No.
Memento manages application/object state.
Database transactions provide atomicity and consistency for database operations.
13. What happens if the object contains mutable collections?
A shallow copy may preserve references to the same collection, causing multiple states to share mutable data.
A suitable deep-copy or immutable-state strategy may be required.
14. How would you optimize Memento for large objects?
Possible approaches include:
Differential snapshots
Compression
Version identifiers
Limited history
Persistent snapshots
Event sourcing
Periodic full snapshots with incremental changes
15. Why should a Memento ideally be immutable?
Because a snapshot should represent a stable point-in-time state.
If the snapshot can change after being stored, history becomes unpredictable.
16. How would you implement Memento in ASP.NET Core?
Possible approaches include:
Domain Object
↓
Create Snapshot
↓
Application Service
↓
Snapshot Repository
↓
Database
17. How does Memento relate to Event Sourcing?
Both can preserve historical state, but they work differently.
Memento typically stores snapshots.
Event Sourcing stores the sequence of events that caused state changes.
They can also be combined.
18. When would you choose Event Sourcing instead of Memento?
If the application needs a complete, auditable history of business events, Event Sourcing may be more appropriate.
If the main requirement is simply restoring previous object state, Memento may be simpler.
38. Memento vs Event Sourcing
This distinction is especially important in enterprise applications.
Memento
State 1
State 2
State 3
The system stores snapshots.
Event Sourcing
AccountCreated
MoneyDeposited
MoneyWithdrawn
AddressChanged
The system stores events.
Current state can be reconstructed by replaying events.
Therefore:
Memento
→ Save state
Event Sourcing
→ Save changes/events
39. Practical Example – Document Editor
Let's put everything together.
Suppose a user writes:
Hello
The application saves:
Memento 1
User changes it:
Hello World
Save:
Memento 2
User changes it:
Hello World!
Current state:
Hello World!
The user presses Undo.
Current
Hello World!
↓
Restore
↓
Hello World
Press Undo again:
Hello World
↓
Restore
↓
Hello
This is the fundamental idea behind Memento.
40. Key Takeaways
Remember these important points:
1. Memento is a Behavioral Design Pattern
It focuses on managing and restoring object state.
2. It has three major components
Originator
Memento
Caretaker
3. Originator owns the state
It creates and restores Mementos.
4. Caretaker manages history
It stores snapshots without understanding their internal details.
5. Memento supports Undo/Redo
Two stacks are commonly used:
Undo
Redo
6. Prefer immutable snapshots
C# records are often convenient for snapshot data.
7. Watch memory usage
Large snapshots and unlimited history can be expensive.
8. Memento doesn't replace transactions
Use database transactions for database atomicity.
9. Memento and Event Sourcing are different
Memento stores state snapshots.
Event Sourcing stores events.
10. Use it when state restoration is a real requirement
Don't introduce Memento merely because it is a design pattern.
Conclusion
The Memento Design Pattern provides a clean way to preserve and restore an object's state while keeping the object's internal implementation encapsulated.
The fundamental architecture is:
Originator
|
| Save()
↓
Memento
↑
|
Caretaker
|
| Restore()
↓
Originator
Its most common applications include:
Undo/Redo
Document versioning
Configuration rollback
Workflow restoration
Game save points
State snapshots
Application-level rollback
In modern C#, immutable records make snapshot objects particularly convenient:
public record DocumentMemento(
string Content);
And in enterprise ASP.NET Core applications, Memento can be combined with:
ASP.NET Core
↓
Clean Architecture
↓
Application Services
↓
Domain Objects
↓
Memento / Snapshots
↓
Persistence
The most important lesson is:
Memento allows an object to preserve a point-in-time snapshot of its state and restore that state later without exposing the object's internal implementation details.
When used carefully, it provides an elegant foundation for undo/redo, versioning, and state restoration. However, for large or persistent histories, developers should consider storage costs, immutable snapshots, differential snapshots, database versioning, or Event Sourcing.
🚀 Coming Up Next: Part 4.7 – Observer Design Pattern
In the next article, we'll explore the Observer Design Pattern, including:
What is the Observer Pattern?
Why do we need it?
Subject and Observer concepts
One-to-many dependency
UML Class Diagram
Complete C# Console Application
Events and Delegates in C#
IObservable<T>andIObserver<T>ASP.NET Core implementation
Event-driven architecture
Notification systems
Stock price monitoring
Banking transaction notifications
Order status notifications
Real-world enterprise scenarios
Advantages and disadvantages
Best practices
Common mistakes
Observer vs Mediator
Observer vs Pub/Sub
Observer vs Event-Driven Architecture
Interview questions
The Observer Pattern is especially important in modern .NET development because it provides the conceptual foundation for C# events, notifications, reactive programming, UI updates, and event-driven systems.

