- Revised the architecture documentation to include detailed diagrams and descriptions for the GoodGo Microservices Platform, enhancing clarity on system components and interactions. - Updated the .NET template documentation to reflect new naming conventions and project structures, ensuring consistency across services. - Added real-world examples and practical setup instructions for local development, including Traefik routing and environment variable configurations. - Enhanced the guide documentation with verification checklists and troubleshooting steps to support developers in deploying and managing services effectively.
537 lines
17 KiB
Markdown
537 lines
17 KiB
Markdown
# Template Microservice .NET 10
|
|
|
|
> Template microservice .NET 10 cấp doanh nghiệp theo các pattern DDD, CQRS và Clean Architecture.
|
|
|
|
## Tổng Quan
|
|
|
|
Template này cung cấp cấu trúc sẵn sàng production cho microservices .NET dựa trên kiến trúc tham chiếu eShopOnContainers với:
|
|
|
|
- **Domain-Driven Design (DDD)** - Aggregates, Entities, Value Objects, Domain Events
|
|
- **CQRS Pattern** - Tách biệt Commands (ghi) và Queries (đọc) với MediatR
|
|
- **Clean Architecture** - Phân tầng Domain, Infrastructure, API
|
|
- **EF Core 10** - PostgreSQL với connection resilience
|
|
- **FluentValidation** - Validation request
|
|
- **API Versioning** - Versioning theo URL segment
|
|
- **Health Checks** - Probes sẵn sàng cho Kubernetes
|
|
- **Structured Logging** - Serilog với console và Seq
|
|
|
|
## Yêu Cầu
|
|
|
|
| Yêu cầu | Phiên bản |
|
|
|---------|-----------|
|
|
| .NET SDK | 10.0.101+ |
|
|
| Docker | 24.0+ |
|
|
| PostgreSQL | 15+ (hoặc dùng Docker) |
|
|
|
|
## Bắt Đầu Nhanh
|
|
|
|
### 1. Tạo Service Mới
|
|
|
|
```bash
|
|
# Sao chép template sang service mới
|
|
cp -r services/_template_dot_net services/your-service-name-net
|
|
cd services/your-service-name-net
|
|
|
|
# Đổi tên tất cả "MyService" thành "YourService"
|
|
# Ví dụ: MyService → StorageService, UserService, PaymentService
|
|
find . -type f -name "*.cs" -exec sed -i '' 's/MyService/YourService/g' {} +
|
|
find . -type f -name "*.csproj" -exec sed -i '' 's/MyService/YourService/g' {} +
|
|
find . -type f -name "*.sln" -exec sed -i '' 's/MyService/YourService/g' {} +
|
|
```
|
|
|
|
**Naming Convention**:
|
|
- Service folder: `{service-name}-net` (ví dụ: `storage-service-net`, `iam-service-net`)
|
|
- Projects: `{ServiceName}.API`, `{ServiceName}.Domain`, `{ServiceName}.Infrastructure`
|
|
- DbContext: `{ServiceName}Context` (ví dụ: `StorageServiceContext`)
|
|
- Namespace: `GoodGo.Services.{ServiceName}` (ví dụ: `GoodGo.Services.StorageService`)
|
|
|
|
### 2. Cấu Hình Môi Trường
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
nano .env
|
|
```
|
|
|
|
### 3. Chạy với Docker
|
|
|
|
```bash
|
|
docker-compose up -d
|
|
docker-compose logs -f myservice-api
|
|
```
|
|
|
|
### 4. Chạy Local
|
|
|
|
```bash
|
|
dotnet restore
|
|
dotnet build
|
|
dotnet run --project src/MyService.API
|
|
```
|
|
|
|
## Cấu Trúc Dự Án
|
|
|
|
```
|
|
{service-name}-net/ # Ví dụ: storage-service-net
|
|
├── src/
|
|
│ ├── {ServiceName}.API/ # Lớp Presentation (Controllers, CQRS)
|
|
│ │ ├── Controllers/ # Các API endpoints
|
|
│ │ ├── Application/ # Triển khai CQRS
|
|
│ │ │ ├── Commands/ # Thao tác ghi (MediatR)
|
|
│ │ │ ├── Queries/ # Thao tác đọc
|
|
│ │ │ ├── Behaviors/ # MediatR pipeline behaviors
|
|
│ │ │ └── Validations/ # FluentValidation validators
|
|
│ │ ├── Middlewares/ # Custom middlewares
|
|
│ │ └── Program.cs # Điểm vào ứng dụng
|
|
│ │
|
|
│ ├── {ServiceName}.Domain/ # Lớp Domain (Business logic thuần túy)
|
|
│ │ ├── AggregatesModel/ # Aggregate roots và entities
|
|
│ │ ├── Events/ # Domain events
|
|
│ │ ├── Exceptions/ # Domain exceptions
|
|
│ │ └── SeedWork/ # Base classes (Entity, IAggregateRoot)
|
|
│ │
|
|
│ └── {ServiceName}.Infrastructure/ # Lớp Infrastructure
|
|
│ ├── EntityConfigurations/ # Cấu hình EF Core (Fluent API)
|
|
│ ├── Repositories/ # Triển khai repositories
|
|
│ ├── Providers/ # External service providers (nếu có)
|
|
│ ├── Services/ # Infrastructure services
|
|
│ ├── Migrations/ # EF Core migrations
|
|
│ └── {ServiceName}Context.cs # DbContext với Unit of Work
|
|
│
|
|
├── tests/
|
|
│ ├── {ServiceName}.UnitTests/ # Unit tests
|
|
│ └── {ServiceName}.FunctionalTests/ # Integration tests
|
|
│
|
|
├── docs/
|
|
│ ├── en/ # English documentation
|
|
│ │ ├── README.md
|
|
│ │ └── ARCHITECTURE.md
|
|
│ └── vi/ # Vietnamese documentation
|
|
│ ├── README.md
|
|
│ └── ARCHITECTURE.md
|
|
│
|
|
├── Dockerfile # Multi-stage Docker build
|
|
├── docker-compose.yml # Thiết lập phát triển local
|
|
└── README.md # Service overview (bilingual)
|
|
```
|
|
|
|
**Examples thực tế**:
|
|
- Storage Service: [`services/storage-service-net/`](file:///Users/velikho/Desktop/WORKING/Base/services/storage-service-net)
|
|
- IAM Service: [`services/iam-service-net/`](file:///Users/velikho/Desktop/WORKING/Base/services/iam-service-net)
|
|
- Chat Service: [`services/chat-service-net/`](file:///Users/velikho/Desktop/WORKING/Base/services/chat-service-net)
|
|
|
|
## Kiến Trúc
|
|
|
|
```mermaid
|
|
graph TB
|
|
subgraph "Lớp API"
|
|
C[Controllers]
|
|
CMD[Commands]
|
|
Q[Queries]
|
|
B[Behaviors]
|
|
V[Validations]
|
|
end
|
|
|
|
subgraph "Lớp Domain"
|
|
AR[Aggregate Roots]
|
|
E[Entities]
|
|
VO[Value Objects]
|
|
DE[Domain Events]
|
|
end
|
|
|
|
subgraph "Lớp Infrastructure"
|
|
DB[(PostgreSQL)]
|
|
R[Repositories]
|
|
CTX[DbContext]
|
|
end
|
|
|
|
C --> CMD
|
|
C --> Q
|
|
CMD --> B --> V
|
|
CMD --> AR
|
|
Q --> R
|
|
R --> CTX --> DB
|
|
|
|
style C fill:#4a90d9,stroke:#2d5986,color:#fff
|
|
style AR fill:#50c878,stroke:#2d8659,color:#fff
|
|
style DB fill:#ff6b6b,stroke:#c0392b,color:#fff
|
|
```
|
|
|
|
## Trách Nhiệm Các Lớp
|
|
|
|
### 1. Lớp Domain
|
|
|
|
- **ZERO** phụ thuộc bên ngoài (ngoại trừ MediatR.Contracts)
|
|
- Chỉ chứa các class POCO
|
|
- Triển khai các tactical patterns của DDD
|
|
|
|
| Thành phần | Mục Đích |
|
|
|------------|----------|
|
|
| **SeedWork** | Base classes: Entity, ValueObject, IAggregateRoot |
|
|
| **AggregatesModel** | Aggregate roots với entities và value objects |
|
|
| **Events** | Domain events cho giao tiếp cross-aggregate |
|
|
| **Exceptions** | Domain exceptions |
|
|
|
|
### 2. Lớp Infrastructure
|
|
|
|
- Truy cập database (EF Core)
|
|
- Triển khai repositories
|
|
- Tích hợp external services
|
|
|
|
### 3. Lớp API
|
|
|
|
- Controllers để xử lý HTTP
|
|
- Commands cho các thao tác ghi
|
|
- Queries cho các thao tác đọc
|
|
- MediatR behaviors cho cross-cutting concerns
|
|
|
|
## Pattern CQRS
|
|
|
|
### Commands (Thao Tác Ghi)
|
|
|
|
```csharp
|
|
public record CreateSampleCommand(string Name, string? Description)
|
|
: IRequest<CreateSampleCommandResult>;
|
|
|
|
public class CreateSampleCommandHandler : IRequestHandler<CreateSampleCommand, CreateSampleCommandResult>
|
|
{
|
|
public async Task<CreateSampleCommandResult> Handle(CreateSampleCommand request, CancellationToken ct)
|
|
{
|
|
var sample = new Sample(request.Name, request.Description);
|
|
_repository.Add(sample);
|
|
await _repository.UnitOfWork.SaveEntitiesAsync(ct);
|
|
return new CreateSampleCommandResult(sample.Id);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Pipeline MediatR
|
|
|
|
```
|
|
Request → LoggingBehavior → ValidatorBehavior → TransactionBehavior → Handler → Response
|
|
```
|
|
|
|
## Các API Endpoints
|
|
|
|
| Method | Endpoint | Mô Tả |
|
|
|--------|----------|-------|
|
|
| `GET` | `/api/v1/samples` | Lấy tất cả samples |
|
|
| `GET` | `/api/v1/samples/{id}` | Lấy sample theo ID |
|
|
| `POST` | `/api/v1/samples` | Tạo sample mới |
|
|
| `PUT` | `/api/v1/samples/{id}` | Cập nhật sample |
|
|
| `DELETE` | `/api/v1/samples/{id}` | Xóa sample |
|
|
| `PATCH` | `/api/v1/samples/{id}/status` | Thay đổi trạng thái |
|
|
|
|
### Health Endpoints
|
|
|
|
| Endpoint | Mục Đích |
|
|
|----------|----------|
|
|
| `/health` | Trạng thái health đầy đủ |
|
|
| `/health/live` | Kiểm tra sống |
|
|
| `/health/ready` | Kiểm tra sẵn sàng |
|
|
|
|
## Biến Môi Trường
|
|
|
|
### Required / Bắt buộc
|
|
|
|
| Biến | Mô Tả | Mặc định |
|
|
|------|-------|----------|
|
|
| `ASPNETCORE_ENVIRONMENT` | Tên môi trường | `Development` |
|
|
| `DATABASE_URL` | Kết nối Neon PostgreSQL | - |
|
|
| `ConnectionStrings__DefaultConnection` | Connection string format chuẩn | - |
|
|
|
|
### Optional / Tùy chọn
|
|
|
|
| Biến | Mô Tả | Mặc định |
|
|
|------|-------|----------|
|
|
| `REDIS_URL` | Kết nối Redis (cache) | - |
|
|
| `RabbitMQ__Host` | RabbitMQ hostname | `localhost` |
|
|
| `RabbitMQ__Port` | RabbitMQ port | `5672` |
|
|
| `RabbitMQ__Username` | RabbitMQ username | `guest` |
|
|
| `RabbitMQ__Password` | RabbitMQ password | `guest` |
|
|
|
|
### Inter-Service Communication
|
|
|
|
| Biến | Mô Tả | Mặc định |
|
|
|------|-------|----------|
|
|
| `IamService__BaseUrl` | IAM Service URL | `http://iam-service-net:5001` |
|
|
| `Services__InternalApiKey` | Shared secret cho service-to-service auth | - |
|
|
|
|
### Multi-Provider Pattern (Example: Storage Service)
|
|
|
|
| Biến | Mô Tả | Mặc định |
|
|
|------|-------|----------|
|
|
| `Storage__Provider` | Provider: `minio` hoặc `aliyun` | `minio` |
|
|
| `Storage__DefaultBucket` | Default bucket name | `storage` |
|
|
| `Storage__MinIO__Endpoint` | MinIO endpoint | `localhost:9000` |
|
|
| `Storage__MinIO__AccessKey` | MinIO access key | - |
|
|
| `Storage__MinIO__SecretKey` | MinIO secret key | - |
|
|
| `Storage__AliyunOSS__Endpoint` | Aliyun OSS endpoint | - |
|
|
| `Storage__AliyunOSS__AccessKeyId` | Aliyun access key | - |
|
|
| `Storage__AliyunOSS__AccessKeySecret` | Aliyun secret key | - |
|
|
|
|
## Kiểm Thử
|
|
|
|
```bash
|
|
# Chạy tất cả tests
|
|
dotnet test
|
|
|
|
# Chạy với coverage
|
|
dotnet test /p:CollectCoverage=true /p:CoverageReportFormat=cobertura
|
|
|
|
# Chạy project test cụ thể
|
|
dotnet test tests/MyService.UnitTests
|
|
```
|
|
|
|
## Triển Khai
|
|
|
|
### Docker Build
|
|
|
|
```bash
|
|
docker build -t myservice:latest .
|
|
docker run -p 5000:8080 --env-file .env myservice:latest
|
|
```
|
|
|
|
### Kubernetes
|
|
|
|
```yaml
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: myservice-api
|
|
spec:
|
|
replicas: 3
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: api
|
|
image: myservice:latest
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /health/live
|
|
port: 8080
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /health/ready
|
|
port: 8080
|
|
```
|
|
|
|
## Patterns Nâng cao
|
|
|
|
### Multi-Provider Pattern
|
|
|
|
**Sử dụng khi**: Service cần hỗ trợ nhiều external providers (storage, payment, SMS)
|
|
|
|
**Example**: Storage Service với MinIO và Aliyun OSS
|
|
|
|
```csharp
|
|
// Domain - Interface
|
|
public interface IStorageProvider
|
|
{
|
|
Task<string> UploadFileAsync(Stream fileStream, string fileName, CancellationToken ct);
|
|
Task<Stream> DownloadFileAsync(string objectKey, CancellationToken ct);
|
|
Task DeleteFileAsync(string objectKey, CancellationToken ct);
|
|
Task<string> GeneratePresignedUrlAsync(string objectKey, int expiryMinutes, CancellationToken ct);
|
|
}
|
|
|
|
// Infrastructure - MinIO Implementation
|
|
public class MinioStorageProvider : IStorageProvider
|
|
{
|
|
private readonly MinioClient _client;
|
|
// Implementation...
|
|
}
|
|
|
|
// Infrastructure - Aliyun OSS Implementation
|
|
public class AliyunOssStorageProvider : IStorageProvider
|
|
{
|
|
private readonly OssClient _client;
|
|
// Implementation...
|
|
}
|
|
|
|
// API - Provider Factory
|
|
public class StorageProviderFactory
|
|
{
|
|
public IStorageProvider CreateProvider(IConfiguration config)
|
|
{
|
|
var provider = config["Storage:Provider"];
|
|
return provider?.ToLower() switch
|
|
{
|
|
"minio" => new MinioStorageProvider(config),
|
|
"aliyun" => new AliyunOssStorageProvider(config),
|
|
_ => throw new InvalidOperationException($"Unknown provider: {provider}")
|
|
};
|
|
}
|
|
}
|
|
```
|
|
|
|
**File Reference**: [`StorageService.Infrastructure/Providers/`](file:///Users/velikho/Desktop/WORKING/Base/services/storage-service-net/src/StorageService.Infrastructure/Providers)
|
|
|
|
---
|
|
|
|
### Inter-Service Authentication Pattern
|
|
|
|
**Sử dụng khi**: Service cần validate JWT tokens với IAM Service
|
|
|
|
```csharp
|
|
// Infrastructure - IAM Service Client
|
|
public class IamServiceClient
|
|
{
|
|
private readonly HttpClient _httpClient;
|
|
private readonly IConfiguration _config;
|
|
|
|
public IamServiceClient(HttpClient httpClient, IConfiguration config)
|
|
{
|
|
_httpClient = httpClient;
|
|
_config = config;
|
|
_httpClient.BaseAddress = new Uri(_config["IamService:BaseUrl"]);
|
|
}
|
|
|
|
public async Task<UserInfo> ValidateTokenAsync(string token, CancellationToken ct)
|
|
{
|
|
var request = new HttpRequestMessage(HttpMethod.Post, "/api/v1/auth/validate")
|
|
{
|
|
Headers = { { "Authorization", $"Bearer {token}" } }
|
|
};
|
|
|
|
var response = await _httpClient.SendAsync(request, ct);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<UserInfo>(ct);
|
|
}
|
|
}
|
|
|
|
// API - Middleware
|
|
public class JwtValidationMiddleware
|
|
{
|
|
private readonly RequestDelegate _next;
|
|
private readonly IamServiceClient _iamClient;
|
|
|
|
public async Task InvokeAsync(HttpContext context)
|
|
{
|
|
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
|
|
|
|
if (!string.IsNullOrEmpty(token))
|
|
{
|
|
var userInfo = await _iamClient.ValidateTokenAsync(token, context.RequestAborted);
|
|
context.Items["UserId"] = userInfo.UserId;
|
|
}
|
|
|
|
await _next(context);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Có Gì Mới Trong .NET 10
|
|
|
|
- Tính năng ngôn ngữ **C# 14**
|
|
- Hỗ trợ **Native AOT** được cải thiện
|
|
- Hiệu suất **async/await** tốt hơn
|
|
- **JSON serialization** được nâng cao (System.Text.Json)
|
|
- Hỗ trợ **LTS** 3 năm (đến tháng 11/2028)
|
|
- **Entity Framework Core 10** với performance improvements
|
|
|
|
## Tích hợp với Platform
|
|
|
|
### Traefik Routing
|
|
|
|
Thêm labels vào `docker-compose.yml`:
|
|
|
|
```yaml
|
|
services:
|
|
your-service-net:
|
|
build:
|
|
context: ../..
|
|
dockerfile: services/your-service-net/Dockerfile
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.your-service.rule=PathPrefix(`/api/v1/your-service`)"
|
|
- "traefik.http.services.your-service.loadbalancer.server.port=8080"
|
|
- "traefik.http.routers.your-service.middlewares=strip-prefix@docker"
|
|
```
|
|
|
|
### Neon PostgreSQL Configuration
|
|
|
|
```json
|
|
{
|
|
"ConnectionStrings": {
|
|
"DefaultConnection": "Host=ep-xxx.neon.tech;Database=your_service;Username=xxx;Password=xxx;SSL Mode=Require;Trust Server Certificate=true"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Best Practices**:
|
|
- Sử dụng connection pooling: `Pooling=true;Maximum Pool Size=100`
|
|
- Enable SSL: `SSL Mode=Require`
|
|
- Set command timeout: `Command Timeout=30`
|
|
|
|
### RabbitMQ Integration (thay vì Kafka)
|
|
|
|
```csharp
|
|
// Domain Event
|
|
public record FileUploadedEvent(string FileId, string UserId, long FileSize) : IDomainEvent;
|
|
|
|
// Infrastructure - Event Publisher
|
|
public class RabbitMqEventPublisher : IEventPublisher
|
|
{
|
|
private readonly IConnection _connection;
|
|
private readonly IModel _channel;
|
|
|
|
public async Task PublishAsync<T>(T @event, CancellationToken ct) where T : IDomainEvent
|
|
{
|
|
var message = JsonSerializer.Serialize(@event);
|
|
var body = Encoding.UTF8.GetBytes(message);
|
|
|
|
_channel.BasicPublish(
|
|
exchange: "domain_events",
|
|
routingKey: typeof(T).Name,
|
|
body: body
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Tài Liệu Liên Quan
|
|
|
|
### Architecture
|
|
- [System Design](../architecture/system-design.md) - Kiến trúc tổng thể GoodGo Platform
|
|
- [Microservices Communication](../architecture/microservices-communication.md) - Patterns giao tiếp
|
|
- [Caching Architecture](../architecture/caching-architecture.md) - Chiến lược Redis caching
|
|
|
|
### Skills
|
|
- [CQRS with MediatR](../skills/cqrs-mediatr.md) - CQRS pattern implementation
|
|
- [Repository Pattern](../skills/repository-pattern.md) - EF Core repository pattern
|
|
- [Domain-Driven Design](../skills/domain-driven-design.md) - DDD tactical patterns
|
|
- [Error Handling](../skills/error-handling-patterns.md) - Exception handling patterns
|
|
|
|
### Guides
|
|
- [Local Development](../guides/local-development.md) - Setup môi trường dev
|
|
- [Deployment](../guides/deployment.md) - Deploy lên Kubernetes
|
|
|
|
## Tài Nguyên Bổ sung
|
|
|
|
### Microsoft Official
|
|
- [.NET 10 Documentation](https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-10)
|
|
- [Entity Framework Core 10](https://docs.microsoft.com/en-us/ef/core/)
|
|
- [ASP.NET Core 10](https://docs.microsoft.com/en-us/aspnet/core/)
|
|
|
|
### Architecture References
|
|
- [eShopOnContainers](https://github.com/dotnet-architecture/eShopOnContainers) - Microservices reference architecture
|
|
- [.NET Microservices Architecture Book](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/)
|
|
|
|
### Libraries
|
|
- [MediatR](https://github.com/jbogard/MediatR) - CQRS implementation
|
|
- [FluentValidation](https://docs.fluentvalidation.net/) - Validation library
|
|
- [Polly](https://github.com/App-vNext/Polly) - Resilience and transient-fault-handling
|
|
|
|
### Tools
|
|
- [EF Core Tools](https://docs.microsoft.com/en-us/ef/core/cli/dotnet) - Migrations và scaffolding
|
|
- [Neon PostgreSQL](https://neon.tech/docs) - Serverless Postgres documentation
|
|
|
|
---
|
|
|
|
**Last Updated**: 2026-01-14
|
|
**Template Version**: 2.0
|
|
**Maintained By**: GoodGo Architecture Team
|