Files
pos-system/docs/en/architecture/data-consistency-patterns.md
Ho Ngoc Hai 3ed499ef7c docs: Update architecture documentation for GoodGo Platform
- Translated and revised architecture documents to enhance clarity and accessibility for both English and Vietnamese audiences.
- Improved diagrams and descriptions for caching, data consistency, event-driven architecture, microservices communication, observability, and security architecture.
- Ensured consistent formatting and terminology across all documents to facilitate better understanding and navigation.
- Added quick tips and troubleshooting sections to assist developers in implementing and managing the architecture effectively.
2026-01-14 13:07:19 +07:00

24 KiB

Kiến trúc Patterns Đồng bộ Dữ liệu

Các patterns để duy trì tính nhất quán dữ liệu trong kiến trúc microservices phân tán

Sơ đồ Tổng quan

graph TD
    subgraph "Consistency Patterns"
        Saga[Saga Pattern<br/>Distributed Transactions]
        Outbox[Outbox Pattern<br/>Reliable Events]
        Idempotency[Idempotency<br/>Retry Safety]
        OptimisticLock[Optimistic Locking<br/>Concurrent Updates]
        CQRS[CQRS<br/>Read/Write Separation]
    end
    
    Service1[Service A] --> Saga
    Service2[Service B] --> Outbox
    Service3[Service C] --> Idempotency
    
    Saga --> EventualConsistency[Eventual Consistency]
    Outbox --> EventualConsistency
    Idempotency --> EventualConsistency
    OptimisticLock --> StrongConsistency[Strong Consistency]
    CQRS --> EventualConsistency
    
    %% Dark color palette with white text
    style Saga fill:#1d4ed8,stroke:#3b82f6,color:#ffffff
    style Outbox fill:#b45309,stroke:#f59e0b,color:#ffffff
    style Idempotency fill:#7e22ce,stroke:#a855f7,color:#ffffff
    style OptimisticLock fill:#15803d,stroke:#22c55e,color:#ffffff
    style CQRS fill:#15803d,stroke:#22c55e,color:#ffffff
    style EventualConsistency fill:#374151,stroke:#6b7280,color:#ffffff
    style StrongConsistency fill:#374151,stroke:#6b7280,color:#ffffff
    style Service1 fill:#4527a0,stroke:#7c4dff,color:#ffffff
    style Service2 fill:#4527a0,stroke:#7c4dff,color:#ffffff
    style Service3 fill:#4527a0,stroke:#7c4dff,color:#ffffff

Mô tả Kiến trúc

Tổng quan Kiến trúc

Nền tảng GoodGo sử dụng nhiều consistency patterns để xử lý dữ liệu phân tán:

Thách thức Cốt lõi:

  • Không có distributed transactions (2PC quá chậm)
  • Services sở hữu dữ liệu riêng (database per service)
  • Network failures có thể gây partial completion
  • Cần maintain data integrity giữa các services

Lựa chọn Pattern:

  • Saga: Cho workflows nhiều services
  • Outbox: Cho event publishing đảm bảo
  • Idempotency: Cho retries an toàn
  • Optimistic Locking: Cho concurrent updates
  • CQRS: Cho tối ưu read/write

Bối cảnh Hệ thống

C4Context
    title System Context for Data Consistency in GoodGo Platform
    
    Person(user, "User", "End user performing actions")
    
    System_Boundary(goodgo, "GoodGo Microservices") {
        System(order_service, "Order Service", "Manages orders with Saga")
        System(payment_service, "Payment Service", "Processes payments")
        System(inventory_service, "Inventory Service", "Manages stock")
        System(saga_orchestrator, "Saga Orchestrator", "Coordinates distributed transactions")
        System(outbox_processor, "Outbox Processor", "Publishes events reliably")
    }
    
    System_Ext(db_order, "Order DB", "PostgreSQL with Outbox table")
    System_Ext(db_payment, "Payment DB", "PostgreSQL with version field")
    System_Ext(db_inventory, "Inventory DB", "PostgreSQL")
    System_Ext(kafka, "Event Bus", "Kafka - Event streaming")
    System_Ext(redis, "Cache", "Redis - Idempotency keys")
    
    Rel(user, order_service, "Places order", "HTTPS")
    Rel(order_service, saga_orchestrator, "Starts saga", "Internal")
    Rel(saga_orchestrator, payment_service, "Process payment", "HTTP")
    Rel(saga_orchestrator, inventory_service, "Reserve stock", "HTTP")
    
    Rel(order_service, db_order, "Writes + Outbox", "SQL")
    Rel(payment_service, db_payment, "Updates with version", "SQL")
    Rel(inventory_service, db_inventory, "Reads/Writes", "SQL")
    
    Rel(outbox_processor, db_order, "Polls outbox", "SQL")
    Rel(outbox_processor, kafka, "Publishes events", "Kafka Protocol")
    Rel(order_service, redis, "Checks idempotency key", "Redis Protocol")
    
    UpdateRelStyle(saga_orchestrator, payment_service, $lineColor="red", $textColor="red")
    UpdateRelStyle(saga_orchestrator, inventory_service, $lineColor="red", $textColor="red")

Nền tảng GoodGo sử dụng kiến trúc database-per-service nơi mỗi service sở hữu dữ liệu riêng. Tính nhất quán dữ liệu giữa các services đạt được thông qua các patterns như Saga (cho workflows phối hợp), Outbox (cho event publishing đáng tin cậy), Idempotency (cho retries an toàn), và Optimistic Locking (cho concurrent updates). Các patterns này cho phép eventual consistency đồng thời duy trì data integrity.

Pattern Saga

sequenceDiagram
    participant Orchestrator
    participant OrderService
    participant PaymentService
    participant InventoryService
    
    Orchestrator->>OrderService: 1. Create Order
    OrderService-->>Orchestrator: Order Created
    
    Orchestrator->>PaymentService: 2. Process Payment
    PaymentService-->>Orchestrator: Payment Success
    
    Orchestrator->>InventoryService: 3. Reserve Inventory
    
    alt Inventory Reserved
        InventoryService-->>Orchestrator: Success
        Orchestrator->>Orchestrator: Complete Saga ✓
    else Inventory Failed
        InventoryService-->>Orchestrator: Failed ✗
        Orchestrator->>PaymentService: Compensate: Refund
        PaymentService-->>Orchestrator: Refunded
        Orchestrator->>OrderService: Compensate: Cancel Order
        OrderService-->>Orchestrator: Cancelled
    end

Mô tả: Saga quản lý distributed transactions dưới dạng chuỗi local transactions với compensation.

Triển khai:

// Saga orchestrator
class OrderSaga {
  async execute(orderData: OrderData): Promise<void> {
    const sagaContext = {
      orderId: null,
      paymentId: null,
      inventoryId: null
    };
    
    try {
      // Bước 1: Tạo đơn hàng
      sagaContext.orderId = await orderService.create(orderData);
      
      // Bước 2: Xử lý thanh toán
      sagaContext.paymentId = await paymentService.process(orderData.payment);
      
      // Bước 3: Đặt trước kho
      sagaContext.inventoryId = await inventoryService.reserve(orderData.items);
      
      // Tất cả thành công - commit
      await this.completeSaga(sagaContext);
    } catch (error) {
      // Compensate theo thứ tự ngược lại
      await this.compensate(sagaContext, error);
      throw error;
    }
  }
  
  private async compensate(context: SagaContext, error: Error): Promise<void> {
    if (context.inventoryId) {
      await inventoryService.release(context.inventoryId);
    }
    if (context.paymentId) {
      await paymentService.refund(context.paymentId);
    }
    if (context.orderId) {
      await orderService.cancel(context.orderId);
    }
  }
}

Pattern Outbox

sequenceDiagram
    participant Service
    participant DB as Database
    participant OutboxTable as Outbox Table
    participant Processor as Outbox Processor
    participant Kafka
    
    Service->>DB: Begin Transaction
    Service->>DB: Update Business Data
    Service->>OutboxTable: Insert Event
    Service->>DB: Commit Transaction
    
    loop Every 5 seconds
        Processor->>OutboxTable: SELECT unpublished events
        OutboxTable-->>Processor: Events
        Processor->>Kafka: Publish Events
        Kafka-->>Processor: Ack
        Processor->>OutboxTable: Mark as published
    end

Mô tả: Đảm bảo event publishing bằng cách lưu events trong database cùng transaction với business data.

Triển khai (.NET với EF Core):

