0

Clean Architecture không phải là đích đến — đó là kỷ luật bạn duy trì mỗi ngày

Tại sao hầu hết các hệ thống đều bắt đầu clean và kết thúc là một mớ hỗn độn

Và những nguyên tắc thực sự giúp bạn xây dựng phần mềm bền vững theo thời gian


Mở đầu: Nghịch lý của phần mềm "tốt"

Có một nghịch lý kỳ lạ trong ngành phần mềm: hầu hết mọi hệ thống đều được viết bởi những developer có năng lực, với ý định tốt, nhưng vẫn trở nên khó bảo trì sau một vài năm.

Không phải vì họ không biết Clean Architecture. Không phải vì họ không đọc sách của Uncle Bob.
Mà vì architecture không phải là thứ bạn thiết kế một lần rồi xong — nó là thứ bạn phải chủ động bảo vệ mỗi ngày.

Bài viết này không phải là tutorial. Không có diagram, không có giải thích layer.
Câu hỏi chính:

Tại sao architecture tốt lại khó duy trì?
Và điều gì thực sự giúp phần mềm bền vững theo thời gian?


1. Entropy là kẻ thù thực sự, không phải bad developer

Trong vật lý, entropy là xu hướng mọi hệ thống tiến về hỗn loạn.

Phần mềm cũng vậy.

Ban đầu:

  • Code clean
  • Boundaries rõ
  • Dependency ít

Theo thời gian:

  • “Tạm gọi DB trực tiếp, sau refactor”
  • “Thêm flag cho nhanh”
  • “Copy logic cho kịp deadline”

Mỗi quyết định đều hợp lý tại thời điểm đó.
Nhưng tích lũy lại → technical debt + architectural erosion.

“The only way to go fast is to go well.” — Robert C. Martin


2. Dependency là nguồn gốc của mọi vấn đề

Tất cả vấn đề maintainability đều quay về dependency:

  • Khó test → phụ thuộc DB/external
  • Khó thay đổi → coupling chặt
  • Khó đọc → dependency chồng chéo
  • Khó onboard → không có boundary rõ

Dependency Rule

Dependency chỉ đi từ ngoài vào trong

  • Domain không biết database/framework/UI
  • Nhưng ngược lại thì được

Ví dụ

❌ Sai

class OrderService {
  async createOrder(data: CreateOrderDTO) {
    return prisma.order.create({ data });
  }
}

✅ Đúng

interface OrderRepository {
  save(order: Order): Promise<void>;
}

class OrderService {
  constructor(private orderRepo: OrderRepository) {}

  async createOrder(command: CreateOrderCommand) {
    const order = Order.create(command);
    await this.orderRepo.save(order);
    return order;
  }
}

3. Domain Model — trái tim của hệ thống

❌ Anemic Domain Model

class Order {
  id: string;
  status: string;
}

Logic nằm ở service → bị phân tán.


✅ Rich Domain Model

class Order {
  private status: OrderStatus;

  confirm() {
    if (this.status === 'cancelled') {
      throw new Error('Cannot confirm');
    }
    this.status = 'confirmed';
  }
}

Lợi ích

  • Business rule = executable documentation
  • Invariants luôn được bảo vệ
  • Test dễ và meaningful

4. Anti-Corruption Layer (ACL)

Domain không nên bị “ô nhiễm” bởi external system.

Ví dụ

  • Stripe: amount = cents, status phức tạp
  • Domain: Money, PaymentStatus đơn giản

ACL sẽ:

  • Map data external → domain
  • Cô lập dependency

5. Tại sao architecture vẫn bị phá vỡ?

5.1 Pragmatism Trap

  • “Dự án nhỏ thôi”
  • “Tạm thời”
  • “Chỉ dùng 1 lần”

→ Temporary trở thành permanent


5.2 Knowledge Gradient

Người thiết kế hiểu hệ thống.
Người sau không có context → phá rule.

Giải pháp: ADR

# ADR-007: Use Repository Pattern

## Context
Service đang gọi Prisma trực tiếp

## Decision
Introduce Repository interface

## Consequences
- Test dễ hơn
- Code phức tạp hơn chút

5.3 Consistency Illusion

Dev sẽ follow code gần nhất, không phải code đúng.

Consistency > Perfection


6. Cơ chế bảo vệ architecture

6.1 ESLint rules

'import/no-restricted-paths': [
  'error',
  {
    zones: [
      {
        target: './domain',
        from: './infrastructure'
      }
    ]
  }
]

6.2 Fitness Functions

it('Domain should not depend on infra', () => {
  expect(illegalDeps).toEqual([]);
});

6.3 Test structure phản ánh architecture

src/
  domain/
  application/
  infrastructure/

test/
  unit/
  integration/
  e2e/

7. Khi nào KHÔNG nên dùng Clean Architecture

Phù hợp khi:

  • Domain phức tạp
  • System dài hạn
  • Team lớn
  • Có khả năng đổi infra

Overkill khi:

  • CRUD đơn giản
  • Prototype
  • Team nhỏ

Bắt đầu simple, nhưng design để scale


Kết luận

Architecture là kỷ luật, không phải kiến thức

  • Biết ≠ áp dụng
  • Clean architecture phải được bảo vệ mỗi ngày

Entropy luôn thắng nếu bạn không chống lại nó


📌 Key Takeaways

  • Dependency là gốc rễ → kiểm soát direction
  • Rich domain model = đúng nơi cho business logic
  • Enforcement > Convention
  • ADR giữ lại “why”
  • Consistency > Perfection

Tags:
#cleanarchitecture #systemdesign #ddd #backend #typescript


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí