Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Kiến trúc Hệ thống

Overview

System Architecture là thiết kế tổng thể của hệ thống, bao gồm cách các components tổ chức, giao tiếp, và tương tác với nhau. Nó định nghĩa cấu trúc, behavior, và mối quan hệ giữa các phần của hệ thống.

Types of System Architectures

1. Monolithic Architecture

Toàn bộ ứng dụng chạy trong một process duy nhất.

// Single deployment unit
public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }
    
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();  // All in one
            });
}

2. Client-Server Architecture

Phân chia giữa client (frontend) và server (backend).

┌─────────────┐         ┌─────────────┐
│   Client    │────────▶│   Server    │
│  (Browser)  │◀────────│  (API)      │
└─────────────┘         └─────────────┘

3. Microservices Architecture

Chia ứng dụng thành các services nhỏ, độc lập.

┌────────────┐  ┌────────────┐  ┌────────────┐
│   Order   │  │   Product  │  │   User    │
│  Service  │  │  Service   │  │  Service  │
└─────┬──────┘  └─────┬──────┘  └─────┬──────┘
      │               │               │
      └───────────────┼───────────────┘
                     │
              ┌──────┴──────┐
              │ API Gateway │
              └─────────────┘
// Service A - Orders
public class OrderService : IOrderService
{
    public async Task<Order> GetOrderAsync(Guid id)
    {
        // Only handles order logic
    }
}

// Service B - Products (separate deployment)
public class ProductService : IProductService
{
    public async Task<Product> GetProductAsync(Guid id)
    {
        // Only handles product logic
    }
}

4. Event-Driven Architecture

Components giao tiếp qua events.

Producer ──Events──▶ Broker ──Events──▶ Consumers

5. Layered Architecture

┌────────────────────┐
│  Presentation      │  (Controllers, UI)
├────────────────────┤
│  Application       │  (Services, Use Cases)
├────────────────────┤
│  Domain           │  (Entities, Business Rules)
├────────────────────┤
│  Infrastructure    │  (Repositories, External APIs)
└────────────────────┘

Architectural Patterns

CQRS Pattern

// Separate read and write models
public interface ICommandHandler<TCommand> { }
public interface IQueryHandler<TQuery, TResult> { }

Repository Pattern

public interface IRepository<T>
{
    Task<T> GetByIdAsync(Guid id);
    Task<IEnumerable<T>> GetAllAsync();
    Task AddAsync(T entity);
    Task UpdateAsync(T entity);
    Task DeleteAsync(T entity);
}

Unit of Work Pattern

public interface IUnitOfWork
{
    Task SaveChangesAsync();
    IRepository<Order> Orders { get; }
    IRepository<Product> Products { get; }
    IRepository<Customer> Customers { get; }
}

Design Principles

1. High Cohesion, Low Coupling

// High cohesion - related things together
public class Order
{
    public Guid Id { get; private set; }
    public List<OrderItem> Items { get; private set; }
    public decimal Total => Items.Sum(i => i.Price * i.Quantity);
}

// Low coupling - depend on abstractions
public class OrderService
{
    private readonly IOrderRepository _repository;
    private readonly INotificationService _notification;
    
    public OrderService(
        IOrderRepository repository,
        INotificationService notification)
    {
        _repository = repository;
        _notification = notification;
    }
}

2. Dependency Inversion

// Depend on abstractions, not concretions
public interface IOrderRepository { }

public class OrderService
{
    private readonly IOrderRepository _repository;  // Interface
}

public class EfOrderRepository : IOrderRepository { }  // Implementation

3. Single Responsibility

// Each class has one reason to change
public class Order { }  // Only represents order data
public class OrderService { }  // Only handles order logic
public class OrderRepository { }  // Only handles data access

Scalability Patterns

1. Horizontal vs Vertical Scaling

// Vertical Scaling - bigger machine
services.AddSingleton<IHostApplicationLifetime>(host => host);

// Horizontal Scaling - more machines
// Load balancer distributes requests

2. Caching

// Distributed cache
services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "localhost:6379";
});

// Using cache
public async Task<Product> GetProductAsync(Guid id)
{
    var cacheKey = $"product:{id}";
    var cached = await _cache.GetAsync<Product>(cacheKey);
    
    if (cached != null)
        return cached;
    
    var product = await _repository.GetByIdAsync(id);
    await _cache.SetAsync(cacheKey, product);
    
    return product;
}

3. Database Sharding

// Sharding by user ID
public class ShardedOrderRepository : IOrderRepository
{
    public async Task<Order> GetByIdAsync(Guid id)
    {
        var shardId = GetShardId(id);
        var context = GetContextForShard(shardId);
        
        return await context.Orders.FindAsync(id);
    }
}

Reliability Patterns

1. Circuit Breaker

public class CircuitBreaker
{
    private int _failureCount;
    private const int Threshold = 5;
    private CircuitState _state = CircuitState.Closed;
    
    public async Task<T> ExecuteAsync<T>(Func<Task<T>> action)
    {
        if (_state == CircuitState.Open)
            throw new CircuitOpenException();
            
        try
        {
            var result = await action();
            _failureCount = 0;
            return result;
        }
        catch
        {
            _failureCount++;
            if (_failureCount >= Threshold)
                _state = CircuitState.Open;
            throw;
        }
    }
}

2. Retry with Exponential Backoff

public async Task<T> ExecuteWithRetryAsync<T>(Func<Task<T>> action)
{
    var retryCount = 0;
    var maxRetries = 3;
    
    while (retryCount < maxRetries)
    {
        try
        {
            return await action();
        }
        catch (Exception ex)
        {
            retryCount++;
            if (retryCount >= maxRetries)
                throw;
                
            var delay = TimeSpan.FromSeconds(Math.Pow(2, retryCount));
            await Task.Delay(delay);
        }
    }
    
    throw new Exception("Max retries exceeded");
}

Communication Patterns

1. Synchronous (REST/gRPC)

// REST API
[HttpGet("{id}")]
public async Task<Order> GetOrder(Guid id)
{
    return await _orderService.GetOrderAsync(id);
}

// gRPC
public class OrderService : OrderServiceBase
{
    public override async Task<OrderResponse> GetOrder(
        OrderRequest request, ServerCallContext context)
    {
        return await _orderRepository.GetByIdAsync(Guid.Parse(request.Id));
    }
}

2. Asynchronous (Message Queue)

// Publish event
await _messageBus.PublishAsync(new OrderCreatedEvent(order));

// Subscribe to events
public class OrderCreatedHandler : IMessageHandler<OrderCreatedEvent>
{
    public async Task Handle(OrderCreatedEvent message)
    {
        await _notificationService.SendAsync(message.CustomerEmail);
    }
}

Monitoring and Observability

// Health checks
public class OrderServiceHealthCheck : IHealthCheck
{
    public async Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context, CancellationToken cancellationToken)
    {
        var canConnect = await _repository.CanConnectAsync();
        
        return canConnect 
            ? HealthCheckResult.Healthy("Order service is healthy")
            : HealthCheckResult.Unhealthy("Cannot connect to database");
    }
}

// Metrics
public class OrderMetrics
{
    private readonly Counter _orderCount;
    
    public OrderMetrics(IMeterFactory meterFactory)
    {
        _orderCount = meterFactory.CreateCounter("orders.created");
    }
    
    public void RecordOrderCreated() => _orderCount.Add(1);
}

Choosing Right Architecture

FactorRecommended Architecture
Small team, simple appMonolithic
Large team, complex domainMicroservices
Real-time processingEvent-Driven
High read/write ratioCQRS
Frequent changesModular Monolith

References