// EN: Save event in outbox with business data
// VI: Lưu event trong outbox cùng với business data
public async Task<User> CreateUserAsync(CreateUserDto dto, CancellationToken ct)
{
    await using var transaction = await _context.Database.BeginTransactionAsync(ct);
    
    try
    {
        // Business operation
        var user = new User
        {
            Id = Guid.NewGuid(),
            Email = dto.Email,
            FirstName = dto.FirstName,
            LastName = dto.LastName
        };
        _context.Users.Add(user);
        
        // Lưu event trong outbox (cùng transaction)
        var outboxEvent = new OutboxMessage
        {
            Id = Guid.NewGuid(),
            AggregateId = user.Id.ToString(),
            AggregateType = nameof(User),
            EventType = "user.created.v1",
            Payload = JsonSerializer.Serialize(user),
            CreatedAt = DateTime.UtcNow
        };
        _context.OutboxMessages.Add(outboxEvent);
        
        await _context.SaveChangesAsync(ct);
        await transaction.CommitAsync(ct);
        
        return user;
    }
    catch
    {
        await transaction.RollbackAsync(ct);
        throw;
    }
}

Pattern Idempotency

graph LR
    Request1[Request with<br/>Idempotency Key]
    Request2[Retry with<br/>Same Key]
    
    Request1 --> Check{Key Exists?}
    Check -->|No| Process[Process Request]
    Check -->|Yes| Return[Return Cached Result]
    
    Process --> Store[Store Result<br/>with Key]
    Store --> Response1[Response]
    
    Request2 --> Check
    Return --> Response2[Same Response]
    
    %% Dark color palette with white text
    style Request1 fill:#374151,stroke:#6b7280,color:#ffffff
    style Request2 fill:#374151,stroke:#6b7280,color:#ffffff
    style Check fill:#b45309,stroke:#f59e0b,color:#ffffff
    style Process fill:#1d4ed8,stroke:#3b82f6,color:#ffffff
    style Store fill:#15803d,stroke:#22c55e,color:#ffffff
    style Return fill:#7e22ce,stroke:#a855f7,color:#ffffff
    style Response1 fill:#15803d,stroke:#22c55e,color:#ffffff
    style Response2 fill:#15803d,stroke:#22c55e,color:#ffffff

Mô tả: Đảm bảo operations có thể retry an toàn mà không có side effects bằng cách sử dụng idempotency keys.

Triển khai:

// Idempotency middleware
async function idempotentOperation<T>(
  key: string,
  operation: () => Promise<T>,
  ttl: number = 86400
): Promise<T> {
  // Kiểm tra đã xử lý chưa
  const cached = await redis.get(`idempotency:${key}`);
  if (cached) {
    return JSON.parse(cached);
  }
  
  // Xử lý operation
  const result = await operation();
  
  // Lưu kết quả
  await redis.setex(`idempotency:${key}`, ttl, JSON.stringify(result));
  
  return result;
}

// Sử dụng trong controller
async createPayment(req: Request, res: Response): Promise<void> {
  const idempotencyKey = req.headers['idempotency-key'] as string;
  
  if (!idempotencyKey) {
    return res.status(400).json({ error: 'Idempotency-Key header required' });
  }
  
  const result = await idempotentOperation(
    idempotencyKey,
    () => paymentService.process(req.body)
  );
  
  res.json({ success: true, data: result });
}

Khóa Lạc quan (Optimistic Locking)

sequenceDiagram
    participant User1
    participant User2
    participant Service
    participant DB
    
    User1->>Service: Read (version=1)
    User2->>Service: Read (version=1)
    
    User1->>Service: Update (version=1)
    Service->>DB: UPDATE WHERE version=1
    DB-->>Service: Success, version→2
    Service-->>User1: Success
    
    User2->>Service: Update (version=1)
    Service->>DB: UPDATE WHERE version=1
    DB-->>Service: No rows updated
    Service-->>User2: Conflict - version mismatch
    User2->>Service: Read (version=2)
    User2->>Service: Update (version=2)
    Service-->>User2: Success

Mô tả: Ngăn chặn lost updates bằng cách kiểm tra version khi update.

Triển khai (.NET với EF Core):

// EN: Entity with concurrency token
// VI: Entity với concurrency token
public class User
{
    public Guid Id { get; set; }
    public string Email { get; set; } = default!;
    public string Name { get; set; } = default!;
    
    [ConcurrencyCheck]
    public int Version { get; set; } = 1;
    
    // Or use RowVersion for SQL Server
    // [Timestamp]
    // public byte[] RowVersion { get; set; }
}

