Skip to content
Go back

Lịch sử các phương pháp thiết kế phần mềm — từ Structured Programming đến Microservices

Published:  at  10:00 AM

Mỗi phương pháp thiết kế phần mềm đều sinh ra để giải quyết một vấn đề cụ thể của thời đại nó. Đọc theo trình tự thời gian, có thể thấy một chuỗi liên tục: phương pháp này dọn xong bài toán phương pháp trước để lại, và đặt ra bài toán mới cho phương pháp kế tiếp. Bài này kể câu chuyện đó — từ 1968 đến nay.

Table of contents

Open Table of contents

Dòng thời gian tổng quan

timeline
    title Lịch sử các phương pháp thiết kế phần mềm
    1968 : Structured Programming
         : (Dijkstra)
    1972 : Object-Oriented Programming
         : (Alan Kay - Smalltalk)
    1986 : Design by Contract
         : (Bertrand Meyer)
    1991 : Component-Based SE
         : (CORBA, COM)
    1997 : Aspect-Oriented Programming
         : (Gregor Kiczales)
    2003 : Domain-Driven Design
         : (Eric Evans)
    2005 : Hexagonal Architecture
         : (Alistair Cockburn)
    2006 : Behavior-Driven Development
         : (Dan North)
    2010 : CQRS & Event Sourcing
         : (Greg Young)
    2012 : Clean Architecture
         : (Robert C. Martin)
    2014 : Microservices
         : (Fowler & Lewis)

1. Structured Programming (1968–1970s)

Câu chuyện bắt đầu năm 1968. Code thời đó dùng goto nhảy tự do — chương trình viết xong vài tháng quay lại đọc còn không dò được luồng chạy. Bài Go To Statement Considered Harmful của Dijkstra ra mắt năm đó là cột mốc. Cùng Constantine và Yourdon, ông đề xuất một bộ kỷ luật đơn giản: bỏ goto, viết theo hàm/thủ tục, gò luồng vào ba cấu trúc cơ bản (sequence, selection, iteration), và bẻ bài toán lớn xuống dần.

Nghe bây giờ thấy hiển nhiên — vì mọi paradigm sau này đều giả định bạn đã có những thứ này.

flowchart TD
    A[Bài toán lớn] --> B[Module 1]
    A --> C[Module 2]
    A --> D[Module 3]
    B --> B1[Function 1.1]
    B --> B2[Function 1.2]
    C --> C1[Function 2.1]
    C --> C2[Function 2.2]
    D --> D1[Function 3.1]
    classDef root fill:#dbeafe,stroke:#1e40af,color:#1e3a8a,stroke-width:2px
    classDef module fill:#fef3c7,stroke:#92400e,color:#78350f
    class A root
    class B,C,D module
Ưu điểmNhược điểm
Dễ học, dễ đọcKhó quản lý dữ liệu và hành vi cùng nhau
Phù hợp thuật toán tuyến tínhDữ liệu toàn cục khó kiểm soát
Nền tảng cho mọi paradigm sau nàyKhó tái sử dụng code

2. Object-Oriented Programming (1970s–1980s)

Tới giữa thập niên 70, lập trình cấu trúc đã giải quyết được vấn đề luồng. Nhưng khi codebase phình to lên hàng trăm nghìn dòng, một vấn đề mới lộ ra: dữ liệu một nơi, hàm thao tác trên dữ liệu một nơi khác, sửa một chỗ phá ba chỗ. Alan Kay với Smalltalk (1972), rồi Stroustrup với C++ (1983), rồi Java (1995) cùng đẩy một ý: gom dữ liệu cùng hành vi vào object, mô hình theo cách thế giới thực vận hành.

Bốn ý chính của OOP:

mindmap
  root((OOP))
    Encapsulation
      Đóng gói dữ liệu
      Ẩn implementation
    Inheritance
      Tái sử dụng code
      Phân cấp class
    Polymorphism
      Đa hình
      Override method
    Abstraction
      Interface
      Abstract class

Ví dụ quan hệ class:

classDiagram
    class Animal {
        +String name
        +int age
        +eat()
        +sleep()
    }
    class Dog {
        +bark()
        +fetch()
    }
    class Cat {
        +meow()
        +scratch()
    }
    Animal <|-- Dog
    Animal <|-- Cat
Ưu điểmNhược điểm
Mô hình hóa thế giới thực tự nhiênLạm dụng kế thừa gây cứng nhắc
Tái sử dụng qua kế thừa, compositionDễ rơi vào “anemic model”
Encapsulation giảm couplingTư duy “mọi thứ là object” không luôn phù hợp

3. Design by Contract (1986)

OOP giải xong bài toán tổ chức — nhưng để lại bài toán tin cậy. Khi object A gọi object B, ai đảm bảo B làm đúng việc B nói nó làm? Bertrand Meyer trả lời câu này trong ngôn ngữ Eiffel năm 1986: coi mỗi method như một hợp đồng. Caller phải đảm bảo precondition, method đảm bảo postcondition, class luôn duy trì invariant. Vi phạm hợp đồng thì biết ngay ai sai — không còn chuyện đổ lỗi qua lại.

Mô hình hợp đồng:

flowchart LR
    A[Caller] -- Đảm bảo Precondition --> B[Method]
    B -- Đảm bảo Postcondition --> C[Caller nhận kết quả]
    B -. Luôn duy trì Invariant .- B
    classDef caller fill:#dbeafe,stroke:#1e40af,color:#1e3a8a
    classDef method fill:#fef3c7,stroke:#92400e,color:#78350f
    class A,C caller
    class B method
Ưu điểmNhược điểm
Tăng độ tin cậy phần mềmÍt ngôn ngữ hỗ trợ native
Tài liệu hóa rõ ràng kỳ vọngTăng overhead khi viết code
Phát hiện lỗi sớmKhó áp dụng vào codebase có sẵn

4. Component-Based Software Engineering (1990s)

Class giúp tái sử dụng trong cùng codebase. Nhưng đầu thập niên 90, khi các hệ thống enterprise lớn lên và các công ty bắt đầu cần dùng phần mềm của nhau, class không đủ. CORBA (1991), COM của Microsoft (1993), rồi JavaBeans (1996), .NET, OSGi — tất cả cùng chạy theo một ý: đóng gói thành component có interface rõ, có thể versioning, deploy độc lập, lắp ghép như Lego — kể cả lắp ghép giữa các ngôn ngữ và hệ thống khác nhau.

flowchart TB
    subgraph App [Ứng dụng]
        direction LR
        C1[Component A<br/>Authentication]
        C2[Component B<br/>Payment]
        C3[Component C<br/>Notification]
        C4[Component D<br/>Reporting]
    end
    C1 -. Interface .- C2
    C2 -. Interface .- C3
    C1 -. Interface .- C4
    classDef comp fill:#dcfce7,stroke:#166534,color:#14532d
    class C1,C2,C3,C4 comp
Ưu điểmNhược điểm
Tái sử dụng caoPhức tạp về mặt kỹ thuật
Phát triển song songVấn đề versioning component
Tách biệt interface và implementationDependency hell, overhead

5. Aspect-Oriented Programming (cuối 1990s)

Component giải bài toán tái sử dụng ở mức vĩ mô. Nhưng bên trong từng component, một loại vấn đề khác vẫn còn nguyên: logging, security check, transaction management — những thứ này len vào mọi method nghiệp vụ và không có chỗ nào thực sự là “của” chúng. Gregor Kiczales tại Xerox PARC (AspectJ, 1997–2001) đề xuất gom chúng lại thành aspect — viết một nơi, weave vào nhiều nơi qua khai báo. Business logic không còn phải gánh các concern xuyên ngang.

flowchart TD
    subgraph Business [Business Logic]
        M1[createOrder]
        M2[processPayment]
        M3[shipProduct]
    end
    subgraph Aspects [Cross-cutting Aspects]
        A1[Logging]
        A2[Security]
        A3[Transaction]
    end
    A1 -. weave .-> M1
    A1 -. weave .-> M2
    A1 -. weave .-> M3
    A2 -. weave .-> M1
    A2 -. weave .-> M2
    A3 -. weave .-> M2
    classDef biz fill:#dbeafe,stroke:#1e40af,color:#1e3a8a
    classDef asp fill:#fee2e2,stroke:#991b1b,color:#7f1d1d
    class M1,M2,M3 biz
    class A1,A2,A3 asp
Ưu điểmNhược điểm
Giảm code trùng lặp đáng kểKhó debug, luồng không hiển nhiên
Tách biệt business và infrastructureĐường cong học tập dốc
Code chính sạch sẽDễ bị lạm dụng

6. Domain-Driven Design (2003)

Đến đầu thế kỷ 21, các vấn đề kỹ thuật đã có lời giải tương đối. Nhưng vấn đề lớn nhất vẫn còn — và nó không phải vấn đề kỹ thuật. Phần lớn dự án enterprise không chết vì code dở. Chúng chết vì code và nghiệp vụ nói hai thứ tiếng khác nhau, không ai chịu trách nhiệm dịch giữa hai bên. Eric Evans, trong cuốn Domain-Driven Design: Tackling Complexity in the Heart of Software (2003), đề xuất một cách tiếp cận: đặt domain (nghiệp vụ thực) vào trung tâm thiết kế, dùng cùng vốn từ trong code lẫn trong cuộc họp với business — không còn chỗ nào phải dịch nữa.

DDD chia làm hai tầng:

flowchart TB
    DDD[Domain-Driven Design]
    DDD --> Strategic
    DDD --> Tactical
    subgraph Strategic [Strategic Design]
        S1[Ubiquitous Language]
        S2[Bounded Context]
        S3[Context Map]
    end
    subgraph Tactical [Tactical Design]
        T1[Entity]
        T2[Value Object]
        T3[Aggregate]
        T4[Domain Event]
        T5[Repository]
        T6[Domain Service]
    end
    classDef center fill:#dcfce7,stroke:#166534,color:#14532d,stroke-width:3px
    classDef strategic fill:#dbeafe,stroke:#1e40af,color:#1e3a8a
    classDef tactical fill:#fef3c7,stroke:#92400e,color:#78350f
    class DDD center
    class S1,S2,S3 strategic
    class T1,T2,T3,T4,T5,T6 tactical

Ví dụ Bounded Context trong E-commerce:

flowchart LR
    subgraph Sales [Sales Context]
        S_Customer[Customer = Buyer]
        S_Order[Order]
    end
    subgraph Support [Support Context]
        SP_Customer[Customer = Ticket Owner]
        SP_Ticket[Ticket]
    end
    subgraph Shipping [Shipping Context]
        SH_Customer[Customer = Recipient]
        SH_Address[Address]
    end
    Sales <-. Context Map .-> Support
    Sales <-. Context Map .-> Shipping
    classDef ctx fill:#dcfce7,stroke:#166534,color:#14532d
    class S_Customer,S_Order,SP_Customer,SP_Ticket,SH_Customer,SH_Address ctx
Ưu điểmNhược điểm
Thu hẹp khoảng cách business–techĐường cong học tập dốc
Quản lý phức tạp hiệu quảOver-engineering với hệ thống đơn giản
Code dễ bảo trì lâu dàiCần domain expert thực sự
Nền tảng cho microservicesTốn chi phí thiết kế ban đầu

7. Hexagonal Architecture / Ports and Adapters (2005)

DDD đặt domain vào trung tâm — về mặt khái niệm. Nhưng trong code thực tế, lõi domain vẫn dễ dính chặt vào Spring, vào Postgres, vào REST controller. Đổi một thứ ở rìa kéo theo phải sửa cả lõi. Alistair Cockburn (2005) đề xuất một bố cục dứt khoát: lõi public các port (interface), thế giới ngoài (UI, DB, external API) cài adapter để nói chuyện với lõi qua các port đó. Đổi DB là đổi adapter, không động vào một dòng nào của logic.

flowchart TB
    subgraph External [Thế giới bên ngoài]
        UI[Web UI]
        CLI[CLI]
        DB[(Database)]
        API[External API]
    end
    subgraph Adapters [Adapters]
        UA[UI Adapter]
        CA[CLI Adapter]
        DA[DB Adapter]
        AA[API Adapter]
    end
    subgraph Core [Core Domain]
        Ports[Ports / Interfaces]
        Logic[Business Logic]
    end
    UI --> UA --> Ports
    CLI --> CA --> Ports
    Ports --> DA --> DB
    Ports --> AA --> API
    Ports <--> Logic
    classDef core fill:#dcfce7,stroke:#166534,color:#14532d,stroke-width:3px
    classDef adapter fill:#dbeafe,stroke:#1e40af,color:#1e3a8a
    classDef ext fill:#fee2e2,stroke:#991b1b,color:#7f1d1d
    class Ports,Logic core
    class UA,CA,DA,AA adapter
    class UI,CLI,DB,API ext
Ưu điểmNhược điểm
Business logic độc lập với công nghệTăng số lượng abstraction
Dễ test (mock adapter)Over-engineering với app nhỏ
Dễ đổi DB, UI frameworkCần kỷ luật cao trong team

8. Behavior-Driven Development (2006)

DDD và Hexagonal giúp tách lõi và rìa, nhưng làm sao chứng minh lõi đúng? TDD đã trả lời phần nào — viết test trước, code sau. Nhưng cú pháp test toàn ngôn ngữ kỹ thuật, business stakeholder mở ra đọc không hiểu gì, lại quay về cảnh “code và nghiệp vụ nói hai thứ tiếng” mà DDD vừa cố giải. Dan North (2006) đẩy thêm một bước: viết test bằng ngôn ngữ ai cũng đọc được, dạng Given — When — Then. Test trở thành đặc tả, đặc tả trở thành test, business tham gia được vào cả hai.

Quy trình BDD:

flowchart LR
    A[Business Requirement] --> B[Viết Scenario<br/>Given-When-Then]
    B --> C[Step Definitions]
    C --> D[Viết Code<br/>để test pass]
    D --> E[Refactor]
    E --> F{Đủ scenario?}
    F -- Chưa --> B
    F -- Đủ --> G[Done]
    classDef start fill:#dbeafe,stroke:#1e40af,color:#1e3a8a
    classDef step fill:#fef3c7,stroke:#92400e,color:#78350f
    classDef test fill:#fee2e2,stroke:#991b1b,color:#7f1d1d
    classDef done fill:#dcfce7,stroke:#166534,color:#14532d,stroke-width:2px
    class A start
    class B,C,E step
    class D test
    class G done

Ví dụ scenario:

Feature: Đăng nhập hệ thống
  Scenario: Đăng nhập thành công
    Given người dùng đã đăng ký với email "user@example.com"
    When người dùng nhập đúng mật khẩu
    Then hệ thống hiển thị trang chủ
Ưu điểmNhược điểm
Stakeholder phi kỹ thuật đọc đượcTốn công viết và bảo trì scenario
Tài liệu sống, đồng bộ với codeDễ bị lạm dụng cho mọi loại test
Tập trung vào hành vi nghiệp vụCần sự tham gia thực sự của business

9. CQRS & Event Sourcing (2010s)

Đến đầu thập niên 2010, hệ thống bắt đầu phình to ở quy mô internet — Twitter, Facebook, Amazon. CRUD chuẩn (đọc và ghi cùng model, cùng database) tới một quy mô nào đó là gãy: ghi cần consistency cao, đọc cần throughput cao, một schema không phục vụ tốt cả hai. Greg Young hệ thống hóa CQRS quanh năm 2010, dựa trên CQS của Bertrand Meyer thập niên 80: tách hẳn write side và read side. Đi kèm là Event Sourcing (Fowler mô tả 2005): không lưu state hiện tại, lưu chuỗi event đã xảy ra — khi cần state thì replay từ đầu.

Mô hình CQRS:

flowchart TB
    Client[Client]
    Client -- Command --> CommandSide
    Client -- Query --> QuerySide
    subgraph CommandSide [Write Side]
        CH[Command Handler]
        WM[Write Model]
        ES[(Event Store)]
        CH --> WM --> ES
    end
    subgraph QuerySide [Read Side]
        QH[Query Handler]
        RM[Read Model]
        QDB[(Read DB)]
        QH --> RM --> QDB
    end
    ES -. Event .-> RM
    classDef write fill:#fee2e2,stroke:#991b1b,color:#7f1d1d
    classDef read fill:#dcfce7,stroke:#166534,color:#14532d
    class CH,WM write
    class QH,RM read

Mô hình Event Sourcing:

flowchart LR
    A[OrderCreated] --> B[ItemAdded]
    B --> C[ItemAdded]
    C --> D[PaymentReceived]
    D --> E[OrderShipped]
    E --> F[Current State]
    G[Replay Events] -. Tái dựng .-> F
    classDef event fill:#dbeafe,stroke:#1e40af,color:#1e3a8a
    classDef state fill:#dcfce7,stroke:#166534,color:#14532d,stroke-width:2px
    class A,B,C,D,E event
    class F state
Ưu điểmNhược điểm
Audit trail tự nhiênPhức tạp hơn nhiều so với CRUD
Scale đọc/ghi độc lậpEventually consistent
Phù hợp với DDDSchema migration của event khó
Khả năng “time travel”Dễ over-engineering

10. Clean Architecture (2012)

Đến đây ngành đã có cả một dãy kiến trúc cùng nói một ý tưởng — Hexagonal, Onion, Screaming Architecture, DDD layered — đều nói chuyện “lõi không biết gì về rìa”, nhưng mỗi cái dùng một bộ thuật ngữ khác. Mỗi đội lại tranh cãi nên dùng tên nào. Robert C. Martin (Uncle Bob) gom chúng lại thành Clean Architecture (2012, sách 2017): bốn vòng đồng tâm, một quy tắc duy nhất — phụ thuộc chỉ hướng từ ngoài vào trong. Không có gì hoàn toàn mới, nhưng có một cái tên thống nhất để cả ngành tham chiếu.

Mô hình các vòng tròn đồng tâm:

flowchart TB
    subgraph L4 [Frameworks & Drivers]
        subgraph L3 [Interface Adapters]
            subgraph L2 [Use Cases]
                subgraph L1 [Entities]
                    E[Business Rules]
                end
                UC[Application Logic]
            end
            IA[Controllers, Presenters, Gateways]
        end
        FW[Web, DB, UI, External APIs]
    end
    FW -- Phụ thuộc --> IA
    IA -- Phụ thuộc --> UC
    UC -- Phụ thuộc --> E
    classDef inner fill:#dcfce7,stroke:#166534,color:#14532d,stroke-width:3px
    classDef use fill:#fef3c7,stroke:#92400e,color:#78350f
    classDef adapter fill:#dbeafe,stroke:#1e40af,color:#1e3a8a
    classDef ext fill:#fee2e2,stroke:#991b1b,color:#7f1d1d
    class E inner
    class UC use
    class IA adapter
    class FW ext

Nguyên tắc Dependency Rule: Phụ thuộc chỉ hướng từ ngoài vào trong.

Ưu điểmNhược điểm
Cấu trúc rõ ràng, dễ truyền đạtTăng số layer, boilerplate
Tách biệt business và infrastructureQuá mức cho dự án nhỏ
Dễ testTranh cãi về việc đặt code ở layer nào
Phổ biến, dễ tìm tài liệu

11. Microservices (2014)

Clean Architecture giải bài toán cấu trúc bên trong một ứng dụng. Nhưng khi tổ chức lớn lên, vấn đề mới chính là “một ứng dụng” đó: monolith vài triệu dòng, mỗi lần deploy là cả công ty hồi hộp; scale chỉ có một mức (tất cả hoặc không); người mới phải đọc hết codebase trước khi sửa được một thứ. Netflix và Amazon đã chạy theo hướng giải khác từ trước, nhưng phải đến bài của Martin Fowler và James Lewis năm 2014 thì “microservices” mới có tên gọi chính thức. Ý là: chia hệ thống thành các service nhỏ — mỗi service có database riêng, deploy riêng, scale riêng. Đổi lại, độ phức tạp vận hành tăng vọt.

So sánh Monolith vs Microservices:

flowchart TB
    subgraph Monolith [Monolithic Architecture]
        UI1[UI Layer]
        BL1[Business Logic]
        DB1[(Single DB)]
        UI1 --> BL1 --> DB1
    end
    subgraph Microservices [Microservices Architecture]
        GW[API Gateway]
        S1[User Service]
        S2[Order Service]
        S3[Payment Service]
        S4[Notification Service]
        DB2[(User DB)]
        DB3[(Order DB)]
        DB4[(Payment DB)]
        GW --> S1
        GW --> S2
        GW --> S3
        GW --> S4
        S1 --> DB2
        S2 --> DB3
        S3 --> DB4
        S2 -. Event .-> S4
    end
    classDef mono fill:#fee2e2,stroke:#991b1b,color:#7f1d1d
    classDef micro fill:#dcfce7,stroke:#166534,color:#14532d
    class UI1,BL1 mono
    class GW,S1,S2,S3,S4 micro
Ưu điểmNhược điểm
Scale độc lậpPhức tạp về vận hành
Deploy độc lậpDistributed system fallacies
Team autonomy caoKhó debug
Tech stack đa dạngTốn chi phí infrastructure
Phù hợp với DDD bounded contextKhông phù hợp team nhỏ

Khi nhìn lại, chúng xếp chồng lên nhau

Đọc xong từ 1968 đến 2014, có một điều dễ bỏ lỡ: các phương pháp này không thay thế lẫn nhau — chúng chồng lên nhau. OOP không xóa Structured Programming, nó dựa vào. DDD không phế OOP, nó cho OOP một lý do để tồn tại. Microservices không bỏ Clean Architecture, nó áp Clean cho từng service.

