docs: Update architecture and template documentation for GoodGo Platform
- 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.
This commit is contained in:
@@ -138,61 +138,45 @@ export class ExamplePattern {
|
||||
```
|
||||
|
||||
**File Location / Vị trí File**:
|
||||
- **Template**: [`_template/src/modules/example/example.pattern.ts`](file:///Users/velikho/Desktop/WORKING/Base/services/_template/src/modules/example/example.pattern.ts)
|
||||
- **Production**: [`iam-service/src/modules/example/example.pattern.ts`](file:///Users/velikho/Desktop/WORKING/Base/services/iam-service/src/modules/example/example.pattern.ts)
|
||||
- **Skill Template**: [`.agent/skills/skill-authoring/SKILL.md`](file:///Users/velikho/Desktop/WORKING/Base/.agent/skills/skill-authoring/SKILL.md)
|
||||
- **Example Skills**: [`.agent/skills/`](file:///Users/velikho/Desktop/WORKING/Base/.agent/skills)
|
||||
|
||||
### Advanced Implementation / Triển khai Nâng cao
|
||||
|
||||
**EN**: More complex implementation with additional features.
|
||||
**EN**: More complex implementation with additional features like caching and error handling.
|
||||
|
||||
**VI**: Triển khai phức tạp hơn với các tính năng bổ sung.
|
||||
**VI**: Triển khai phức tạp hơn với các tính năng bổ sung như caching và xử lý lỗi.
|
||||
|
||||
```typescript
|
||||
// EN: Advanced implementation with caching and error handling
|
||||
// VI: Triển khai nâng cao với caching và xử lý lỗi
|
||||
```csharp
|
||||
// EN: Advanced .NET implementation with caching
|
||||
// VI: Triển khai .NET nâng cao với caching
|
||||
|
||||
export class AdvancedExamplePattern extends ExamplePattern {
|
||||
constructor(
|
||||
dependency1: Dependency1,
|
||||
dependency2: Dependency2,
|
||||
private cache: CacheService,
|
||||
private logger: Logger
|
||||
) {
|
||||
super(dependency1, dependency2);
|
||||
}
|
||||
public class CachedStorageProvider : IStorageProvider
|
||||
{
|
||||
private readonly IStorageProvider _innerProvider;
|
||||
private readonly IDistributedCache _cache;
|
||||
private readonly ILogger<CachedStorageProvider> _logger;
|
||||
|
||||
async execute(input: InputType): Promise<OutputType> {
|
||||
// EN: Try cache first
|
||||
// VI: Thử cache trước
|
||||
const cacheKey = this.getCacheKey(input);
|
||||
const cached = await this.cache.get<OutputType>(cacheKey);
|
||||
|
||||
if (cached) {
|
||||
this.logger.info('Cache hit', { key: cacheKey });
|
||||
return cached;
|
||||
public async Task<string> UploadFileAsync(Stream fileStream, string fileName, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
// EN: Upload file
|
||||
// VI: Upload file
|
||||
var result = await _innerProvider.UploadFileAsync(fileStream, fileName, ct);
|
||||
|
||||
// EN: Invalidate cache
|
||||
// VI: Vô hiệu hóa cache
|
||||
await _cache.RemoveAsync($"file:{fileName}", ct);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to upload file {FileName}", fileName);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// EN: Execute pattern logic
|
||||
// VI: Thực thi logic pattern
|
||||
const result = await super.execute(input);
|
||||
|
||||
// EN: Cache result
|
||||
// VI: Cache kết quả
|
||||
await this.cache.set(cacheKey, result, 300); // 5 minutes
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
// EN: Error handling
|
||||
// VI: Xử lý lỗi
|
||||
this.logger.error('Pattern execution failed', { error, input });
|
||||
throw new PatternExecutionError('Execution failed', { cause: error });
|
||||
}
|
||||
}
|
||||
|
||||
private getCacheKey(input: InputType): string {
|
||||
return `pattern:example:${JSON.stringify(input)}`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -255,60 +239,76 @@ sequenceDiagram
|
||||
|
||||
## Usage Examples / Ví dụ Sử dụng
|
||||
|
||||
### Example 1 / Ví dụ 1: Basic Usage
|
||||
### Example 1: Multi-Provider Storage Pattern (Real-World)
|
||||
|
||||
**EN**: Basic usage scenario with explanation.
|
||||
**EN**: Real implementation from Storage Service.
|
||||
|
||||
**VI**: Tình huống sử dụng cơ bản với giải thích.
|
||||
**VI**: Triển khai thực tế từ Storage Service.
|
||||
|
||||
```typescript
|
||||
// EN: Setup
|
||||
// VI: Thiết lập
|
||||
const dependency1 = new Dependency1();
|
||||
const dependency2 = new Dependency2();
|
||||
const pattern = new ExamplePattern(dependency1, dependency2);
|
||||
```csharp
|
||||
// Domain interface
|
||||
public interface IStorageProvider
|
||||
{
|
||||
Task<string> UploadFileAsync(Stream fileStream, string fileName, CancellationToken ct);
|
||||
Task<string> GeneratePresignedUrlAsync(string objectKey, int expiryMinutes, CancellationToken ct);
|
||||
}
|
||||
|
||||
// EN: Execute pattern
|
||||
// VI: Thực thi pattern
|
||||
const input: InputType = {
|
||||
id: '123',
|
||||
data: 'example',
|
||||
};
|
||||
|
||||
const result = await pattern.execute(input);
|
||||
|
||||
console.log(result);
|
||||
// EN: Output / VI: Kết quả:
|
||||
// { success: true, data: { ... } }
|
||||
// Factory pattern
|
||||
public class StorageProviderFactory
|
||||
{
|
||||
public IStorageProvider CreateProvider(string providerName)
|
||||
{
|
||||
return providerName?.ToLower() switch
|
||||
{
|
||||
"minio" => _serviceProvider.GetRequiredService<MinioStorageProvider>(),
|
||||
"aliyun" => _serviceProvider.GetRequiredService<AliyunOssStorageProvider>(),
|
||||
_ => throw new InvalidOperationException($"Unknown provider: {providerName}")
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Example 2 / Ví dụ 2: Advanced Usage with Caching
|
||||
**File Reference**: [`services/storage-service-net/src/StorageService.Infrastructure/Providers/`](file:///Users/velikho/Desktop/WORKING/Base/services/storage-service-net/src/StorageService.Infrastructure/Providers)
|
||||
|
||||
**EN**: Advanced usage with caching enabled.
|
||||
---
|
||||
|
||||
**VI**: Sử dụng nâng cao với caching được bật.
|
||||
### Example 2: CQRS with MediatR (Real-World)
|
||||
|
||||
```typescript
|
||||
// EN: Setup with additional dependencies
|
||||
// VI: Thiết lập với các dependencies bổ sung
|
||||
const cache = new CacheService();
|
||||
const logger = new Logger();
|
||||
const pattern = new AdvancedExamplePattern(
|
||||
dependency1,
|
||||
dependency2,
|
||||
cache,
|
||||
logger
|
||||
);
|
||||
**EN**: Command handler pattern from actual services.
|
||||
|
||||
// EN: First call (cache miss)
|
||||
// VI: Lần gọi đầu tiên (cache miss)
|
||||
const result1 = await pattern.execute(input);
|
||||
**VI**: Pattern command handler từ các services thực tế.
|
||||
|
||||
// EN: Second call (cache hit)
|
||||
// VI: Lần gọi thứ hai (cache hit)
|
||||
const result2 = await pattern.execute(input); // Returns cached result
|
||||
```csharp
|
||||
// Command
|
||||
public record CreateFileCommand(string FileName, Stream FileStream, string UserId)
|
||||
: IRequest<CreateFileCommandResult>;
|
||||
|
||||
// Handler
|
||||
public class CreateFileCommandHandler : IRequestHandler<CreateFileCommand, CreateFileCommandResult>
|
||||
{
|
||||
private readonly IStorageProvider _storageProvider;
|
||||
private readonly IFileRepository _fileRepository;
|
||||
|
||||
public async Task<CreateFileCommandResult> Handle(CreateFileCommand request, CancellationToken ct)
|
||||
{
|
||||
// Upload to storage
|
||||
var objectKey = await _storageProvider.UploadFileAsync(
|
||||
request.FileStream,
|
||||
request.FileName,
|
||||
ct
|
||||
);
|
||||
|
||||
// Save metadata
|
||||
var file = new File(request.FileName, objectKey, request.UserId);
|
||||
await _fileRepository.AddAsync(file, ct);
|
||||
|
||||
return new CreateFileCommandResult(file.Id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**File Reference**: See [CQRS with MediatR Skill](../skills/cqrs-mediatr.md)
|
||||
|
||||
## Best Practices / Thực hành Tốt nhất
|
||||
|
||||
### EN: Recommended Practices
|
||||
@@ -372,81 +372,79 @@ if (input) {
|
||||
|
||||
## Testing / Kiểm thử
|
||||
|
||||
### Unit Test Example / Ví dụ Unit Test
|
||||
### Unit Test Example for .NET / Ví dụ Unit Test cho .NET
|
||||
|
||||
```typescript
|
||||
// EN: Unit test for the pattern
|
||||
// VI: Unit test cho pattern
|
||||
```csharp
|
||||
// EN: Unit test for CQRS command handler
|
||||
// VI: Unit test cho CQRS command handler
|
||||
|
||||
describe('ExamplePattern', () => {
|
||||
let pattern: ExamplePattern;
|
||||
let mockDep1: jest.Mocked<Dependency1>;
|
||||
let mockDep2: jest.Mocked<Dependency2>;
|
||||
using Xunit;
|
||||
using NSubstitute;
|
||||
|
||||
beforeEach(() => {
|
||||
mockDep1 = {
|
||||
transform: jest.fn(),
|
||||
} as any;
|
||||
mockDep2 = {} as any;
|
||||
public class CreateFileCommandHandlerTests
|
||||
{
|
||||
private readonly IStorageProvider _storageProvider;
|
||||
private readonly IFileRepository _fileRepository;
|
||||
private readonly CreateFileCommandHandler _handler;
|
||||
|
||||
pattern = new ExamplePattern(mockDep1, mockDep2);
|
||||
});
|
||||
public CreateFileCommandHandlerTests()
|
||||
{
|
||||
_storageProvider = Substitute.For<IStorageProvider>();
|
||||
_fileRepository = Substitute.For<IFileRepository>();
|
||||
_handler = new CreateFileCommandHandler(_storageProvider, _fileRepository);
|
||||
}
|
||||
|
||||
it('should execute successfully', async () => {
|
||||
// EN: Arrange
|
||||
// VI: Chuẩn bị
|
||||
const input = { id: '123', data: 'test' };
|
||||
const expectedProcessed = { transformed: true };
|
||||
mockDep1.transform.mockResolvedValue(expectedProcessed);
|
||||
[Fact]
|
||||
public async Task Handle_ValidCommand_CreatesFileSuccessfully()
|
||||
{
|
||||
// Arrange
|
||||
var command = new CreateFileCommand("test.txt", Stream.Null, "user-123");
|
||||
_storageProvider.UploadFileAsync(Arg.Any<Stream>(), Arg.Any<string>(), Arg.Any<CancellationToken>())
|
||||
.Returns("object-key-123");
|
||||
|
||||
// EN: Act
|
||||
// VI: Thực thi
|
||||
const result = await pattern.execute(input);
|
||||
// Act
|
||||
var result = await _handler.Handle(command, CancellationToken.None);
|
||||
|
||||
// EN: Assert
|
||||
// VI: Kiểm tra
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data).toEqual(expectedProcessed);
|
||||
expect(mockDep1.transform).toHaveBeenCalledWith(input);
|
||||
});
|
||||
|
||||
it('should throw error for invalid input', async () => {
|
||||
// EN: Expect error for null input
|
||||
// VI: Mong đợi lỗi cho đầu vào null
|
||||
await expect(pattern.execute(null as any))
|
||||
.rejects
|
||||
.toThrow('Input is required');
|
||||
});
|
||||
});
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.NotEmpty(result.FileId);
|
||||
await _fileRepository.Received(1).AddAsync(Arg.Any<File>(), Arg.Any<CancellationToken>());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Reference**: See [Testing Patterns Skill](../skills/testing-patterns.md)
|
||||
|
||||
## Related Patterns / Patterns Liên quan
|
||||
|
||||
**EN**: Other patterns that complement or relate to this one.
|
||||
|
||||
**VI**: Các patterns khác bổ sung hoặc liên quan đến pattern này.
|
||||
|
||||
- [Related Pattern 1](./related-pattern-1.md) - EN: How it relates / VI: Cách nó liên quan
|
||||
- [Related Pattern 2](./related-pattern-2.md) - EN: Comparison / VI: So sánh
|
||||
- [Alternative Pattern](./alternative-pattern.md) - EN: When to use instead / VI: Khi nào sử dụng thay thế
|
||||
**GoodGo Skills**:
|
||||
- [CQRS with MediatR](../skills/cqrs-mediatr.md) - EN: Command/Query separation / VI: Tách biệt Command/Query
|
||||
- [Repository Pattern](../skills/repository-pattern.md) - EN: Data access abstraction / VI: Trừa tượng hóa truy cập dữ liệu
|
||||
- [Domain-Driven Design](../skills/domain-driven-design.md) - EN: DDD tactical patterns / VI: Patterns chiến thuật DDD
|
||||
- [Redis Caching](../skills/redis-caching.md) - EN: Distributed caching / VI: Caching phân tán
|
||||
- [Error Handling](../skills/error-handling-patterns.md) - EN: Exception patterns / VI: Patterns exception
|
||||
|
||||
## Real-World Examples / Ví dụ Thực tế
|
||||
|
||||
**EN**: Examples of this pattern used in the codebase.
|
||||
**EN**: Examples of this pattern used in the GoodGo codebase.
|
||||
|
||||
**VI**: Ví dụ về pattern này được sử dụng trong codebase.
|
||||
**VI**: Ví dụ về pattern này được sử dụng trong codebase GoodGo.
|
||||
|
||||
### Example from IAM Service
|
||||
### Storage Service - Multi-Provider Pattern
|
||||
|
||||
**File**: [`iam-service/src/modules/rbac/rbac.service.ts`](file:///Users/velikho/Desktop/WORKING/Base/services/iam-service/src/modules/rbac/rbac.service.ts)
|
||||
**File**: [`services/storage-service-net/src/StorageService.Infrastructure/Providers/`](file:///Users/velikho/Desktop/WORKING/Base/services/storage-service-net/src/StorageService.Infrastructure/Providers)
|
||||
|
||||
```typescript
|
||||
// EN: Real implementation from IAM service
|
||||
// VI: Triển khai thực tế từ IAM service
|
||||
export class RBACService implements ExamplePattern {
|
||||
// ... implementation
|
||||
}
|
||||
```
|
||||
**Pattern**: Factory pattern for switching between MinIO and Aliyun OSS providers.
|
||||
|
||||
### IAM Service - RBAC Pattern
|
||||
|
||||
**File**: [`services/iam-service-net/`](file:///Users/velikho/Desktop/WORKING/Base/services/iam-service-net)
|
||||
|
||||
**Pattern**: Role-Based Access Control implementation with permission caching.
|
||||
|
||||
## Additional Resources / Tài nguyên Bổ sung
|
||||
|
||||
|
||||
Reference in New Issue
Block a user