// EN: Update with optimistic locking
// VI: Update với optimistic locking
public async Task<User> UpdateUserAsync(
    Guid userId, 
    UpdateUserDto dto, 
    CancellationToken ct)
{
    var user = await _context.Users.FindAsync([userId], ct)
        ?? throw new UserNotFoundException(userId);
    
    user.Name = dto.Name;
    user.Version++; // Increment version
    
    try
    {
        await _context.SaveChangesAsync(ct);
        return user;
    }
    catch (DbUpdateConcurrencyException)
    {
        throw new ConcurrencyConflictException(
            "Data was modified by another user. Please refresh and try again.");
    }
}

CQRS Pattern

graph LR
    subgraph "Write Side"
        Command[Command] --> WriteModel[Write Model<br/>Normalized]
        WriteModel --> Events[Domain Events]
    end
    
    subgraph "Read Side"
        Events --> Projection[Event Projection]
        Projection --> ReadModel[Read Model<br/>Denormalized]
        Query[Query] --> ReadModel
    end
    
    WriteModel --> DB1[(Write DB)]
    ReadModel --> DB2[(Read DB<br/>Optimized)]
    
    %% Dark color palette with white text
    style Command fill:#1d4ed8,stroke:#3b82f6,color:#ffffff
    style WriteModel fill:#7e22ce,stroke:#a855f7,color:#ffffff
    style Events fill:#b45309,stroke:#f59e0b,color:#ffffff
    style Projection fill:#1d4ed8,stroke:#3b82f6,color:#ffffff
    style ReadModel fill:#15803d,stroke:#22c55e,color:#ffffff
    style Query fill:#15803d,stroke:#22c55e,color:#ffffff
    style DB1 fill:#374151,stroke:#6b7280,color:#ffffff
    style DB2 fill:#374151,stroke:#6b7280,color:#ffffff

Mô tả: Tách biệt read và write models để tối ưu hiệu suất.

Đặc điểm Hiệu suất

Chỉ số hiệu suất và chiến lược tối ưu cho patterns đồng bộ dữ liệu.

Pattern Tác động Độ trễ Thông lượng Ghi chú
Thực thi Saga 500ms - 2s 100-500 sagas/s Phụ thuộc số bước và compensation
Xử lý Outbox < 100ms 10,000 events/s Xử lý bất đồng bộ, tác động tối thiểu
Kiểm tra Idempotency < 10ms 50,000 checks/s Redis lookup, rất nhanh
Cập nhật Optimistic Lock < 50ms 5,000 updates/s Single DB operation với version check
CQRS Projection 100ms - 1s 1,000 events/s Xử lý event sang read model
Thực thi Compensation 200ms - 1s Varies Rollback operations trong saga

Chiến lược Tối ưu Hiệu suất

Saga Pattern:

  • Giảm thiểu số bước (< 5 bước lý tưởng)
  • Thực thi song song khi có thể
  • Cache service responses
  • Đặt timeouts phù hợp (30s mặc định)

Outbox Pattern:

  • Batch process outbox events (100-500 mỗi batch)
  • Index cột publishedAt cho hiệu suất
  • Archive processed events định kỳ
  • Sử dụng connection pooling cho Kafka

Idempotency:

  • Sử dụng Redis cho fast key lookups
  • Đặt TTL 24-48 giờ
  • Hash long idempotency keys
  • Clean expired keys thường xuyên

Optimistic Locking:

  • Hoạt động tốt nhất cho low-contention scenarios
  • Triển khai retry với exponential backoff
  • Giám sát conflict rates (nên < 5%)
  • Cân nhắc pessimistic locking nếu conflicts > 10%

Cân nhắc Bảo mật

Biện pháp bảo mật để bảo vệ các operations đồng bộ dữ liệu.

Bảo mật Saga

Bảo vệ Compensation:

  • Xác thực saga execution permissions ở mỗi bước
  • Mã hóa sensitive data trong saga context
  • Log tất cả saga executions cho audit
  • Triển khai timeout để ngăn hanging sagas
// Saga context bảo mật
interface SecureSagaContext {
  sagaId: string;
  userId: string;
  permissions: string[];
  encryptedData: string;
  auditLog: AuditEntry[];
}

Bảo mật Outbox

