Complete Gang of Four (GoF) & Architecture Design Patterns: Angular & .NET C#
All Creational, Structural, Behavioral & Senior/PM Enterprise System Architectures

0. SOLID Principles (Fundamental Software Engineering Architecture)

SOLID Principle — S

0.1 Single Responsibility Principle (SRP)

Core Rule: "A class should have one, and only one, reason to change."

🏛️ Real-Life Metaphor: Restaurant Operations.
A Chef cooks meals, a Waiter serves food, and an Accountant manages finances. If a single employee cooks meals, takes customer orders, washes dishes, and files corporate tax returns, the restaurant collapses when customer volume grows!

Deep Explanation & Architectural Reasoning:
SRP is about cohesion and responsibility actors. A class has a single responsibility if it serves only one actor (e.g., UI Team, Accounting Team, or DB Admin). When a single class handles UI rendering, HTTP network requests, data validation, and PDF generation, a change requested by Accounting risks breaking the UI or data layer unexpectedly.

❌ Anti-Pattern (Violating SRP): Single monolithic InvoiceComponent handling API calls, tax math, local storage caching, and PDF generation.
// ❌ BAD: InvoiceComponent has 5 separate reasons to change!
@Component({ selector: 'app-invoice', template: `...` })
export class InvoiceComponent implements OnInit {
  invoiceItems: any[] = [];
  totalAmount = 0;

  ngOnInit() {
    // Reason 1: API Endpoint changes
    fetch('/api/invoices/current').then(r => r.json()).then(data => {
      this.invoiceItems = data.items;
      // Reason 2: Tax formula changes
      let sub = this.invoiceItems.reduce((sum, i) => sum + (i.price * i.qty), 0);
      this.totalAmount = sub + (sub * 0.18);
      // Reason 3: Caching requirement changes
      localStorage.setItem('last_inv_total', this.totalAmount.toString());
    });
  }

  // Reason 4: PDF formatting changes
  downloadPDF() { const doc = new jsPDF(); doc.text(`Total: ${this.totalAmount}`, 10, 10); doc.save('inv.pdf'); }
}
✅ Clean Senior Architecture (Adhering to SRP): Split into 4 distinct single-responsibility services (Data API Service, Tax Calculator Service, PDF Exporter Service, UI Presentation Component).

🅰️ Angular Refactored SRP Services:

// 1. Data Layer
@Injectable({ providedIn: 'root' })
export class InvoiceApiService {
  private api = inject(ApiInterfaceService);
  getInvoice() { return this.api.get<Invoice>('invoices/current'); }
}

// 2. Business Logic Layer
@Injectable({ providedIn: 'root' })
export class TaxCalculatorService {
  calculateTotalWithTax(items: InvoiceItem[], vatRate = 0.18): number {
    const subtotal = items.reduce((s, i) => s + (i.price * i.qty), 0);
    return subtotal + (subtotal * vatRate);
  }
}

🔷 .NET Refactored SRP Architecture:

// 1. Repository Interface
public interface IInvoiceRepository {
  Task<Invoice> GetByIdAsync(Guid id);
}

// 2. Domain Tax Calculator
public class TaxDomainService {
  public decimal CalculateVat(decimal subtotal, decimal rate = 0.18m) 
    => subtotal + (subtotal * rate);
}

// 3. PDF Generator Service
public class PdfInvoiceGenerator : IPdfInvoiceGenerator { ... }
SOLID Principle — O

0.2 Open/Closed Principle (OCP)

Core Rule: "Software entities should be open for extension, but closed for modification."

🔌 Real-Life Metaphor: Wall Electrical Outlet.
The wall socket is closed for modification (you don't rip open the drywall and rewire your house's copper cables every time you buy a new electronic device). It is open for extension (you plug in a lamp, TV, or laptop as long as it has a standard plug).

Deep Explanation & Architectural Reasoning:
Modifying existing source code introduces high regression risks — every edit forces re-testing all existing workflows across the system. By leveraging polymorphism, dependency injection, and abstraction contracts, new capabilities are added by creating new classes without editing existing working code.

