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
| Factor | Recommended Architecture |
|---|---|
| Small team, simple app | Monolithic |
| Large team, complex domain | Microservices |
| Real-time processing | Event-Driven |
| High read/write ratio | CQRS |
| Frequent changes | Modular Monolith |