Mã hóa Event Payload:

  • Mã hóa PII (Personally Identifiable Information) trước khi lưu trong outbox
  • Sử dụng AES-256-GCM cho event payload encryption
  • Giải mã chỉ khi publishing sang Kafka
  • Rotate encryption keys hàng quý

Kiểm soát Truy cập:

  • Hạn chế truy cập outbox table chỉ cho outbox processor
  • Sử dụng database roles và permissions
  • Giám sát outbox table access patterns

Bảo mật Idempotency

Bảo mật Key:

  • Sử dụng cryptographic hashing cho idempotency keys (SHA-256)
  • Bao gồm user context trong key generation
  • Xác thực key ownership trước khi xử lý
  • Clear keys khi user logout cho sensitive operations
// Tạo idempotency key bảo mật
function generateIdempotencyKey(
  operation: string,
  userId: string,
  data: any
): string {
  const payload = JSON.stringify({ operation, userId, data });
  return crypto.createHash('sha256').update(payload).digest('hex');
}

Bảo mật Optimistic Lock

Ngăn chặn Giả mạo Version:

  • Xác thực version field chỉ ở server-side
  • Không bao giờ chấp nhận version từ client trực tiếp
  • Log version conflicts cho security monitoring
  • Rate limit update attempts per user

Triển khai

Cách các patterns đồng bộ dữ liệu được triển khai và mở rộng.

graph TD
    subgraph "Production Deployment"
        subgraph "Order Service Cluster"
            OS1[Order Service\nPod 1]
            OS2[Order Service\nPod 2]
            OS3[Order Service\nPod 3]
        end
        
        subgraph "Saga Orchestrator"
            SO1[Saga Orchestrator\nPod 1]
            SO2[Saga Orchestrator\nPod 2]
        end
        
        subgraph "Outbox Processor"
            OP1[Outbox Processor\nPod 1]
            OP2[Outbox Processor\nPod 2]
        end
        
        OS1 & OS2 & OS3 --> DB[(Order DB\nwith Outbox)]
        OS1 & OS2 & OS3 --> Redis[(Redis\nIdempotency Keys)]
        
        SO1 & SO2 --> PS[Payment Service]
        SO1 & SO2 --> IS[Inventory Service]
        
        OP1 & OP2 --> DB
        OP1 & OP2 --> Kafka[Kafka Cluster\n5 brokers]
    end
    
    %% Dark color palette with white text
    style OS1 fill:#4527a0,stroke:#7c4dff,color:#ffffff
    style OS2 fill:#4527a0,stroke:#7c4dff,color:#ffffff
    style OS3 fill:#4527a0,stroke:#7c4dff,color:#ffffff
    style SO1 fill:#1d4ed8,stroke:#3b82f6,color:#ffffff
    style SO2 fill:#1d4ed8,stroke:#3b82f6,color:#ffffff
    style OP1 fill:#b45309,stroke:#f59e0b,color:#ffffff
    style OP2 fill:#b45309,stroke:#f59e0b,color:#ffffff
    style DB fill:#15803d,stroke:#22c55e,color:#ffffff
    style Redis fill:#7e22ce,stroke:#a855f7,color:#ffffff
    style Kafka fill:#b91c1c,stroke:#ef4444,color:#ffffff
    style PS fill:#374151,stroke:#6b7280,color:#ffffff
    style IS fill:#374151,stroke:#6b7280,color:#ffffff

Cấu hình Triển khai

Thành phần Replicas Resources HA Strategy
Saga Orchestrator 2-3 512Mi RAM, 500m CPU Leader election với etcd
Outbox Processor 2-5 256Mi RAM, 250m CPU Distributed lock per event batch
Services với Outbox 3+ Varies Standard service scaling
Redis (Idempotency) 3 nodes 1Gi RAM each Redis Cluster với replication

Chiến lược Mở rộng

Saga Orchestrator:

  • Scale dựa trên pending saga count
  • Sử dụng queue-based load distribution
  • Giám sát saga execution duration

Outbox Processor:

  • Scale với database sharding (1 processor per shard)
  • Tăng batch size trước khi thêm replicas
  • Giám sát outbox table size và age

Idempotency Store (Redis):

  • Scale Redis cluster horizontally
  • Sử dụng consistent hashing cho key distribution
  • Giám sát memory usage (nên < 70%)

Giám sát & Khả năng quan sát

Chiến lược giám sát cho patterns đồng bộ dữ liệu.

Chỉ số Chính