❌ Anti-Pattern (Violating OCP): Using a switch/case or if/else block that requires modifying the class every time a new carrier or payment method is added.
// ❌ BAD: Adding a new carrier (Aramex) requires modifying existing source code!
export class ShippingCostCalculator {
  calculateCost(carrier: string, weight: number): number {
    switch (carrier) {
      case 'DHL': return weight * 12.5 + 5.0;
      case 'FedEx': return weight * 14.0 + 3.0;
      case 'Aramex': // <-- Modifying existing code risks breaking DHL & FedEx!
        return weight * 10.0 + 4.0;
      default: throw new Error(`Unsupported carrier: ${carrier}`);
    }
  }
}
✅ Clean Polymorphic Solution (Adhering to OCP): Define an abstract contract interface. New carriers implement the interface cleanly without touching existing code.

🅰️ Angular OCP Strategy Implementation:

export interface IShippingStrategy {
  readonly carrierName: string;
  calculateFee(weightKg: number): number;
}

@Injectable({ providedIn: 'root' })
export class DhlStrategy implements IShippingStrategy {
  readonly carrierName = 'DHL';
  calculateFee(w: number) { return w * 12.5 + 5.0; }
}

@Injectable({ providedIn: 'root' })
export class AramexStrategy implements IShippingStrategy {
  readonly carrierName = 'Aramex'; // NEW class! Zero edits to DHL!
  calculateFee(w: number) { return w * 10.0 + 4.0; }
}

🔷 .NET C# OCP Implementation:

public interface ICourierStrategy {
  string ProviderName { get; }
  decimal CalculateRate(decimal weight);
}

public class FedExService : ICourierStrategy {
  public string ProviderName => "FedEx";
  public decimal CalculateRate(decimal w) => w * 14.0m + 3.0m;
}
SOLID Principle — L

0.3 Liskov Substitution Principle (LSP)

Core Rule: "Subtypes must be substitutable for their base types without altering correctness or throwing unexpected errors."

🔋 Real-Life Metaphor: AA Battery Replaceability.
A remote control expects a standard 1.5V AA Battery. Whether you insert a Duracell, Energizer, or rechargeable AA NiMH battery, the device turns on and functions normally. If a brand labeled "AA" exploded when inserted because it required 240V mains power, it would violate Liskov Substitution!

Deep Explanation & Architectural Reasoning:
LSP guarantees that any subclass can be substituted wherever its parent class is expected without breaking caller code. Overriding a base method to throw NotImplementedException or throwing errors on valid inputs violates LSP.

❌ Anti-Pattern (Violating LSP): Derived subclass overriding a method to throw an error because it cannot support the base contract.
// ❌ BAD: Subclass throws an error for base class method withdraw()
export class BankAccount {
  protected balance = 0;
  withdraw(amount: number) { this.balance -= amount; }
}

export class FixedDepositAccount extends BankAccount {
  override withdraw(amount: number) {
    // 💥 VIOLATION: Throws runtime error breaking code that expects BankAccount!
    throw new Error("Withdrawals are locked on Fixed Deposit accounts!");
  }
}
✅ Clean LSP Solution: Segregate account hierarchy so withdrawal capability is a specific contract guaranteed not to throw stubs.

🅰️ Angular / TypeScript LSP Refactoring:

export abstract class BaseAccount {
  protected balance = 0;
  getBalance() { return this.balance; }
}

export abstract class TransactionalAccount extends BaseAccount {
  abstract withdraw(amount: number): void;
}

export class CheckingAccount extends TransactionalAccount {
  withdraw(amt: number) { this.balance -= amt; }
}

export class FixedDepositAccount extends BaseAccount {
  // Does NOT inherit withdraw() -> Cannot break caller contracts!
}

🔷 .NET C# LSP Hierarchy:

public abstract class Bird { public abstract void Eat(); }
public interface IFlyingBird { void Fly(); }

public class Eagle : Bird, IFlyingBird {
  public override void Eat() { ... }
  public void Fly() { ... }
}

public class Ostrich : Bird {
  public override void Eat() { ... } // No Fly() stub!
}
SOLID Principle — I

0.4 Interface Segregation Principle (ISP)

Core Rule: "Clients should not be forced to depend upon interfaces that they do not use."

🏨 Real-Life Metaphor: Hotel Keycard.
A guest gets a keycard programmed *only* for Room 304 and the Gym. A housekeeper gets a keycard programmed for all guest rooms on Floor 3. If the hotel handed every guest a master keycard with 50 buttons for the boiler room, master vault, and kitchen ovens, it would cause mass confusion and severe security risks.

Deep Explanation & Architectural Reasoning:
ISP prevents bloated "Fat Interfaces". Large monolithic interfaces force implementing classes to write dummy stubs or throw errors for methods they do not care about. Creating small, role-specific interfaces keeps components lean and decoupled.

❌ Anti-Pattern (Violating ISP): A monolithic interface forcing basic implementations to write empty or error-throwing stubs.
// ❌ BAD: Fat Interface with 5 unrelated methods
export interface IEnterpriseDevice {
  print(docId: string): void;
  scan(): void;
  fax(num: string): void;
  ocrExtract(): string;
}

export class BasicPrinter implements IEnterpriseDevice {
  print(id: string) { console.log("Printing..."); }
  scan() { throw new Error("No scanner!"); } // Forced stub!
  fax(n: string) { throw new Error("No fax!"); }   // Forced stub!
  ocrExtract() { throw new Error("No OCR!"); }   // Forced stub!
}
✅ Clean ISP Solution: Decompose into role-specific micro-interfaces.

🅰️ Angular / TypeScript ISP Interfaces:

export interface IPrinter { print(docId: string): void; }
export interface IScanner { scan(): Blob; }
export interface IFax { fax(num: string, doc: Blob): void; }

// Basic Desktop Printer depends ONLY on IPrinter
export class BasicPrinter implements IPrinter {
  print(id: string) { console.log(`Printing: ${id}`); }
}

🔷 .NET C# Role Interfaces:

public interface IReadRepository<T> {
  Task<T?> GetByIdAsync(Guid id);
}

public interface IWriteRepository<T> {
  Task AddAsync(T entity);
  Task DeleteAsync(Guid id);
}

// Read-only view model service implements ONLY IReadRepository
public class UserQueryService : IReadRepository<UserDto> { ... }
SOLID Principle — D

0.5 Dependency Inversion Principle (DIP)

Core Rule: "High-level modules should not depend on low-level modules. Both should depend on abstractions."

💡 Real-Life Metaphor: Lamp and Wall Power Outlet.
A lamp (high-level module) does not solder its copper wires directly into the power plant's generator (low-level detail). Both depend on an Abstract Wall Socket Interface (230V AC plug format). You can swap grid electricity for a solar generator without changing a single wire inside your lamp!

Deep Explanation & Architectural Reasoning:
DIP decouples core business logic from concrete technical infrastructure (SQL DBs, Axios, LocalStorage). High-level modules request interfaces via Dependency Injection, making the codebase 100% unit-testable with mock implementations.

❌ Anti-Pattern (Violating DIP): High-level business service instantiating concrete low-level drivers directly using new.
// ❌ BAD: OrderService tightly coupled to concrete Axios driver and SqlLogger
import { SqlLogger } from './sql-logger';
import { AxiosDriver } from './axios-driver';

export class OrderService {
  private logger = new SqlLogger();    // Tight coupling!
  private http = new AxiosDriver();     // Tight coupling!

  placeOrder(order: any) {
    this.logger.log(`Placing: ${order.id}`);
    this.http.post('/api/orders', order);
  }
}
✅ Clean DIP Solution: High-level service depends on abstraction contracts injected via Dependency Injection framework.

🅰️ Angular DIP Implementation:

@Injectable({ providedIn: 'root' })
export class OrderService {
  // Angular DI injects abstract ApiInterfaceService
  private api = inject(ApiInterfaceService);
  private notify = inject(NotificationService);

  placeOrder(order: OrderPayload): Observable<OrderResult> {
    return this.api.post<OrderResult>('orders', order);
  }
}

🔷 .NET C# DIP Registration:

// Depend on IOrderRepository abstraction
public class OrderCommandHandler {
  private readonly IOrderRepository _repository;
  public OrderCommandHandler(IOrderRepository repo) => _repository = repo;
}

// In Program.cs: Register abstraction to concrete
builder.Services.AddScoped<IOrderRepository, EfOrderRepository>();

1. Creational Design Patterns (Object Creation)

Creational Pattern

1.1 Singleton Pattern

🏛️ Real-Life Metaphor: The President of a Country.
There is only ONE single instance running executive power at any time. Every citizen interacts with that same single instance.

🅰️ Angular Implementation:

@Injectable({ providedIn: 'root' })
export class AuthService {
  private user = signal<User | null>(null);
}

🔷 .NET C# Implementation:

builder.Services.AddSingleton<ICacheService, MemoryCacheService>();
Creational Pattern

1.2 Factory Method / Abstract Factory Pattern

🏭 Real-Life Metaphor: Vehicle Factory.
Ask for "Electric" ➡️ returns Tesla. Ask for "Heavy" ➡️ returns Truck. You don't care how it is manufactured internally!