Có thể vẽ thành sơ đồ:

flowchart TB
    subgraph Foundation [Nền tảng]
        OOP[OOP]
        SP[Structured Programming]
    end
    subgraph Modeling [Modeling]
        DDD[DDD]
        DBC[Design by Contract]
    end
    subgraph Architecture [Kiến trúc]
        Hex[Hexagonal]
        Clean[Clean Architecture]
        MS[Microservices]
    end
    subgraph Patterns [Patterns]
        CQRS[CQRS]
        ES[Event Sourcing]
        AOP[AOP]
        CBSE[Component-Based]
    end
    subgraph Process [Process]
        BDD[BDD]
    end
    SP --> OOP
    OOP --> DDD
    OOP --> DBC
    DDD --> Hex
    Hex --> Clean
    DDD --> MS
    DDD --> CQRS
    CQRS --> ES
    DDD --> BDD
    classDef found fill:#dbeafe,stroke:#1e40af,color:#1e3a8a
    classDef model fill:#fef3c7,stroke:#92400e,color:#78350f
    classDef arch fill:#fee2e2,stroke:#991b1b,color:#7f1d1d
    classDef pat fill:#dcfce7,stroke:#166534,color:#14532d
    classDef proc fill:#f4f4f5,stroke:#52525b,color:#27272a
    class SP,OOP found
    class DDD,DBC model
    class Hex,Clean,MS arch
    class CQRS,ES,AOP,CBSE pat
    class BDD proc

Một dự án 2026 thường đồng thời mang theo nhiều lớp di sản:

  • OOP làm ngôn ngữ chung của codebase
  • DDD để mô hình domain
  • Clean Architecture cho cấu trúc tổng thể
  • Microservices cho cách deploy
  • CQRS + Event Sourcing cho một vài bounded context có yêu cầu đặc biệt
  • BDD cho lớp acceptance test

Không có dự án nào dùng “tất cả” — mỗi dự án chọn cái phù hợp với điểm đau của nó. Câu hỏi quan trọng không phải “phương pháp nào đúng” mà “tôi đang đau ở đâu, và phương pháp nào sinh ra để giải đúng cái đau đó”.

Cách chọn cho dự án của bạn

Một sơ đồ ra quyết định đơn giản — không phải để theo cứng, mà để gợi lại các câu hỏi cần tự trả lời trước khi rủ team theo một trường phái nào đó:

flowchart TD
    Start[Bắt đầu dự án] --> Q1{Domain phức tạp?}
    Q1 -- Không --> Simple[CRUD đơn giản<br/>+ MVC]
    Q1 -- Có --> Q2{Team lớn?}
    Q2 -- Không --> Mono[Monolith<br/>+ DDD<br/>+ Clean Architecture]
    Q2 -- Có --> Q3{Cần scale độc lập?}
    Q3 -- Không --> Modular[Modular Monolith<br/>+ DDD]
    Q3 -- Có --> Q4{Cần audit / lịch sử?}
    Q4 -- Không --> MS2[Microservices<br/>+ DDD]
    Q4 -- Có --> Full[Microservices<br/>+ DDD<br/>+ CQRS + Event Sourcing]
    classDef start fill:#dcfce7,stroke:#166534,color:#14532d,stroke-width:2px
    classDef good fill:#dcfce7,stroke:#166534,color:#14532d
    classDef warn fill:#fef3c7,stroke:#92400e,color:#78350f
    classDef bad fill:#fee2e2,stroke:#991b1b,color:#7f1d1d
    class Start start
    class Simple,Mono,Modular,MS2 good
    class Full bad

Tóm lại

50 năm thiết kế phần mềm có thể tóm gọn thành một quy luật: mỗi phương pháp giải quyết một bài toán cụ thể của thời đại nó, đồng thời để lại bài toán mới cho thời đại sau. Không có phương pháp nào “đúng tuyệt đối”, và cũng không có phương pháp nào lỗi thời hoàn toàn — vì các bài toán cũ vẫn xuất hiện trong codebase mới mỗi ngày.

Cái thiếu nhất không phải là biết các phương pháp. Cái thiếu là biết vấn đề nào mỗi phương pháp được sinh ra để giải, và nhận ra trong codebase của mình lúc nào đang gặp đúng vấn đề đó.

Đó mới là kỹ năng thiết kế phần mềm — chứ không phải thuộc tên các pattern.



Previous Post
A history of software design methods — from Structured Programming to Microservices