Saga Metrics:

  • saga_executions_total - Tổng saga executions (success/failure)
  • saga_duration_seconds - Saga execution time histogram
  • saga_compensations_total - Tổng compensation executions
  • saga_timeout_total - Sagas timeout
  • saga_pending_count - Sagas đang thực thi

Outbox Metrics:

  • outbox_events_total - Events ghi vào outbox
  • outbox_published_total - Events published sang Kafka
  • outbox_processing_lag_seconds - Thời gian từ write đến publish
  • outbox_table_size - Số dòng outbox table
  • outbox_failed_events_total - Failed event publications

Idempotency Metrics:

  • idempotency_checks_total - Tổng idempotency checks
  • idempotency_hits_total - Duplicate requests prevented
  • idempotency_key_ttl_seconds - Average key TTL
  • idempotency_redis_errors_total - Redis failures

Optimistic Lock Metrics:

  • optimistic_lock_conflicts_total - Version conflicts detected
  • optimistic_lock_retries_total - Retry attempts sau conflict
  • optimistic_lock_success_rate - Update success percentage

Cảnh báo

Critical Alerts:

# Saga timeout rate quá cao
alert: HighSagaTimeoutRate
expr: rate(saga_timeout_total[5m]) > 0.05
for: 5m
severity: critical

# Outbox processing lag
alert: OutboxProcessingLag
expr: outbox_processing_lag_seconds > 300
for: 10m
severity: critical

# High optimistic lock conflict rate
alert: HighOptimisticLockConflicts
expr: rate(optimistic_lock_conflicts_total[5m]) / rate(optimistic_lock_attempts_total[5m]) > 0.1
for: 5m
severity: warning

Dashboard Giám sát

Grafana Panels:

  1. Tổng quan Saga Orchestration:

    • Saga execution rate (success/failure)
    • Average saga duration
    • Compensation rate
    • Pending saga count
  2. Sức khỏe Outbox Processing:

    • Outbox publishing rate
    • Processing lag (P95, P99)
    • Failed events
    • Table size trend
  3. Hiệu quả Idempotency:

    • Duplicate prevention rate
    • Redis hit rate
    • Key distribution
  4. Data Consistency SLA:

    • Overall consistency rate (target: 99.9%)
    • Mean time to consistency (MTTC)
    • Conflict resolution success rate

Tracing Phân tán

Trace Saga Execution:

// Saga step được trace
async function executeStepWithTracing(
  step: SagaStep,
  context: SagaContext
): Promise<void> {
  const tracer = trace.getTracer('saga-orchestrator');
  const span = tracer.startSpan(`saga.step.${step.name}`, {
    attributes: {
      'saga.id': context.sagaId,
      'saga.step': step.name,
      'saga.attempt': context.currentAttempt
    }
  });

  try {
    await step.execute(context);
    span.setStatus({ code: SpanStatusCode.OK });
  } catch (error) {
    span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
    span.recordException(error);
    throw error;
  } finally {
    span.end();
  }
}

Tài liệu Liên quan


Cập nhật Lần cuối: 2026-01-14
Tác giả: GoodGo Architecture Team

Quick Tips

Mermaid Common Issues

  • ⚠️ Syntax Error: Kiểm tra dấu ( ) [ ] { }
  • ⚠️ Render Error: Kiểm tra graph vs flowchart, sử dụng graph cho compatibility
  • ⚠️ Arrow Direction: Sử dụng --> (solid) hoặc -.-> (dashed)
  • Color: Luôn sử dụng dark palette với white text

Color Palette Reference

Color Fill Stroke Use Case
Blue #1d4ed8 #3b82f6 Primary Components, Saga
Green #15803d #22c55e Success, DB, Stable States
Purple #7e22ce #a855f7 Feature, Logic, Idempotency
Orange #b45309 #f59e0b Warning, External, Outbox
Red #b91c1c #ef4444 Error, Failure, Critical
Gray #374151 #6b7280 Background, Secondary

Pattern áp dụng:

style NodeName fill:#1d4ed8,stroke:#3b82f6,color:#ffffff

Visual Indicators

  • Recommended: Best practices, khuyến nghị sử dụng
  • ⚠️ Warning: Cần chú ý, có điều kiện
  • Avoid: Anti-patterns, tránh sử dụng
  • 🔒 Security: Liên quan đến bảo mật
  • Performance: Liên quan đến hiệu suất