🅰️ Angular Notification Factory:

getProvider(type: 'email' | 'sms') {
  return type === 'email' ? inject(EmailService) : inject(SmsService);
}

🔷 .NET Payment Gateway Factory:

public IPaymentProcessor Create(PaymentMethod m) => m switch {
  PaymentMethod.Stripe => new StripeProcessor(),
  PaymentMethod.PayPal => new PayPalProcessor()
};
Creational Pattern

1.3 Builder Pattern

🥪 Real-Life Metaphor: Subway Sandwich.
Construct step-by-step: Bread ➡️ Meat ➡️ Cheese ➡️ Veggies ➡️ `.Build()`.

🅰️ Angular FormBuilder:

this.form = this.fb.group({ title: [''], amount: [0] });

🔷 .NET WebApplicationBuilder:

var app = WebApplication.CreateBuilder(args).Build();
Creational Pattern

1.4 Prototype Pattern (Cloning)

📄 Real-Life Metaphor: Document Photocopying.
Instead of typing a 50-page legal contract from scratch every time, you photocopy (clone) an existing master template and modify minor fields!

🅰️ Angular / JS Implementation:

const clonedOffer = structuredClone(masterOfferTemplate);

🔷 .NET C# Implementation:

public class Invoice : ICloneable {
  public object Clone() => this.MemberwiseClone();
}

2. Structural Design Patterns (Composition & Interfaces)

Structural Pattern

2.1 Adapter Pattern

🔌 Real-Life Metaphor: Travel Plug Adapter.
Adapts a US 2-pin flat plug to fit a European 3-pin round wall socket.

🅰️ Angular Model Adapter:

adapt(dto: any): UserModel {
  return new UserModel(dto.user_id, dto.first_name);
}

🔷 .NET Third-Party Adapter:

public class SoapEmailAdapter : IEmailSender { ... }
Structural Pattern

2.2 Decorator Pattern

📱 Real-Life Metaphor: Phone Case.
Adds drop protection dynamically without changing internal phone hardware.

🅰️ Angular Decorator:

@Component({ selector: 'app-card', ... })

🔷 .NET Caching Decorator:

public class CachedRepository : IRepository { ... }
Structural Pattern

2.3 Facade Pattern

🎛️ Real-Life Metaphor: Home Theater Universal Remote.
Press 1 button *"Watch Movie"*, it controls TV, Soundbar, and Lights behind the scenes.

🅰️ Angular State Facade:

export class OffersFacade {
  readonly offers$ = this.store.select(selectOffers);
}

🔷 .NET Checkout Facade:

public async Task Checkout() {
  await _payment.Charge(); await _stock.Deduct();
}
Structural Pattern

2.4 Composite Pattern (Tree Hierarchy)

📁 Real-Life Metaphor: File System Directory Hierarchy.
A Folder can contain individual Files OR sub-folders, both implementing `FileSystemItem`.

🅰️ Angular Recursive Tree Component:

<app-tree-node *ngFor="let node of node.children" [node]="node" />

🔷 .NET Organizational Hierarchy:

public class Manager : IEmployee {
  private List<IEmployee> _subordinates;
}

3. Behavioral Design Patterns (Algorithms & Communications)

Behavioral Pattern

3.1 Observer Pattern

🔔 Real-Life Metaphor: YouTube Channel Subscribers.
New video uploads ➡️ all subscribers get push notifications automatically.

🅰️ Angular RxJS / Signals:

this.userSubject$.subscribe(user => ...);

🔷 .NET MediatR Domain Events:

public class SendEmailOnOrderPlaced : INotificationHandler<OrderPlacedEvent>
Behavioral Pattern

3.2 Strategy Pattern

🗺️ Real-Life Metaphor: GPS Navigation.
Swap strategies dynamically: Car 🚘 | Bicycle 🚲 | Walking 🚶.

🅰️ Angular Sorting Strategy:

export class SortByPriceStrategy implements ISortStrategy { ... }

🔷 .NET Payment Strategy:

public class StripePaymentStrategy : IPaymentStrategy { ... }
Behavioral Pattern

3.3 Chain of Responsibility Pattern

🎧 Real-Life Metaphor: Tech Support Tiers.
Tier 1 ➡️ Tier 2 ➡️ Senior DevOps Engineer until resolved.

🅰️ Angular HTTP Interceptors:

return next(authReq); // Passes to next interceptor

🔷 .NET Middleware Pipeline:

await _next(context); // Passes to next middleware
Behavioral Pattern

3.4 Mediator Pattern

✈️ Real-Life Metaphor: Air Traffic Control (ATC) Tower.
Airplanes do not communicate directly with each other; all communication goes through the Central Control Tower.

🅰️ Angular Parent Component / Event Bus:

this.eventBus.emit('FILTER_CHANGED', filterValue);

🔷 .NET MediatR Command Dispatcher:

await _mediator.Send(new CreateOrderCommand(dto));
Behavioral Pattern

3.5 State Pattern (Finite State Machine)

🚥 Real-Life Metaphor: Traffic Light System.
Behavior of `changeLight()` changes dynamically based on current internal state (`RedState` ➡️ `GreenState`).

🅰️ Angular Order Workflow FSM:

export class DraftState implements OrderState { ... }

🔷 .NET C# State Machine:

public class ShippedState : IOrderState { ... }
Behavioral Pattern

3.6 Template Method Pattern

🍰 Real-Life Metaphor: Baking Cake Recipe Template.
Base recipe defines high-level steps: Prep ➡️ Mix Ingredients (subclass defines details) ➡️ Bake.

🅰️ Angular Abstract Base Component:

export abstract class BaseGridComponent { ... }

🔷 .NET C# Abstract Base Service:

public abstract class DataProcessor { ... }

4. Senior & PM Level Enterprise System Architectures

Senior / PM Frontend Architecture

4.1 Angular Facade Service Pattern (Decoupling Components from Direct Services)

🛎️ The Luxury Hotel Receptionist Analogy:
When you stay at a 5-star hotel, you don't call the laundry guy, the kitchen chef, the pool maintenance team, and the valet driver directly yourself. You talk to ONE person: The Front Desk Receptionist (Facade)! The Receptionist coordinates all 4 internal departments behind the scenes and hands you a clean result.

Without Facade Pattern: Component constructor injects 6 services (`OfferService`, `VendorService`, `AuthService`, `NotificationService`, `Router`, `MatDialog`). Component code becomes 500 lines of messy glue logic! ❌
With Facade Pattern: Component injects ONLY 1 service (`OffersFacadeService`). Component code becomes 30 lines of pure UI logic! ✅

1. Bad Practice (Direct Injection Spaghetti):

// ❌ Messy Component injecting 5 services directly:
constructor(
  private offerService: OfferService,
  private vendorService: VendorService,
  private authService: AuthService,
  private notifyService: NotificationService,
  private router: Router
) {}

2. Senior Best Practice: The Facade Service Layer (`offers.facade.ts`):

@Injectable({ providedIn: 'root' })
export class OffersFacadeService {
  // 1. Encapsulate all internal services & RxJS/Signal streams inside Facade:
  private readonly offerService = inject(OfferService);
  private readonly vendorService = inject(VendorService);
  private readonly notifyService = inject(NotificationService);

  // 2. Expose clean public reactive state to HTML components:
  readonly offers = this.offerService.offersList;
  readonly isLoading = this.offerService.loadingState;

  // 3. Expose high-level user action methods:
  loadOffersForCurrentVendor(): void {
    this.vendorService.currentVendor$.pipe(
      switchMap(vendor => this.offerService.getByVendor(vendor.id))
    ).subscribe();
  }
}

3. Clean Component Template Usage (`offers.component.ts`):

@Component({ ... })
export class OffersComponent {
  // ✅ Component injects ONLY 1 Facade Service! Super clean & 100% easy to test!
  readonly facade = inject(OffersFacadeService);

  ngOnInit() {
    this.facade.loadOffersForCurrentVendor();
  }
}
Senior / PM Backend Architecture

4.2 Clean Architecture / Onion Architecture (.NET C# Backend)

🧅 The Onion Layer Analogy:
An Onion has layers protecting the central core seed. In Clean Architecture, the **Domain Business Rules are the core seed** in the center. All outer layers (Database, Web APIs, Third-Party SDKs) depend INWARD toward the core seed. The core business rules NEVER depend on external databases or UI frameworks!
Onion Layer Project Name in .NET Solution What Code Belongs Here?
1. Domain Core
(Center Seed)
LP2.Domain.csproj Pure C# Entities, Value Objects, Domain Exceptions, Repository Interfaces. Zero external NuGet packages!
2. Application Layer
(Use Cases)
LP2.Application.csproj CQRS Commands & Queries (MediatR), DTOs, FluentValidation rules, Application Interfaces.
3. Infrastructure Layer
(External Details)
LP2.Infrastructure.csproj Entity Framework Core `DbContext`, SQL DB Migrations, SendGrid Email SDK, Redis Cache implementation.
4. Presentation Layer
(Outer Shell)
LP2.WebApi.csproj ASP.NET Core Web API Controllers, Minimal API endpoints, Swagger UI, Middleware.
Senior Frontend Architecture

4.3 Smart (Container) vs Presentational (Dumb) Component Pattern

Component Type Responsibilities Services Injected? Reusability & Testing
Smart (Container) Component
OffersContainerComponent
Handles routing params, injects Facade/Services, subscribes to async state streams. Has ZERO complex HTML layout. YES (Injects Facade/Services) Specific to application feature route.
Presentational (Dumb) Component
OfferCardComponent
Pure UI template. Receives data via @Input() offer and emits user actions via @Output() onSelect. NO! Zero injected services! 100% Reusable anywhere! Easiest unit testing without mocking! ✅

5. Advanced Principal Architect System Design Patterns

Principal Backend Architecture

5.1 Transactional Outbox Pattern (Reliable Event-Driven Messaging)

📫 The Postal Mailbox Analogy:
Instead of running to the post office yourself every time you write a letter (which might fail if the post office is closed!), you drop the letter into your house's Outgoing Mailbox (Outbox Table). The mailman (Background Worker) picks up all outgoing letters periodically and delivers them guaranteed!

Problem & Principal Solution:

Problem: Saving an Order to SQL Database succeeds, but publishing the OrderPlaced event to RabbitMQ/Kafka fails due to a network glitch. Your database and message broker are now out of sync! ❌
Solution: Save both the Order Entity AND the Message Event into an OutboxMessages table inside the SAME atomic SQL Database Transaction. A background worker service (Hangfire / Quartz.NET) continuously polls the `OutboxMessages` table and publishes them to RabbitMQ reliably!

System Migration Architecture

5.2 Strangler Fig Pattern (Legacy Monolith Migration)

🌿 The Strangler Fig Tree Analogy:
A Strangler Fig vine seeds in the upper branches of a massive old oak tree. Over years, it grows roots down around the trunk, gradually replacing the old tree until the new tree stands completely on its own!

Principal Architect Solution:

Never attempt a "Big Bang" rewrite of a 10-year-old legacy ASP.NET WebForms / AngularJS application—it will fail. Instead, place an API Gateway / Reverse Proxy (YARP / Nginx) in front of the legacy app. Migrate 1 module at a time to Angular 21 & .NET 9. Route modern paths (`/offers`) to the new app while legacy paths (`/billing`) continue running on the old app until 100% migrated!

Resilience Architecture (.NET & Angular)

5.3 Circuit Breaker & Retry Pattern (Polly & RxJS)

The Household Fuse Box Analogy:
When a power surge occurs, the circuit breaker TRIPS OPEN to protect your home electronics from exploding. Once the surge passes, you reset the breaker.
Circuit State System Behavior Why it Saves the Server
🟢 Closed (Normal) All HTTP requests flow through normally. Everything is healthy.
🔴 Open (Tripped) If 5 consecutive API calls fail, the Circuit Breaker trips! All new requests fail-fast immediately without hitting the dead external server. Prevents 1,000 user requests from locking up server threads waiting 30 seconds for timeouts! ⚡
🟡 Half-Open (Trial) Sends 1 trial request to see if external API recovered. If success ➡️ reset to Closed! Automated self-healing recovery.

6. Enterprise Architectural Patterns Summary

Architectural Pattern

6.1 Repository, Unit of Work & CQRS Summary

Pattern Name Real-World Purpose Angular / Frontend Role .NET C# Backend Role
Repository Pattern Decouples business logic from database/API data access details. Service classes encapsulating HttpClient (`OffersService`). IRepository<T> wrapping EF Core DB contexts (`SqlRepository`).
Unit of Work Pattern Guarantees multiple database repository changes are saved in 1 single atomic DB transaction. Batch state dispatch in NgRx. IUnitOfWork.SaveChangesAsync() executing EF Core atomic transactions.
CQRS Pattern Separates Read Operations (Queries) from Write Operations (Commands) for high performance. NgRx Actions (Commands) vs Selectors (Queries). MediatR `IRequest` (`CreateOrderCommand` vs `GetOrderByIdQuery`).

Post a Comment

Previous Post Next Post